diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 86becfe06d..dde65c5648 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -82,17 +82,18 @@ #define CUOPT_MODE_DETERMINISTIC 1 /* @brief LP/MIP termination status constants */ -#define CUOPT_TERIMINATION_STATUS_NO_TERMINATION 0 -#define CUOPT_TERIMINATION_STATUS_OPTIMAL 1 -#define CUOPT_TERIMINATION_STATUS_INFEASIBLE 2 -#define CUOPT_TERIMINATION_STATUS_UNBOUNDED 3 -#define CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT 4 -#define CUOPT_TERIMINATION_STATUS_TIME_LIMIT 5 -#define CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR 6 -#define CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE 7 -#define CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND 8 -#define CUOPT_TERIMINATION_STATUS_CONCURRENT_LIMIT 9 -#define CUOPT_TERIMINATION_STATUS_WORK_LIMIT 10 +#define CUOPT_TERMINATION_STATUS_NO_TERMINATION 0 +#define CUOPT_TERMINATION_STATUS_OPTIMAL 1 +#define CUOPT_TERMINATION_STATUS_INFEASIBLE 2 +#define CUOPT_TERMINATION_STATUS_UNBOUNDED 3 +#define CUOPT_TERMINATION_STATUS_ITERATION_LIMIT 4 +#define CUOPT_TERMINATION_STATUS_TIME_LIMIT 5 +#define CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR 6 +#define CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE 7 +#define CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND 8 +#define CUOPT_TERMINATION_STATUS_CONCURRENT_LIMIT 9 +#define CUOPT_TERMINATION_STATUS_WORK_LIMIT 10 +#define CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE 11 /* @brief The objective sense constants */ #define CUOPT_MINIMIZE 1 diff --git a/cpp/include/cuopt/linear_programming/mip/solver_solution.hpp b/cpp/include/cuopt/linear_programming/mip/solver_solution.hpp index a6c28ac20d..70c2e4bcac 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_solution.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_solution.hpp @@ -24,13 +24,14 @@ namespace cuopt::linear_programming { enum class mip_termination_status_t : int8_t { - NoTermination = CUOPT_TERIMINATION_STATUS_NO_TERMINATION, - Optimal = CUOPT_TERIMINATION_STATUS_OPTIMAL, - FeasibleFound = CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND, - Infeasible = CUOPT_TERIMINATION_STATUS_INFEASIBLE, - Unbounded = CUOPT_TERIMINATION_STATUS_UNBOUNDED, - TimeLimit = CUOPT_TERIMINATION_STATUS_TIME_LIMIT, - WorkLimit = CUOPT_TERIMINATION_STATUS_WORK_LIMIT, + NoTermination = CUOPT_TERMINATION_STATUS_NO_TERMINATION, + Optimal = CUOPT_TERMINATION_STATUS_OPTIMAL, + FeasibleFound = CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND, + Infeasible = CUOPT_TERMINATION_STATUS_INFEASIBLE, + Unbounded = CUOPT_TERMINATION_STATUS_UNBOUNDED, + TimeLimit = CUOPT_TERMINATION_STATUS_TIME_LIMIT, + WorkLimit = CUOPT_TERMINATION_STATUS_WORK_LIMIT, + UnboundedOrInfeasible = CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE, }; template diff --git a/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp b/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp index 45a47e7401..8681690540 100644 --- a/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp +++ b/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp @@ -25,15 +25,16 @@ namespace cuopt::linear_programming { // Possible reasons for terminating enum class pdlp_termination_status_t : int8_t { - NoTermination = CUOPT_TERIMINATION_STATUS_NO_TERMINATION, - NumericalError = CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR, - Optimal = CUOPT_TERIMINATION_STATUS_OPTIMAL, - PrimalInfeasible = CUOPT_TERIMINATION_STATUS_INFEASIBLE, - DualInfeasible = CUOPT_TERIMINATION_STATUS_UNBOUNDED, - IterationLimit = CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT, - TimeLimit = CUOPT_TERIMINATION_STATUS_TIME_LIMIT, - PrimalFeasible = CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE, - ConcurrentLimit = CUOPT_TERIMINATION_STATUS_CONCURRENT_LIMIT + NoTermination = CUOPT_TERMINATION_STATUS_NO_TERMINATION, + NumericalError = CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR, + Optimal = CUOPT_TERMINATION_STATUS_OPTIMAL, + PrimalInfeasible = CUOPT_TERMINATION_STATUS_INFEASIBLE, + DualInfeasible = CUOPT_TERMINATION_STATUS_UNBOUNDED, + IterationLimit = CUOPT_TERMINATION_STATUS_ITERATION_LIMIT, + TimeLimit = CUOPT_TERMINATION_STATUS_TIME_LIMIT, + PrimalFeasible = CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE, + ConcurrentLimit = CUOPT_TERMINATION_STATUS_CONCURRENT_LIMIT, + UnboundedOrInfeasible = CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE }; /** diff --git a/cpp/src/dual_simplex/solve.cpp b/cpp/src/dual_simplex/solve.cpp index d300d6011c..8dc9f6171c 100644 --- a/cpp/src/dual_simplex/solve.cpp +++ b/cpp/src/dual_simplex/solve.cpp @@ -212,11 +212,13 @@ lp_status_t solve_linear_program_with_advanced_basis( edge_norms, work_unit_context); } - if (phase1_status == dual::status_t::NUMERICAL || - phase1_status == dual::status_t::DUAL_UNBOUNDED) { + if (phase1_status == dual::status_t::NUMERICAL) { settings.log.printf("Failed in Phase 1\n"); return lp_status_t::NUMERICAL_ISSUES; } + if (phase1_status == dual::status_t::DUAL_UNBOUNDED) { + return lp_status_t::UNBOUNDED_OR_INFEASIBLE; + } if (phase1_status == dual::status_t::TIME_LIMIT) { return lp_status_t::TIME_LIMIT; } if (phase1_status == dual::status_t::WORK_LIMIT) { return lp_status_t::WORK_LIMIT; } if (phase1_status == dual::status_t::ITERATION_LIMIT) { return lp_status_t::ITERATION_LIMIT; } @@ -312,7 +314,7 @@ lp_status_t solve_linear_program_with_advanced_basis( if (status == dual::status_t::CUTOFF) { lp_status = lp_status_t::CUTOFF; } original_solution.iterations = iter; } else { - // Dual infeasible -> Primal unbounded + // Dual infeasible -> Primal unbounded or infeasible settings.log.printf("Dual infeasible\n"); original_solution.objective = -inf; if (lp.obj_scale == 1.0) { @@ -323,7 +325,7 @@ lp_status_t solve_linear_program_with_advanced_basis( original_solution.user_objective = inf; } original_solution.iterations = iter; - return lp_status_t::UNBOUNDED; + return lp_status_t::UNBOUNDED_OR_INFEASIBLE; } return lp_status; } diff --git a/cpp/src/dual_simplex/solve.hpp b/cpp/src/dual_simplex/solve.hpp index c8a5cb3105..66a7e1ba86 100644 --- a/cpp/src/dual_simplex/solve.hpp +++ b/cpp/src/dual_simplex/solve.hpp @@ -23,16 +23,17 @@ template bool is_mip(const user_problem_t& problem); enum class lp_status_t { - OPTIMAL = 0, - INFEASIBLE = 1, - UNBOUNDED = 2, - ITERATION_LIMIT = 3, - TIME_LIMIT = 4, - NUMERICAL_ISSUES = 5, - CUTOFF = 6, - CONCURRENT_LIMIT = 7, - WORK_LIMIT = 8, - UNSET = 9 + OPTIMAL = 0, + INFEASIBLE = 1, + UNBOUNDED = 2, + ITERATION_LIMIT = 3, + TIME_LIMIT = 4, + NUMERICAL_ISSUES = 5, + CUTOFF = 6, + CONCURRENT_LIMIT = 7, + WORK_LIMIT = 8, + UNBOUNDED_OR_INFEASIBLE = 9, + UNSET = 10 }; static std::string lp_status_to_string(lp_status_t status) @@ -47,6 +48,7 @@ static std::string lp_status_to_string(lp_status_t status) case lp_status_t::CUTOFF: return "CUTOFF"; case lp_status_t::CONCURRENT_LIMIT: return "CONCURRENT_LIMIT"; case lp_status_t::WORK_LIMIT: return "WORK_LIMIT"; + case lp_status_t::UNBOUNDED_OR_INFEASIBLE: return "UNBOUNDED_OR_INFEASIBLE"; case lp_status_t::UNSET: return "UNSET"; } return "UNKNOWN"; diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp index 2fa10f421b..5ed479ac67 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp @@ -477,6 +477,36 @@ void check_presolve_status(const papilo::PresolveStatus& status) } } +third_party_presolve_status_t convert_papilo_presolve_status_to_third_party_presolve_status( + const papilo::PresolveStatus& status) +{ + switch (status) { + case papilo::PresolveStatus::kUnchanged: return third_party_presolve_status_t::UNCHANGED; + case papilo::PresolveStatus::kReduced: return third_party_presolve_status_t::REDUCED; + case papilo::PresolveStatus::kUnbndOrInfeas: + return third_party_presolve_status_t::UNBNDORINFEAS; + case papilo::PresolveStatus::kInfeasible: return third_party_presolve_status_t::INFEASIBLE; + case papilo::PresolveStatus::kUnbounded: + return third_party_presolve_status_t::UNBOUNDED; + // Do not implement default case to trigger compile time error if new enum is added + } + return third_party_presolve_status_t::UNCHANGED; +} + +third_party_presolve_status_t convert_pslp_presolve_status_to_third_party_presolve_status( + const PresolveStatus& status) +{ + switch (status) { + case PresolveStatus_::UNCHANGED: return third_party_presolve_status_t::UNCHANGED; + case PresolveStatus_::REDUCED: return third_party_presolve_status_t::REDUCED; + case PresolveStatus_::INFEASIBLE: return third_party_presolve_status_t::INFEASIBLE; + case PresolveStatus_::UNBNDORINFEAS: + return third_party_presolve_status_t::UNBNDORINFEAS; + // Do not implement default case to trigger compile time error if new enum is added + } + return third_party_presolve_status_t::UNCHANGED; +} + void check_postsolve_status(const papilo::PostsolveStatus& status) { switch (status) { @@ -570,7 +600,7 @@ void set_presolve_parameters(papilo::Presolve& presolver, } template -std::optional> third_party_presolve_t::apply_pslp( +third_party_presolve_result_t third_party_presolve_t::apply_pslp( optimization_problem_t const& op_problem, const double time_limit) { if constexpr (std::is_same_v) { @@ -584,23 +614,29 @@ std::optional> third_party_presolve_t empty_problem(op_problem.get_handle_ptr()); + return third_party_presolve_result_t{status, std::move(empty_problem), {}, {}, {}}; } auto opt_problem = build_optimization_problem_from_pslp( pslp_presolver_, op_problem.get_handle_ptr(), maximize_, original_obj_offset); - opt_problem.set_problem_name(op_problem.get_problem_name()); - return std::make_optional(third_party_presolve_result_t{opt_problem, {}}); + return third_party_presolve_result_t{status, std::move(opt_problem), {}, {}, {}}; } else { cuopt_expects( false, error_type_t::ValidationError, "PSLP presolver only supports double precision"); - return std::nullopt; + return third_party_presolve_result_t{ + third_party_presolve_status_t::UNCHANGED, + optimization_problem_t(op_problem.get_handle_ptr()), + {}, + {}, + {}}; } } template -std::optional> third_party_presolve_t::apply( +third_party_presolve_result_t third_party_presolve_t::apply( optimization_problem_t const& op_problem, problem_category_t category, cuopt::linear_programming::presolver_t presolver, @@ -648,9 +684,12 @@ std::optional> third_party_presolve_t empty_problem(op_problem.get_handle_ptr()); + return third_party_presolve_result_t{status, std::move(empty_problem), {}, {}, {}}; } papilo_post_solve_storage_.reset(new papilo::PostsolveStorage(result.postsolve)); CUOPT_LOG_INFO("Presolve removed: %d constraints, %d variables, %d nonzeros", @@ -686,8 +725,11 @@ std::optional> third_party_presolve_t{ - opt_problem, implied_integer_indices, reduced_to_original_map_, original_to_reduced_map_}); + return third_party_presolve_result_t{status, + std::move(opt_problem), + implied_integer_indices, + reduced_to_original_map_, + original_to_reduced_map_}; } template diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp index ee273b6497..73743715f3 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp @@ -27,8 +27,18 @@ struct papilo_postsolve_deleter { void operator()(papilo::PostsolveStorage* ptr) const; }; +enum class third_party_presolve_status_t { + INFEASIBLE, + UNBNDORINFEAS, + UNBOUNDED, + OPTIMAL, + REDUCED, + UNCHANGED, +}; + template struct third_party_presolve_result_t { + third_party_presolve_status_t status; optimization_problem_t reduced_problem; std::vector implied_integer_indices; std::vector reduced_to_original_map; @@ -48,15 +58,14 @@ class third_party_presolve_t { third_party_presolve_t(third_party_presolve_t&&) = delete; third_party_presolve_t& operator=(third_party_presolve_t&&) = delete; - std::optional> apply( - optimization_problem_t const& op_problem, - problem_category_t category, - cuopt::linear_programming::presolver_t presolver, - bool dual_postsolve, - f_t absolute_tolerance, - f_t relative_tolerance, - double time_limit, - i_t num_cpu_threads = 0); + third_party_presolve_result_t apply(optimization_problem_t const& op_problem, + problem_category_t category, + cuopt::linear_programming::presolver_t presolver, + bool dual_postsolve, + f_t absolute_tolerance, + f_t relative_tolerance, + double time_limit, + i_t num_cpu_threads = 0); void undo(rmm::device_uvector& primal_solution, rmm::device_uvector& dual_solution, @@ -74,7 +83,7 @@ class third_party_presolve_t { ~third_party_presolve_t(); private: - std::optional> apply_pslp( + third_party_presolve_result_t apply_pslp( optimization_problem_t const& op_problem, const double time_limit); void undo_pslp(rmm::device_uvector& primal_solution, diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index f5a2172f2e..cf85e21788 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -244,7 +244,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, double presolve_time = 0.0; std::unique_ptr> presolver; - std::optional> presolve_result; + std::optional> presolve_result_opt; detail::problem_t problem( op_problem, settings.get_tolerances(), settings.determinism_mode == CUOPT_MODE_DETERMINISTIC); @@ -283,22 +283,32 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, settings.tolerances.relative_tolerance, presolve_time_limit, settings.num_cpu_threads); - if (!result.has_value()) { + if (result.status == detail::third_party_presolve_status_t::INFEASIBLE) { return mip_solution_t(mip_termination_status_t::Infeasible, solver_stats_t{}, op_problem.get_handle_ptr()->get_stream()); } - presolve_result.emplace(std::move(*result)); + if (result.status == detail::third_party_presolve_status_t::UNBNDORINFEAS) { + return mip_solution_t(mip_termination_status_t::UnboundedOrInfeasible, + solver_stats_t{}, + op_problem.get_handle_ptr()->get_stream()); + } + if (result.status == detail::third_party_presolve_status_t::UNBOUNDED) { + return mip_solution_t(mip_termination_status_t::Unbounded, + solver_stats_t{}, + op_problem.get_handle_ptr()->get_stream()); + } + presolve_result_opt.emplace(std::move(result)); - problem = detail::problem_t(presolve_result->reduced_problem); + problem = detail::problem_t(presolve_result_opt->reduced_problem); problem.set_papilo_presolve_data(presolver.get(), - presolve_result->reduced_to_original_map, - presolve_result->original_to_reduced_map, + presolve_result_opt->reduced_to_original_map, + presolve_result_opt->original_to_reduced_map, op_problem.get_n_variables()); - problem.set_implied_integers(presolve_result->implied_integer_indices); + problem.set_implied_integers(presolve_result_opt->implied_integer_indices); presolve_time = timer.elapsed_time(); - if (presolve_result->implied_integer_indices.size() > 0) { - CUOPT_LOG_INFO("%d implied integers", presolve_result->implied_integer_indices.size()); + if (presolve_result_opt->implied_integer_indices.size() > 0) { + CUOPT_LOG_INFO("%d implied integers", presolve_result_opt->implied_integer_indices.size()); } CUOPT_LOG_INFO("Papilo presolve time: %.2f", presolve_time); } diff --git a/cpp/src/mip_heuristics/solver_solution.cu b/cpp/src/mip_heuristics/solver_solution.cu index e497a21c8f..a9bc6c5416 100644 --- a/cpp/src/mip_heuristics/solver_solution.cu +++ b/cpp/src/mip_heuristics/solver_solution.cu @@ -137,8 +137,9 @@ std::string mip_solution_t::get_termination_status_string( case mip_termination_status_t::Infeasible: return "Infeasible"; case mip_termination_status_t::TimeLimit: return "TimeLimit"; case mip_termination_status_t::WorkLimit: return "WorkLimit"; - case mip_termination_status_t::Unbounded: - return "Unbounded"; + case mip_termination_status_t::Unbounded: return "Unbounded"; + case mip_termination_status_t::UnboundedOrInfeasible: + return "UnboundedOrInfeasible"; // Do not implement default case to trigger compile time error if new enum is added } return std::string(); diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 2fc9ec08d5..cefa22875e 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -363,6 +363,8 @@ optimization_problem_solution_t convert_dual_simplex_sol( return pdlp_termination_status_t::IterationLimit; case dual_simplex::lp_status_t::CONCURRENT_LIMIT: return pdlp_termination_status_t::ConcurrentLimit; + case dual_simplex::lp_status_t::UNBOUNDED_OR_INFEASIBLE: + return pdlp_termination_status_t::UnboundedOrInfeasible; default: return pdlp_termination_status_t::NumericalError; } }; @@ -463,9 +465,11 @@ run_barrier(dual_simplex::user_problem_t& user_problem, 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 || - status == dual_simplex::lp_status_t::INFEASIBLE)) { + if (settings.concurrent_halt != nullptr && + (status == dual_simplex::lp_status_t::OPTIMAL || + status == dual_simplex::lp_status_t::UNBOUNDED || + status == dual_simplex::lp_status_t::INFEASIBLE || + status == dual_simplex::lp_status_t::UNBOUNDED_OR_INFEASIBLE)) { // We finished. Tell PDLP to stop if it is still running. *settings.concurrent_halt = 1; } @@ -535,9 +539,11 @@ run_dual_simplex(dual_simplex::user_problem_t& user_problem, CUOPT_LOG_CONDITIONAL_INFO( !settings.inside_mip, "Dual simplex 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 || - status == dual_simplex::lp_status_t::INFEASIBLE)) { + if (settings.concurrent_halt != nullptr && + (status == dual_simplex::lp_status_t::OPTIMAL || + status == dual_simplex::lp_status_t::UNBOUNDED || + status == dual_simplex::lp_status_t::INFEASIBLE || + status == dual_simplex::lp_status_t::UNBOUNDED_OR_INFEASIBLE)) { // We finished. Tell PDLP to stop if it is still running. *settings.concurrent_halt = 1; } @@ -1361,7 +1367,7 @@ optimization_problem_solution_t solve_lp( auto run_presolve = settings.presolver != presolver_t::None; run_presolve = run_presolve && settings.get_pdlp_warm_start_data().total_pdlp_iterations_ == -1; - // Declare result at outer scope so that result->reduced_problem (which may be + // Declare result at outer scope so that result.reduced_problem (which may be // referenced by problem.original_problem_ptr) remains alive through the solve. std::optional> result; @@ -1380,10 +1386,19 @@ optimization_problem_solution_t solve_lp( settings.tolerances.absolute_primal_tolerance, settings.tolerances.relative_primal_tolerance, presolve_time_limit); - if (!result.has_value()) { + if (result->status == detail::third_party_presolve_status_t::INFEASIBLE) { return optimization_problem_solution_t( pdlp_termination_status_t::PrimalInfeasible, op_problem.get_handle_ptr()->get_stream()); } + if (result->status == detail::third_party_presolve_status_t::UNBNDORINFEAS) { + return optimization_problem_solution_t( + pdlp_termination_status_t::UnboundedOrInfeasible, + op_problem.get_handle_ptr()->get_stream()); + } + if (result->status == detail::third_party_presolve_status_t::UNBOUNDED) { + return optimization_problem_solution_t(pdlp_termination_status_t::DualInfeasible, + op_problem.get_handle_ptr()->get_stream()); + } // Handle case where presolve completely solved the problem (reduced to 0 rows/cols) // Must check before constructing problem_t since it fails on empty problems diff --git a/cpp/src/pdlp/solver_solution.cu b/cpp/src/pdlp/solver_solution.cu index 10e6a80593..ba68d20740 100644 --- a/cpp/src/pdlp/solver_solution.cu +++ b/cpp/src/pdlp/solver_solution.cu @@ -316,8 +316,12 @@ std::string optimization_problem_solution_t::get_termination_status_st case pdlp_termination_status_t::NumericalError: return "A numerical error was encountered."; case pdlp_termination_status_t::PrimalFeasible: return "Primal Feasible"; case pdlp_termination_status_t::ConcurrentLimit: return "Concurrent Limit"; - default: return "Unknown cuOpt status"; + case pdlp_termination_status_t::UnboundedOrInfeasible: return "UnboundedOrInfeasible"; + case pdlp_termination_status_t::NoTermination: + return "NoTermination"; + // Do not implement default case to trigger compile time error if new enum is added } + return std::string(); } template diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_test.c b/cpp/tests/linear_programming/c_api_tests/c_api_test.c index 996d60deae..689c8ed742 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_test.c +++ b/cpp/tests/linear_programming/c_api_tests/c_api_test.c @@ -38,21 +38,21 @@ int check_problem(cuOptOptimizationProblem problem, const char* termination_status_to_string(cuopt_int_t termination_status) { switch (termination_status) { - case CUOPT_TERIMINATION_STATUS_OPTIMAL: + case CUOPT_TERMINATION_STATUS_OPTIMAL: return "Optimal"; - case CUOPT_TERIMINATION_STATUS_INFEASIBLE: + case CUOPT_TERMINATION_STATUS_INFEASIBLE: return "Infeasible"; - case CUOPT_TERIMINATION_STATUS_UNBOUNDED: + case CUOPT_TERMINATION_STATUS_UNBOUNDED: return "Unbounded"; - case CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT: + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: return "Iteration limit"; - case CUOPT_TERIMINATION_STATUS_TIME_LIMIT: + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: return "Time limit"; - case CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR: + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: return "Numerical error"; - case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: return "Primal feasible"; - case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; } return "Unknown"; @@ -428,9 +428,9 @@ cuopt_int_t burglar_problem() printf("Error getting termination status\n"); goto DONE; } - if (termination_status != CUOPT_TERIMINATION_STATUS_OPTIMAL) { + if (termination_status != CUOPT_TERMINATION_STATUS_OPTIMAL) { printf("Error: expected termination status to be %d, but got %d\n", - CUOPT_TERIMINATION_STATUS_OPTIMAL, + CUOPT_TERMINATION_STATUS_OPTIMAL, termination_status); status = -1; goto DONE; @@ -898,9 +898,9 @@ cuopt_int_t test_infeasible_problem() printf("Error getting termination status\n"); goto DONE; } - if (termination_status != CUOPT_TERIMINATION_STATUS_INFEASIBLE) { + if (termination_status != CUOPT_TERMINATION_STATUS_INFEASIBLE) { printf("Error: expected termination status to be %d, but got %d\n", - CUOPT_TERIMINATION_STATUS_INFEASIBLE, + CUOPT_TERMINATION_STATUS_INFEASIBLE, termination_status); status = -1; goto DONE; @@ -1151,9 +1151,9 @@ cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) printf("Error getting termination status: %d\n", status); goto DONE; } - if (termination_status != CUOPT_TERIMINATION_STATUS_INFEASIBLE) { + if (termination_status != CUOPT_TERMINATION_STATUS_INFEASIBLE) { printf("Error: expected termination status to be %d, but got %d\n", - CUOPT_TERIMINATION_STATUS_INFEASIBLE, + CUOPT_TERMINATION_STATUS_INFEASIBLE, termination_status); status = CUOPT_VALIDATION_ERROR; goto DONE; @@ -1442,7 +1442,7 @@ cuopt_int_t test_write_problem(const char* input_filename, const char* output_fi printf("Termination status: %d, Objective: %f\n", termination_status, objective_value); - if (termination_status != CUOPT_TERIMINATION_STATUS_OPTIMAL) { + if (termination_status != CUOPT_TERMINATION_STATUS_OPTIMAL) { printf("Expected optimal status\n"); status = -1; goto DONE; @@ -1700,9 +1700,9 @@ cuopt_int_t test_deterministic_bb(const char* filename, goto DONE; } - if (termination_status != CUOPT_TERIMINATION_STATUS_OPTIMAL && - termination_status != CUOPT_TERIMINATION_STATUS_TIME_LIMIT && - termination_status != CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND) { + if (termination_status != CUOPT_TERMINATION_STATUS_OPTIMAL && + termination_status != CUOPT_TERMINATION_STATUS_TIME_LIMIT && + termination_status != CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND) { printf("Run %d: status=%s (%d), unexpected termination status\n", run, termination_status_to_string(termination_status), diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp b/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp index d39a970763..fc17c2b130 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp +++ b/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp @@ -35,7 +35,7 @@ TEST(c_api, afiro) int termination_status; EXPECT_EQ(solve_mps_file(filename.c_str(), 60, CUOPT_INFINITY, &termination_status), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); } // Test both LP and MIP codepaths @@ -56,7 +56,7 @@ TEST_P(TimeLimitTestFixture, time_limit) &solve_time, method), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_TIME_LIMIT); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_TIME_LIMIT); // Dual simplex is spending some time for factorizing the basis, and this computation does not // check for time limit @@ -80,7 +80,7 @@ TEST(c_api, iteration_limit) std::string filename = rapidsDatasetRootDir + "/linear_programming/" + "afiro_original.mps"; int termination_status; EXPECT_EQ(solve_mps_file(filename.c_str(), 60, 1, &termination_status), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_ITERATION_LIMIT); } TEST(c_api, solve_time_bb_preemption) @@ -91,7 +91,7 @@ TEST(c_api, solve_time_bb_preemption) double solve_time = std::numeric_limits::quiet_NaN(); EXPECT_EQ(solve_mps_file(filename.c_str(), 5, CUOPT_INFINITY, &termination_status, &solve_time), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_GT(solve_time, 0); // solve time should not be equal to 0, even on very simple instances // solved by B&B before the diversity solver has time to run } @@ -113,7 +113,7 @@ TEST(c_api, test_ranged_problem) cuopt_int_t termination_status; cuopt_float_t objective; EXPECT_EQ(test_ranged_problem(&termination_status, &objective), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR(objective, 32.0, 1e-3); } @@ -130,7 +130,7 @@ TEST(c_api, test_quadratic_problem) cuopt_int_t termination_status; cuopt_float_t objective; EXPECT_EQ(test_quadratic_problem(&termination_status, &objective), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR(objective, -32.0, 1e-3); } @@ -139,7 +139,7 @@ TEST(c_api, test_quadratic_ranged_problem) cuopt_int_t termination_status; cuopt_float_t objective; EXPECT_EQ(test_quadratic_ranged_problem(&termination_status, &objective), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, (int)CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, (int)CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR(objective, -32.0, 1e-3); } @@ -163,7 +163,7 @@ TEST(c_api, test_maximize_problem_dual_variables) test_maximize_problem_dual_variables( method, &termination_status, &objective, dual_variables, reduced_costs, &dual_objective), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR(objective, dual_objective, method == CUOPT_METHOD_CONCURRENT || method == CUOPT_METHOD_PDLP ? 1e-2 : 1e-5); @@ -287,7 +287,7 @@ TEST(c_api, pdlp_precision_single) cuopt_float_t objective; EXPECT_EQ(test_pdlp_precision_single(filename.c_str(), &termination_status, &objective), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR(objective, -464.7531, 1e-1); } @@ -302,13 +302,13 @@ TEST(c_api, pdlp_precision_mixed) auto status = test_pdlp_precision_mixed(filename.c_str(), &termination_status, &objective); bool solve_returned_error = (status != CUOPT_SUCCESS); bool solve_returned_non_optimal = - (status == CUOPT_SUCCESS && termination_status != CUOPT_TERIMINATION_STATUS_OPTIMAL); + (status == CUOPT_SUCCESS && termination_status != CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_TRUE(solve_returned_error || solve_returned_non_optimal); return; } EXPECT_EQ(test_pdlp_precision_mixed(filename.c_str(), &termination_status, &objective), CUOPT_SUCCESS); - EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR(objective, -464.7531, 1e-1); } diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index d5a8d69008..50d4a8fb45 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -70,7 +70,7 @@ TEST(pdlp_class, run_double) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } @@ -106,7 +106,7 @@ TEST(pdlp_class, precision_mixed) optimization_problem_solution_t solution_mixed = solve_lp(&handle_, op_problem, settings_mixed); - EXPECT_EQ((int)solution_mixed.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution_mixed.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution_mixed.get_additional_termination_information().primal_objective)); @@ -117,7 +117,7 @@ TEST(pdlp_class, precision_mixed) optimization_problem_solution_t solution_full = solve_lp(&handle_, op_problem, settings_full); - EXPECT_EQ((int)solution_full.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution_full.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution_full.get_additional_termination_information().primal_objective)); @@ -148,7 +148,7 @@ TEST(pdlp_class, run_double_very_low_accuracy) 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_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } @@ -170,7 +170,7 @@ TEST(pdlp_class, run_double_initial_solution) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } @@ -192,7 +192,7 @@ TEST(pdlp_class, run_iteration_limit) 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); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_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(), @@ -218,7 +218,7 @@ TEST(pdlp_class, run_time_limit) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_TIME_LIMIT); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_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(), @@ -276,7 +276,7 @@ TEST(pdlp_class, run_sub_mittleman) name.c_str(), (int)solver_mode, (int)settings.presolver); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( expected_objective_value, solution.get_additional_termination_information().primal_objective)); @@ -970,7 +970,7 @@ TEST(pdlp_class, dual_postsolve_size) solver_settings.dual_postsolve = true; optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_EQ(solution.get_dual_solution().size(), op_problem.get_n_constraints()); } @@ -978,7 +978,7 @@ TEST(pdlp_class, dual_postsolve_size) solver_settings.dual_postsolve = false; optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_EQ(solution.get_dual_solution().size(), 0); } } @@ -1017,7 +1017,7 @@ TEST(pdlp_class, run_empty_matrix_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); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR); } // Should run thanks to Dual Simplex @@ -1035,7 +1035,7 @@ TEST(pdlp_class, run_empty_matrix_dual_simplex) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(solution.get_additional_termination_information().solved_by_pdlp); } @@ -1054,7 +1054,7 @@ TEST(pdlp_class, test_max) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR( solution.get_additional_termination_information().primal_objective, 17.0, factor_tolerance); } @@ -1073,7 +1073,7 @@ TEST(pdlp_class, test_max_with_offset) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR( solution.get_additional_termination_information().primal_objective, 0.0, factor_tolerance); } @@ -1091,7 +1091,7 @@ TEST(pdlp_class, test_lp_no_constraints) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_NEAR( solution.get_additional_termination_information().primal_objective, 1.0, factor_tolerance); } @@ -1133,7 +1133,7 @@ TEST(pdlp_class, simple_batch_afiro) // All should be optimal with the right objective for (size_t i = 0; i < batch_size; ++i) { - EXPECT_EQ((int)solution.get_termination_status(i), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(i), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information(i).primal_objective)); } @@ -1225,10 +1225,10 @@ TEST(pdlp_class, simple_batch_different_bounds) // Both should be optimal // Climber #0 should have same objective as ref and #1 as the usual - EXPECT_EQ((int)solution2.get_termination_status(0), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution2.get_termination_status(0), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( new_primal, solution2.get_additional_termination_information(0).primal_objective)); - EXPECT_EQ((int)solution2.get_termination_status(1), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution2.get_termination_status(1), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution2.get_additional_termination_information(1).primal_objective)); @@ -1298,7 +1298,7 @@ TEST(pdlp_class, more_complex_batch_different_bounds) // All should be optimal for (size_t i = 0; i < batch_size; ++i) - EXPECT_EQ((int)solution3.get_termination_status(i), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution3.get_termination_status(i), CUOPT_TERMINATION_STATUS_OPTIMAL); // Climber #0 #2 #4 should have the same primal objective which is the unmodified one EXPECT_FALSE(is_incorrect_objective( @@ -1529,7 +1529,7 @@ TEST(pdlp_class, big_batch_afiro) // All should be optimal with for (size_t i = 0; i < batch_size; ++i) { - EXPECT_EQ((int)solution.get_termination_status(i), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(i), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information(i).primal_objective)); } @@ -1613,8 +1613,8 @@ TEST(pdlp_class, DISABLED_simple_batch_optimal_and_infeasible) solve_lp(&handle_, op_problem, solver_settings); // First should be primal infeasible and the second optimal with the correct - EXPECT_EQ((int)solution.get_termination_status(0), CUOPT_TERIMINATION_STATUS_INFEASIBLE); - EXPECT_EQ((int)solution.get_termination_status(1), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(0), CUOPT_TERMINATION_STATUS_INFEASIBLE); + EXPECT_EQ((int)solution.get_termination_status(1), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information(1).primal_objective)); } @@ -1650,13 +1650,13 @@ TEST(pdlp_class, DISABLED_larger_batch_optimal_and_infeasible) solve_lp(&handle_, op_problem, solver_settings); // #1 and #3 should be infeasible - EXPECT_EQ((int)solution.get_termination_status(1), CUOPT_TERIMINATION_STATUS_INFEASIBLE); - EXPECT_EQ((int)solution.get_termination_status(3), CUOPT_TERIMINATION_STATUS_INFEASIBLE); + EXPECT_EQ((int)solution.get_termination_status(1), CUOPT_TERMINATION_STATUS_INFEASIBLE); + EXPECT_EQ((int)solution.get_termination_status(3), CUOPT_TERMINATION_STATUS_INFEASIBLE); // Rest should be feasible with the correct primal objective - EXPECT_EQ((int)solution.get_termination_status(0), CUOPT_TERIMINATION_STATUS_OPTIMAL); - EXPECT_EQ((int)solution.get_termination_status(2), CUOPT_TERIMINATION_STATUS_OPTIMAL); - EXPECT_EQ((int)solution.get_termination_status(4), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(0), CUOPT_TERMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(2), CUOPT_TERMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(4), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information(0).primal_objective)); @@ -1956,7 +1956,7 @@ TEST(pdlp_class, precision_single) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); @@ -1977,7 +1977,7 @@ TEST(pdlp_class, precision_single_crossover) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); @@ -1997,7 +1997,7 @@ TEST(pdlp_class, precision_single_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_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); @@ -2018,7 +2018,7 @@ TEST(pdlp_class, precision_single_papilo_presolve) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } @@ -2038,7 +2038,7 @@ TEST(pdlp_class, precision_single_pslp_presolve) optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); EXPECT_FALSE(is_incorrect_objective( afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } diff --git a/cpp/tests/linear_programming/unit_tests/presolve_test.cu b/cpp/tests/linear_programming/unit_tests/presolve_test.cu index 97dfe0957e..22fe9a39e1 100644 --- a/cpp/tests/linear_programming/unit_tests/presolve_test.cu +++ b/cpp/tests/linear_programming/unit_tests/presolve_test.cu @@ -137,7 +137,7 @@ TEST(pslp_presolve, postsolve_accuracy_afiro) optimization_problem_solution_t solution = solve_lp(&handle_, mps_data_model, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); // Get the postsolved primal solution auto h_primal_solution = host_copy(solution.get_primal_solution(), handle_.get_stream()); @@ -181,7 +181,7 @@ TEST(pslp_presolve, postsolve_dual_accuracy_afiro) optimization_problem_solution_t solution = solve_lp(&handle_, mps_data_model, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); // Get postsolved solutions auto h_primal = host_copy(solution.get_primal_solution(), handle_.get_stream()); @@ -231,7 +231,7 @@ TEST(pslp_presolve, postsolve_accuracy_larger_problem) optimization_problem_solution_t solution = solve_lp(&handle_, mps_data_model, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); auto h_primal = host_copy(solution.get_primal_solution(), handle_.get_stream()); @@ -285,8 +285,8 @@ TEST(pslp_presolve, compare_with_no_presolve) solve_lp(&handle_, mps_data_model, settings_pslp); // Both should be optimal - EXPECT_EQ((int)solution_no_presolve.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); - EXPECT_EQ((int)solution_pslp.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution_no_presolve.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution_pslp.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); // Objective values should match double obj_no_presolve = @@ -336,7 +336,7 @@ TEST(pslp_presolve, postsolve_reduced_costs) optimization_problem_solution_t solution = solve_lp(&handle_, mps_data_model, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); // Get postsolved reduced costs auto h_reduced_costs = host_copy(solution.get_reduced_cost(), handle_.get_stream()); @@ -370,7 +370,7 @@ TEST(pslp_presolve, postsolve_multiple_problems) optimization_problem_solution_t solution = solve_lp(&handle_, mps_data_model, solver_settings); - EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL) + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL) << "Problem " << name << " should be optimal"; auto h_primal = host_copy(solution.get_primal_solution(), handle_.get_stream()); diff --git a/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu b/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu index 0146204c87..08da011a3d 100644 --- a/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu +++ b/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu @@ -288,7 +288,7 @@ TEST_F(SolutionInterfaceTest, termination_status_int_values) optimization_problem_solution_interface_t* base = / int status = base->get_termination_status_int(); - EXPECT_EQ(status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_EQ(status, CUOPT_TERMINATION_STATUS_OPTIMAL); } // ============================================================================= diff --git a/cpp/tests/mip/bounds_standardization_test.cu b/cpp/tests/mip/bounds_standardization_test.cu index a87419f252..c96c749a42 100644 --- a/cpp/tests/mip/bounds_standardization_test.cu +++ b/cpp/tests/mip/bounds_standardization_test.cu @@ -67,7 +67,7 @@ void test_bounds_standardization_test(std::string test_instance) // run the problem through pdlp auto result_1 = detail::get_relaxed_lp_solution(standardized_problem, solution_1, lp_settings); solution_1.compute_feasibility(); - bool sol_1_feasible = (int)result_1.get_termination_status() == CUOPT_TERIMINATION_STATUS_OPTIMAL; + bool sol_1_feasible = (int)result_1.get_termination_status() == CUOPT_TERMINATION_STATUS_OPTIMAL; // the problem might not be feasible in terms of per constraint residual // only consider the pdlp results EXPECT_TRUE(sol_1_feasible); diff --git a/cpp/tests/mip/elim_var_remap_test.cu b/cpp/tests/mip/elim_var_remap_test.cu index 8b3cd0fecf..6be67780e9 100644 --- a/cpp/tests/mip/elim_var_remap_test.cu +++ b/cpp/tests/mip/elim_var_remap_test.cu @@ -152,8 +152,8 @@ void test_elim_var_solution(std::string test_instance) auto result_1 = detail::get_relaxed_lp_solution(standardized_problem, solution_1, lp_settings); solution_1.compute_feasibility(); // the solution might not be feasible per row as we are getting the result of pdlp - bool sol_1_feasible = (int)result_1.get_termination_status() == CUOPT_TERIMINATION_STATUS_OPTIMAL; - EXPECT_EQ((int)result_1.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + bool sol_1_feasible = (int)result_1.get_termination_status() == CUOPT_TERMINATION_STATUS_OPTIMAL; + EXPECT_EQ((int)result_1.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); standardized_problem.post_process_solution(solution_1); solution_1.problem_ptr = &original_problem; auto opt_sol_1 = solution_1.get_solution(sol_1_feasible, solver_stats_t{}); @@ -181,8 +181,8 @@ void test_elim_var_solution(std::string test_instance) // run the problem through pdlp auto result_2 = detail::get_relaxed_lp_solution(sub_problem, solution_2, lp_settings_2); solution_2.compute_feasibility(); - bool sol_2_feasible = (int)result_2.get_termination_status() == CUOPT_TERIMINATION_STATUS_OPTIMAL; - EXPECT_EQ((int)result_2.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + bool sol_2_feasible = (int)result_2.get_termination_status() == CUOPT_TERMINATION_STATUS_OPTIMAL; + EXPECT_EQ((int)result_2.get_termination_status(), CUOPT_TERMINATION_STATUS_OPTIMAL); sub_problem.post_process_solution(solution_2); solution_2.problem_ptr = &original_problem; auto opt_sol_2 = solution_2.get_solution(sol_2_feasible, solver_stats_t{}); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index cf8abd1b69..cf2532d0f2 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -45,11 +45,12 @@ TEST(problem, find_implied_integers) 1e-12, 20, 1); - ASSERT_TRUE(result.has_value()); + ASSERT_NE(result.status, detail::third_party_presolve_status_t::INFEASIBLE); + ASSERT_NE(result.status, detail::third_party_presolve_status_t::UNBNDORINFEAS); - auto problem = detail::problem_t(result->reduced_problem); - problem.set_implied_integers(result->implied_integer_indices); - ASSERT_TRUE(result->implied_integer_indices.size() > 0); + auto problem = detail::problem_t(result.reduced_problem); + problem.set_implied_integers(result.implied_integer_indices); + ASSERT_TRUE(result.implied_integer_indices.size() > 0); auto var_types = host_copy(problem.variable_types, handle_.get_stream()); // Find the index of the one continuous variable auto it = std::find_if(var_types.begin(), var_types.end(), [](var_t var_type) { diff --git a/cpp/tests/routing/unit_tests/breaks.cu b/cpp/tests/routing/unit_tests/breaks.cu index a2abc0ac8c..0d8a578b6e 100644 --- a/cpp/tests/routing/unit_tests/breaks.cu +++ b/cpp/tests/routing/unit_tests/breaks.cu @@ -354,7 +354,6 @@ TEST(vehicle_breaks, non_uniform_breaks) order_service[i] = route.service_time_h[i + 1]; } int num_v_type_1 = vehicle_num / 2; - int num_v_type_2 = vehicle_num - num_v_type_1; int num_breaks = 3; // Type 1: [40,50]/5, [100,120]/20, [170,180]/10 diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/milp_mps_example.c b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/milp_mps_example.c index fe59f2fc45..6d80679514 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/milp_mps_example.c +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/milp_mps_example.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* @@ -13,22 +13,24 @@ const char* termination_status_to_string(cuopt_int_t termination_status) { switch (termination_status) { - case CUOPT_TERIMINATION_STATUS_OPTIMAL: + case CUOPT_TERMINATION_STATUS_OPTIMAL: return "Optimal"; - case CUOPT_TERIMINATION_STATUS_INFEASIBLE: + case CUOPT_TERMINATION_STATUS_INFEASIBLE: return "Infeasible"; - case CUOPT_TERIMINATION_STATUS_UNBOUNDED: + case CUOPT_TERMINATION_STATUS_UNBOUNDED: return "Unbounded"; - case CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT: + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: return "Iteration limit"; - case CUOPT_TERIMINATION_STATUS_TIME_LIMIT: + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: return "Time limit"; - case CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR: + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: return "Numerical error"; - case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: return "Primal feasible"; - case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE: + return "Unbounded or infeasible"; default: return "Unknown"; } @@ -96,10 +98,17 @@ cuopt_int_t solve_mps_file(const char* filename) goto DONE; } - status = cuOptGetObjectiveValue(solution, &objective_value); - if (status != CUOPT_SUCCESS) { - printf("Error getting objective value: %d\n", status); - goto DONE; + const int has_primal_solution = + termination_status == CUOPT_TERMINATION_STATUS_OPTIMAL || + termination_status == CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE || + termination_status == CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND; + + if (has_primal_solution) { + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto DONE; + } } // Print results @@ -111,11 +120,13 @@ cuopt_int_t solve_mps_file(const char* filename) printf("Objective value: %f\n", objective_value); // Get and print solution variables - solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); - status = cuOptGetPrimalSolution(solution, solution_values); - if (status != CUOPT_SUCCESS) { - printf("Error getting solution values: %d\n", status); - goto DONE; + if (has_primal_solution) { + solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + status = cuOptGetPrimalSolution(solution, solution_values); + if (status != CUOPT_SUCCESS) { + printf("Error getting solution values: %d\n", status); + goto DONE; + } } printf("\nSolution: \n"); @@ -124,7 +135,9 @@ cuopt_int_t solve_mps_file(const char* filename) } DONE: - free(solution_values); + if (solution_values != NULL) { + free(solution_values); + } cuOptDestroyProblem(&problem); cuOptDestroySolverSettings(&settings); cuOptDestroySolution(&solution); diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/mps_file_example.c b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/mps_file_example.c index 6fb3a4fc55..ac32e19afb 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/mps_file_example.c +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/mps_file_example.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* @@ -36,21 +36,21 @@ const char* termination_status_to_string(cuopt_int_t termination_status) { switch (termination_status) { - case CUOPT_TERIMINATION_STATUS_OPTIMAL: + case CUOPT_TERMINATION_STATUS_OPTIMAL: return "Optimal"; - case CUOPT_TERIMINATION_STATUS_INFEASIBLE: + case CUOPT_TERMINATION_STATUS_INFEASIBLE: return "Infeasible"; - case CUOPT_TERIMINATION_STATUS_UNBOUNDED: + case CUOPT_TERMINATION_STATUS_UNBOUNDED: return "Unbounded"; - case CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT: + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: return "Iteration limit"; - case CUOPT_TERIMINATION_STATUS_TIME_LIMIT: + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: return "Time limit"; - case CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR: + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: return "Numerical error"; - case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: return "Primal feasible"; - case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; default: return "Unknown"; diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_lp_example.c b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_lp_example.c index bd613af9be..2d675094c3 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_lp_example.c +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_lp_example.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* @@ -37,21 +37,21 @@ const char* termination_status_to_string(cuopt_int_t termination_status) { switch (termination_status) { - case CUOPT_TERIMINATION_STATUS_OPTIMAL: + case CUOPT_TERMINATION_STATUS_OPTIMAL: return "Optimal"; - case CUOPT_TERIMINATION_STATUS_INFEASIBLE: + case CUOPT_TERMINATION_STATUS_INFEASIBLE: return "Infeasible"; - case CUOPT_TERIMINATION_STATUS_UNBOUNDED: + case CUOPT_TERMINATION_STATUS_UNBOUNDED: return "Unbounded"; - case CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT: + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: return "Iteration limit"; - case CUOPT_TERIMINATION_STATUS_TIME_LIMIT: + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: return "Time limit"; - case CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR: + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: return "Numerical error"; - case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: return "Primal feasible"; - case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; default: return "Unknown"; diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_milp_example.c b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_milp_example.c index 61dba362cb..d406cc8e12 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_milp_example.c +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_milp_example.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* @@ -15,21 +15,21 @@ const char* termination_status_to_string(cuopt_int_t termination_status) { switch (termination_status) { - case CUOPT_TERIMINATION_STATUS_OPTIMAL: + case CUOPT_TERMINATION_STATUS_OPTIMAL: return "Optimal"; - case CUOPT_TERIMINATION_STATUS_INFEASIBLE: + case CUOPT_TERMINATION_STATUS_INFEASIBLE: return "Infeasible"; - case CUOPT_TERIMINATION_STATUS_UNBOUNDED: + case CUOPT_TERMINATION_STATUS_UNBOUNDED: return "Unbounded"; - case CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT: + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: return "Iteration limit"; - case CUOPT_TERIMINATION_STATUS_TIME_LIMIT: + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: return "Time limit"; - case CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR: + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: return "Numerical error"; - case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: return "Primal feasible"; - case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; default: return "Unknown"; diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_qp_example.c b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_qp_example.c index c888901156..a68f360e3b 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_qp_example.c +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/examples/simple_qp_example.c @@ -30,22 +30,24 @@ const char* termination_status_to_string(cuopt_int_t termination_status) { switch (termination_status) { - case CUOPT_TERIMINATION_STATUS_OPTIMAL: + case CUOPT_TERMINATION_STATUS_OPTIMAL: return "Optimal"; - case CUOPT_TERIMINATION_STATUS_INFEASIBLE: + case CUOPT_TERMINATION_STATUS_INFEASIBLE: return "Infeasible"; - case CUOPT_TERIMINATION_STATUS_UNBOUNDED: + case CUOPT_TERMINATION_STATUS_UNBOUNDED: return "Unbounded"; - case CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT: + case CUOPT_TERMINATION_STATUS_ITERATION_LIMIT: return "Iteration limit"; - case CUOPT_TERIMINATION_STATUS_TIME_LIMIT: + case CUOPT_TERMINATION_STATUS_TIME_LIMIT: return "Time limit"; - case CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR: + case CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR: return "Numerical error"; - case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: + case CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE: return "Primal feasible"; - case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: + case CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; + case CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE: + return "Unbounded or infeasible"; default: return "Unknown"; } diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst b/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst index d9a42301cb..e8511cfde0 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst @@ -266,13 +266,14 @@ Termination Status Constants These constants define the termination status received from the :c:func:`cuOptGetTerminationStatus` function. .. LP/MIP termination status constants -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_NO_TERMINATION -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_OPTIMAL -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_INFEASIBLE -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_UNBOUNDED -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_ITERATION_LIMIT -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_TIME_LIMIT -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND -.. doxygendefine:: CUOPT_TERIMINATION_STATUS_CONCURRENT_LIMIT +.. doxygendefine:: CUOPT_TERMINATION_STATUS_NO_TERMINATION +.. doxygendefine:: CUOPT_TERMINATION_STATUS_OPTIMAL +.. doxygendefine:: CUOPT_TERMINATION_STATUS_INFEASIBLE +.. doxygendefine:: CUOPT_TERMINATION_STATUS_UNBOUNDED +.. doxygendefine:: CUOPT_TERMINATION_STATUS_ITERATION_LIMIT +.. doxygendefine:: CUOPT_TERMINATION_STATUS_TIME_LIMIT +.. doxygendefine:: CUOPT_TERMINATION_STATUS_NUMERICAL_ERROR +.. doxygendefine:: CUOPT_TERMINATION_STATUS_PRIMAL_FEASIBLE +.. doxygendefine:: CUOPT_TERMINATION_STATUS_FEASIBLE_FOUND +.. doxygendefine:: CUOPT_TERMINATION_STATUS_CONCURRENT_LIMIT +.. doxygendefine:: CUOPT_TERMINATION_STATUS_UNBOUNDED_OR_INFEASIBLE diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.pxd b/python/cuopt/cuopt/linear_programming/solver/solver.pxd index 15597d48b0..2c3bb046d4 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.pxd +++ b/python/cuopt/cuopt/linear_programming/solver/solver.pxd @@ -106,6 +106,8 @@ cdef extern from "cuopt/linear_programming/mip/solver_solution.hpp" namespace "c Infeasible "cuopt::linear_programming::mip_termination_status_t::Infeasible" # noqa Unbounded "cuopt::linear_programming::mip_termination_status_t::Unbounded" # noqa TimeLimit "cuopt::linear_programming::mip_termination_status_t::TimeLimit" # noqa + WorkLimit "cuopt::linear_programming::mip_termination_status_t::WorkLimit" # noqa + UnboundedOrInfeasible "cuopt::linear_programming::mip_termination_status_t::UnboundedOrInfeasible" # noqa cdef extern from "cuopt/linear_programming/pdlp/solver_solution.hpp" namespace "cuopt::linear_programming": # noqa @@ -119,6 +121,7 @@ cdef extern from "cuopt/linear_programming/pdlp/solver_solution.hpp" namespace " TimeLimit "cuopt::linear_programming::pdlp_termination_status_t::TimeLimit" # noqa ConcurrentLimit "cuopt::linear_programming::pdlp_termination_status_t::ConcurrentLimit" # noqa PrimalFeasible "cuopt::linear_programming::pdlp_termination_status_t::PrimalFeasible" # noqa + UnboundedOrInfeasible "cuopt::linear_programming::pdlp_termination_status_t::UnboundedOrInfeasible" # noqa cdef extern from "cuopt/linear_programming/utilities/cython_types.hpp" namespace "cuopt::cython": # noqa diff --git a/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx b/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx index 10ff04d0a2..ebd89ece40 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx +++ b/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx @@ -80,6 +80,7 @@ class MILPTerminationStatus(IntEnum): Infeasible = mip_termination_status_t.Infeasible Unbounded = mip_termination_status_t.Unbounded TimeLimit = mip_termination_status_t.TimeLimit + UnboundedOrInfeasible = mip_termination_status_t.UnboundedOrInfeasible class LPTerminationStatus(IntEnum): @@ -91,6 +92,7 @@ class LPTerminationStatus(IntEnum): IterationLimit = pdlp_termination_status_t.IterationLimit TimeLimit = pdlp_termination_status_t.TimeLimit PrimalFeasible = pdlp_termination_status_t.PrimalFeasible + UnboundedOrInfeasible = pdlp_termination_status_t.UnboundedOrInfeasible class ErrorStatus(IntEnum): diff --git a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py index e284ffc0ab..08cb0b4a70 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -7,7 +7,11 @@ import numpy as np import pytest -from cuopt.linear_programming import data_model, solver, solver_settings +from cuopt.linear_programming import ( + data_model, + solver, + solver_settings, +) from cuopt.linear_programming.solver.solver_parameters import ( CUOPT_ABSOLUTE_DUAL_TOLERANCE, CUOPT_ABSOLUTE_GAP_TOLERANCE, @@ -36,6 +40,12 @@ from cuopt.linear_programming.solver_settings import ( PDLPSolverMode, SolverMethod, + SolverSettings, +) +from cuopt.linear_programming.problem import ( + Problem, + CONTINUOUS, + MINIMIZE, ) RAPIDS_DATASET_ROOT_DIR = os.getenv("RAPIDS_DATASET_ROOT_DIR") @@ -725,6 +735,22 @@ def test_write_files(): os.remove("afiro.sol") +def test_unbounded_problem(): + problem = Problem("unbounded") + x = problem.addVariable(lb=0.0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0.0, vtype=CONTINUOUS, name="y") + + problem.addConstraint(-1 * x + 2 * y <= 0, name="c1") + + problem.setObjective(-1 * x - 1 * y, sense=MINIMIZE) + + settings = SolverSettings() + + problem.solve(settings) + + assert problem.Status.name == "UnboundedOrInfeasible" + + def test_pdlp_precision_single(): file_path = ( RAPIDS_DATASET_ROOT_DIR + "/linear_programming/afiro_original.mps" diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py index 59ea62089d..dba48f010a 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py @@ -757,6 +757,7 @@ class SolutionData(StrictModel): "IterationLimit", "TimeLimit", "PrimalFeasible", + "UnboundedOrInfeasible", } ) @@ -771,6 +772,7 @@ class SolutionData(StrictModel): "Infeasible", "Unbounded", "TimeLimit", + "UnboundedOrInfeasible", } )