From b865bcf26e6a5bb4fb0216eed76f74d38eb523b3 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Tue, 26 Aug 2025 11:56:23 -0700 Subject: [PATCH 1/3] Enable singleton stuffing and use papilo default params --- cpp/src/mip/presolve/third_party_presolve.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 75bad369f8..36ccd888c0 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -279,8 +279,8 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c presolver.addPresolveMethod(uptr(new papilo::SimpleProbing())); presolver.addPresolveMethod(uptr(new papilo::ParallelRowDetection())); presolver.addPresolveMethod(uptr(new papilo::ParallelColDetection())); - // FIXME: Postsolve fails with this method - // presolver.addPresolveMethod(uptr(new papilo::SingletonStuffing())); + + presolver.addPresolveMethod(uptr(new papilo::SingletonStuffing())); presolver.addPresolveMethod(uptr(new papilo::DualFix())); presolver.addPresolveMethod(uptr(new papilo::SimplifyInequalities())); @@ -303,16 +303,6 @@ void set_presolve_options(papilo::Presolve& presolver, double time_limit) { presolver.getPresolveOptions().tlim = time_limit; - - if (category == problem_category_t::LP) { - presolver.getPresolveOptions().useabsfeas = false; - presolver.getPresolveOptions().feastol = relative_tolerance; - presolver.getPresolveOptions().epsilon = absolute_tolerance; - } else if (category == problem_category_t::MIP || category == problem_category_t::IP) { - presolver.getPresolveOptions().useabsfeas = true; - presolver.getPresolveOptions().feastol = absolute_tolerance; - presolver.getPresolveOptions().epsilon = absolute_tolerance; - } } template From f145f671656b05839f90b757988d351f7123b670 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Tue, 26 Aug 2025 13:46:24 -0700 Subject: [PATCH 2/3] Add LP presolve testing as part of sub mittleman and update log --- cpp/src/mip/presolve/third_party_presolve.cpp | 6 +- cpp/tests/linear_programming/pdlp_test.cu | 268 +++++++++--------- .../utilities/pdlp_test_utilities.cuh | 13 +- 3 files changed, 152 insertions(+), 135 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 36ccd888c0..7c9867d0e7 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -260,7 +260,11 @@ void check_postsolve_status(const papilo::PostsolveStatus& status) { switch (status) { case papilo::PostsolveStatus::kOk: CUOPT_LOG_INFO("Post-solve status:: succeeded"); break; - case papilo::PostsolveStatus::kFailed: CUOPT_LOG_INFO("Post-solve status:: failed"); break; + case papilo::PostsolveStatus::kFailed: + CUOPT_LOG_INFO( + "Post-solve status:: Post solved solution violates constraints. This is most likely due to " + "different tolerances."); + break; } } diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index 47fae97c1e..666745e51d 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -65,126 +65,128 @@ static bool is_incorrect_objective(double reference, double objective) return std::abs((reference - objective) / reference) > 0.01; } -TEST(pdlp_class, run_double) -{ - const raft::handle_t handle_{}; - - auto path = make_path_absolute("linear_programming/afiro_original.mps"); - cuopt::mps_parser::mps_data_model_t op_problem = - cuopt::mps_parser::parse_mps(path, true); - - 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_OPTIMAL); - EXPECT_FALSE(is_incorrect_objective( - afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); -} - -TEST(pdlp_class, run_double_very_low_accuracy) -{ - const raft::handle_t handle_{}; - - auto path = make_path_absolute("linear_programming/afiro_original.mps"); - cuopt::mps_parser::mps_data_model_t op_problem = - cuopt::mps_parser::parse_mps(path, true); - - cuopt::linear_programming::pdlp_solver_settings_t settings = - cuopt::linear_programming::pdlp_solver_settings_t{}; - // With all 0 afiro with return an error - // Setting absolute tolerance to the minimal value of 1e-12 will make it work - settings.tolerances.absolute_dual_tolerance = settings.minimal_absolute_tolerance; - settings.tolerances.relative_dual_tolerance = 0.0; - settings.tolerances.absolute_primal_tolerance = settings.minimal_absolute_tolerance; - settings.tolerances.relative_primal_tolerance = 0.0; - settings.tolerances.absolute_gap_tolerance = settings.minimal_absolute_tolerance; - settings.tolerances.relative_gap_tolerance = 0.0; - settings.method = cuopt::linear_programming::method_t::PDLP; - - optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); - EXPECT_FALSE(is_incorrect_objective( - afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); -} - -TEST(pdlp_class, run_double_initial_solution) -{ - const raft::handle_t handle_{}; - - auto path = make_path_absolute("linear_programming/afiro_original.mps"); - cuopt::mps_parser::mps_data_model_t op_problem = - cuopt::mps_parser::parse_mps(path, true); - - std::vector inital_primal_sol(op_problem.get_n_variables()); - std::fill(inital_primal_sol.begin(), inital_primal_sol.end(), 1.0); - op_problem.set_initial_primal_solution(inital_primal_sol.data(), inital_primal_sol.size()); - - 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_OPTIMAL); - EXPECT_FALSE(is_incorrect_objective( - afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); -} - -TEST(pdlp_class, run_iteration_limit) -{ - const raft::handle_t handle_{}; - - auto path = make_path_absolute("linear_programming/afiro_original.mps"); - cuopt::mps_parser::mps_data_model_t op_problem = - cuopt::mps_parser::parse_mps(path, true); - - cuopt::linear_programming::pdlp_solver_settings_t settings = - cuopt::linear_programming::pdlp_solver_settings_t{}; - - settings.iteration_limit = 10; - // To make sure it doesn't return before the iteration limit - settings.set_optimality_tolerance(0); - settings.method = cuopt::linear_programming::method_t::PDLP; - - optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT); - // By default we would return all 0, we now return what we currently have so not all 0 - EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), - solution.get_primal_solution().begin(), - solution.get_primal_solution().end(), - thrust::placeholders::_1 == 0.0)); -} - -TEST(pdlp_class, run_time_limit) -{ - const raft::handle_t handle_{}; - auto path = make_path_absolute("linear_programming/savsched1/savsched1.mps"); - cuopt::mps_parser::mps_data_model_t op_problem = - cuopt::mps_parser::parse_mps(path); - - cuopt::linear_programming::pdlp_solver_settings_t settings = - cuopt::linear_programming::pdlp_solver_settings_t{}; - - // 200 ms - constexpr double time_limit_seconds = 0.2; - settings.time_limit = time_limit_seconds; - // To make sure it doesn't return before the time limit - settings.set_optimality_tolerance(0); - settings.method = cuopt::linear_programming::method_t::PDLP; - - optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); - - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_TIME_LIMIT); - // By default we would return all 0, we now return what we currently have so not all 0 - EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), - solution.get_primal_solution().begin(), - solution.get_primal_solution().end(), - thrust::placeholders::_1 == 0.0)); - // Check that indeed it didn't run for more than x time - EXPECT_TRUE(solution.get_additional_termination_information().solve_time < - (time_limit_seconds * 5) * 1000); -} +// TEST(pdlp_class, run_double) +// { +// const raft::handle_t handle_{}; + +// auto path = make_path_absolute("linear_programming/afiro_original.mps"); +// cuopt::mps_parser::mps_data_model_t op_problem = +// cuopt::mps_parser::parse_mps(path, true); + +// 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_OPTIMAL); +// EXPECT_FALSE(is_incorrect_objective( +// afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +// } + +// TEST(pdlp_class, run_double_very_low_accuracy) +// { +// const raft::handle_t handle_{}; + +// auto path = make_path_absolute("linear_programming/afiro_original.mps"); +// cuopt::mps_parser::mps_data_model_t op_problem = +// cuopt::mps_parser::parse_mps(path, true); + +// cuopt::linear_programming::pdlp_solver_settings_t settings = +// cuopt::linear_programming::pdlp_solver_settings_t{}; +// // With all 0 afiro with return an error +// // Setting absolute tolerance to the minimal value of 1e-12 will make it work +// settings.tolerances.absolute_dual_tolerance = settings.minimal_absolute_tolerance; +// settings.tolerances.relative_dual_tolerance = 0.0; +// settings.tolerances.absolute_primal_tolerance = settings.minimal_absolute_tolerance; +// settings.tolerances.relative_primal_tolerance = 0.0; +// settings.tolerances.absolute_gap_tolerance = settings.minimal_absolute_tolerance; +// settings.tolerances.relative_gap_tolerance = 0.0; +// settings.method = cuopt::linear_programming::method_t::PDLP; + +// optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, +// settings); EXPECT_EQ((int)solution.get_termination_status(), +// CUOPT_TERIMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( +// afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +// } + +// TEST(pdlp_class, run_double_initial_solution) +// { +// const raft::handle_t handle_{}; + +// auto path = make_path_absolute("linear_programming/afiro_original.mps"); +// cuopt::mps_parser::mps_data_model_t op_problem = +// cuopt::mps_parser::parse_mps(path, true); + +// std::vector inital_primal_sol(op_problem.get_n_variables()); +// std::fill(inital_primal_sol.begin(), inital_primal_sol.end(), 1.0); +// op_problem.set_initial_primal_solution(inital_primal_sol.data(), inital_primal_sol.size()); + +// 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_OPTIMAL); +// EXPECT_FALSE(is_incorrect_objective( +// afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +// } + +// TEST(pdlp_class, run_iteration_limit) +// { +// const raft::handle_t handle_{}; + +// auto path = make_path_absolute("linear_programming/afiro_original.mps"); +// cuopt::mps_parser::mps_data_model_t op_problem = +// cuopt::mps_parser::parse_mps(path, true); + +// cuopt::linear_programming::pdlp_solver_settings_t settings = +// cuopt::linear_programming::pdlp_solver_settings_t{}; + +// settings.iteration_limit = 10; +// // To make sure it doesn't return before the iteration limit +// settings.set_optimality_tolerance(0); +// settings.method = cuopt::linear_programming::method_t::PDLP; + +// optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, +// settings); EXPECT_EQ((int)solution.get_termination_status(), +// CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT); +// // By default we would return all 0, we now return what we currently have so not all 0 +// EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), +// solution.get_primal_solution().begin(), +// solution.get_primal_solution().end(), +// thrust::placeholders::_1 == 0.0)); +// } + +// TEST(pdlp_class, run_time_limit) +// { +// const raft::handle_t handle_{}; +// auto path = make_path_absolute("linear_programming/savsched1/savsched1.mps"); +// cuopt::mps_parser::mps_data_model_t op_problem = +// cuopt::mps_parser::parse_mps(path); + +// cuopt::linear_programming::pdlp_solver_settings_t settings = +// cuopt::linear_programming::pdlp_solver_settings_t{}; + +// // 200 ms +// constexpr double time_limit_seconds = 0.2; +// settings.time_limit = time_limit_seconds; +// // To make sure it doesn't return before the time limit +// settings.set_optimality_tolerance(0); +// settings.method = cuopt::linear_programming::method_t::PDLP; + +// optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, +// settings); + +// EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_TIME_LIMIT); +// // By default we would return all 0, we now return what we currently have so not all 0 +// EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), +// solution.get_primal_solution().begin(), +// solution.get_primal_solution().end(), +// thrust::placeholders::_1 == 0.0)); +// // Check that indeed it didn't run for more than x time +// EXPECT_TRUE(solution.get_additional_termination_information().solve_time < +// (time_limit_seconds * 5) * 1000); +// } TEST(pdlp_class, run_sub_mittleman) { @@ -219,17 +221,21 @@ TEST(pdlp_class, run_sub_mittleman) for (auto solver_mode : solver_mode_list) { auto settings = pdlp_solver_settings_t{}; settings.pdlp_solver_mode = solver_mode; - const raft::handle_t handle_{}; - optimization_problem_solution_t solution = - solve_lp(&handle_, op_problem, settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); - EXPECT_FALSE( - is_incorrect_objective(expected_objective_value, - solution.get_additional_termination_information().primal_objective)); - test_objective_sanity(op_problem, - solution.get_primal_solution(), - solution.get_additional_termination_information().primal_objective); - test_constraint_sanity(op_problem, solution); + for (auto [presolve, epsilon] : {std::pair{true, 1e-1}, std::pair{false, 1e-6}}) { + settings.presolve = presolve; + const raft::handle_t handle_{}; + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + expected_objective_value, + solution.get_additional_termination_information().primal_objective)); + test_objective_sanity(op_problem, + solution.get_primal_solution(), + solution.get_additional_termination_information().primal_objective, + epsilon); + test_constraint_sanity(op_problem, solution, epsilon, presolve); + } } } } diff --git a/cpp/tests/linear_programming/utilities/pdlp_test_utilities.cuh b/cpp/tests/linear_programming/utilities/pdlp_test_utilities.cuh index 7a624e0232..e0144b75ba 100644 --- a/cpp/tests/linear_programming/utilities/pdlp_test_utilities.cuh +++ b/cpp/tests/linear_programming/utilities/pdlp_test_utilities.cuh @@ -70,7 +70,7 @@ static void test_constraint_sanity( const cuopt::mps_parser::mps_data_model_t& op_problem, const optimization_problem_solution_t& solution, double epsilon = tolerance, - bool presolve_enabled = true) + bool presolve_enabled = false) { const std::vector primal_vars = host_copy(solution.get_primal_solution()); const std::vector& values = op_problem.get_constraint_matrix_values(); @@ -83,6 +83,7 @@ static void test_constraint_sanity( std::vector residual(solution.get_dual_solution().size(), 0.0); std::vector viol(solution.get_dual_solution().size(), 0.0); + // No dual solution and residual for presolved problems if (!presolve_enabled) { // CSR SpMV for (size_t i = 0; i < offsets.size() - 1; ++i) { @@ -137,8 +138,14 @@ static void test_constraint_sanity( for (size_t i = 0; i < primal_vars.size(); ++i) { // Not always stricly true because we apply variable bound clamping on the scaled problem // After unscaling it, the variables might not respect exactly (this adding an epsilon) - EXPECT_TRUE(primal_vars[i] >= variable_lower_bounds[i] - epsilon && - primal_vars[i] <= variable_upper_bounds[i] + epsilon); + auto condition = primal_vars[i] >= variable_lower_bounds[i] - epsilon && + primal_vars[i] <= variable_upper_bounds[i] + epsilon; + if (!condition) { + std::cout << "Variable " << i << " is " << primal_vars[i] << " but should be between " + << variable_lower_bounds[i] - epsilon << " and " + << variable_upper_bounds[i] + epsilon << std::endl; + } + EXPECT_TRUE(condition); } } From d1ae1bc9636d8796ed643b012da32cc5b558d37a Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Tue, 26 Aug 2025 13:51:19 -0700 Subject: [PATCH 3/3] Uncomment tests --- cpp/tests/linear_programming/pdlp_test.cu | 242 +++++++++++----------- 1 file changed, 120 insertions(+), 122 deletions(-) diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index 666745e51d..2598561211 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -65,128 +65,126 @@ static bool is_incorrect_objective(double reference, double objective) return std::abs((reference - objective) / reference) > 0.01; } -// TEST(pdlp_class, run_double) -// { -// const raft::handle_t handle_{}; - -// auto path = make_path_absolute("linear_programming/afiro_original.mps"); -// cuopt::mps_parser::mps_data_model_t op_problem = -// cuopt::mps_parser::parse_mps(path, true); - -// 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_OPTIMAL); -// EXPECT_FALSE(is_incorrect_objective( -// afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); -// } - -// TEST(pdlp_class, run_double_very_low_accuracy) -// { -// const raft::handle_t handle_{}; - -// auto path = make_path_absolute("linear_programming/afiro_original.mps"); -// cuopt::mps_parser::mps_data_model_t op_problem = -// cuopt::mps_parser::parse_mps(path, true); - -// cuopt::linear_programming::pdlp_solver_settings_t settings = -// cuopt::linear_programming::pdlp_solver_settings_t{}; -// // With all 0 afiro with return an error -// // Setting absolute tolerance to the minimal value of 1e-12 will make it work -// settings.tolerances.absolute_dual_tolerance = settings.minimal_absolute_tolerance; -// settings.tolerances.relative_dual_tolerance = 0.0; -// settings.tolerances.absolute_primal_tolerance = settings.minimal_absolute_tolerance; -// settings.tolerances.relative_primal_tolerance = 0.0; -// settings.tolerances.absolute_gap_tolerance = settings.minimal_absolute_tolerance; -// settings.tolerances.relative_gap_tolerance = 0.0; -// settings.method = cuopt::linear_programming::method_t::PDLP; - -// optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, -// settings); EXPECT_EQ((int)solution.get_termination_status(), -// CUOPT_TERIMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( -// afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); -// } - -// TEST(pdlp_class, run_double_initial_solution) -// { -// const raft::handle_t handle_{}; - -// auto path = make_path_absolute("linear_programming/afiro_original.mps"); -// cuopt::mps_parser::mps_data_model_t op_problem = -// cuopt::mps_parser::parse_mps(path, true); - -// std::vector inital_primal_sol(op_problem.get_n_variables()); -// std::fill(inital_primal_sol.begin(), inital_primal_sol.end(), 1.0); -// op_problem.set_initial_primal_solution(inital_primal_sol.data(), inital_primal_sol.size()); - -// 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_OPTIMAL); -// EXPECT_FALSE(is_incorrect_objective( -// afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); -// } - -// TEST(pdlp_class, run_iteration_limit) -// { -// const raft::handle_t handle_{}; - -// auto path = make_path_absolute("linear_programming/afiro_original.mps"); -// cuopt::mps_parser::mps_data_model_t op_problem = -// cuopt::mps_parser::parse_mps(path, true); - -// cuopt::linear_programming::pdlp_solver_settings_t settings = -// cuopt::linear_programming::pdlp_solver_settings_t{}; - -// settings.iteration_limit = 10; -// // To make sure it doesn't return before the iteration limit -// settings.set_optimality_tolerance(0); -// settings.method = cuopt::linear_programming::method_t::PDLP; - -// optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, -// settings); EXPECT_EQ((int)solution.get_termination_status(), -// CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT); -// // By default we would return all 0, we now return what we currently have so not all 0 -// EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), -// solution.get_primal_solution().begin(), -// solution.get_primal_solution().end(), -// thrust::placeholders::_1 == 0.0)); -// } - -// TEST(pdlp_class, run_time_limit) -// { -// const raft::handle_t handle_{}; -// auto path = make_path_absolute("linear_programming/savsched1/savsched1.mps"); -// cuopt::mps_parser::mps_data_model_t op_problem = -// cuopt::mps_parser::parse_mps(path); - -// cuopt::linear_programming::pdlp_solver_settings_t settings = -// cuopt::linear_programming::pdlp_solver_settings_t{}; - -// // 200 ms -// constexpr double time_limit_seconds = 0.2; -// settings.time_limit = time_limit_seconds; -// // To make sure it doesn't return before the time limit -// settings.set_optimality_tolerance(0); -// settings.method = cuopt::linear_programming::method_t::PDLP; - -// optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, -// settings); - -// EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_TIME_LIMIT); -// // By default we would return all 0, we now return what we currently have so not all 0 -// EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), -// solution.get_primal_solution().begin(), -// solution.get_primal_solution().end(), -// thrust::placeholders::_1 == 0.0)); -// // Check that indeed it didn't run for more than x time -// EXPECT_TRUE(solution.get_additional_termination_information().solve_time < -// (time_limit_seconds * 5) * 1000); -// } +TEST(pdlp_class, run_double) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + 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_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, run_double_very_low_accuracy) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + cuopt::linear_programming::pdlp_solver_settings_t settings = + cuopt::linear_programming::pdlp_solver_settings_t{}; + // With all 0 afiro with return an error + // Setting absolute tolerance to the minimal value of 1e-12 will make it work + settings.tolerances.absolute_dual_tolerance = settings.minimal_absolute_tolerance; + settings.tolerances.relative_dual_tolerance = 0.0; + settings.tolerances.absolute_primal_tolerance = settings.minimal_absolute_tolerance; + settings.tolerances.relative_primal_tolerance = 0.0; + settings.tolerances.absolute_gap_tolerance = settings.minimal_absolute_tolerance; + settings.tolerances.relative_gap_tolerance = 0.0; + settings.method = cuopt::linear_programming::method_t::PDLP; + + optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, run_double_initial_solution) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + std::vector inital_primal_sol(op_problem.get_n_variables()); + std::fill(inital_primal_sol.begin(), inital_primal_sol.end(), 1.0); + op_problem.set_initial_primal_solution(inital_primal_sol.data(), inital_primal_sol.size()); + + 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_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, run_iteration_limit) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + cuopt::linear_programming::pdlp_solver_settings_t settings = + cuopt::linear_programming::pdlp_solver_settings_t{}; + + settings.iteration_limit = 10; + // To make sure it doesn't return before the iteration limit + settings.set_optimality_tolerance(0); + settings.method = cuopt::linear_programming::method_t::PDLP; + + optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT); + // By default we would return all 0, we now return what we currently have so not all 0 + EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), + solution.get_primal_solution().begin(), + solution.get_primal_solution().end(), + thrust::placeholders::_1 == 0.0)); +} + +TEST(pdlp_class, run_time_limit) +{ + const raft::handle_t handle_{}; + auto path = make_path_absolute("linear_programming/savsched1/savsched1.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path); + + cuopt::linear_programming::pdlp_solver_settings_t settings = + cuopt::linear_programming::pdlp_solver_settings_t{}; + + // 200 ms + constexpr double time_limit_seconds = 0.2; + settings.time_limit = time_limit_seconds; + // To make sure it doesn't return before the time limit + settings.set_optimality_tolerance(0); + settings.method = cuopt::linear_programming::method_t::PDLP; + + optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); + + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_TIME_LIMIT); + // By default we would return all 0, we now return what we currently have so not all 0 + EXPECT_FALSE(thrust::all_of(handle_.get_thrust_policy(), + solution.get_primal_solution().begin(), + solution.get_primal_solution().end(), + thrust::placeholders::_1 == 0.0)); + // Check that indeed it didn't run for more than x time + EXPECT_TRUE(solution.get_additional_termination_information().solve_time < + (time_limit_seconds * 5) * 1000); +} TEST(pdlp_class, run_sub_mittleman) {