diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 09a138eb54..884e211548 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -129,6 +129,7 @@ #define CUOPT_METHOD_PDLP 1 #define CUOPT_METHOD_DUAL_SIMPLEX 2 #define CUOPT_METHOD_BARRIER 3 +#define CUOPT_METHOD_UNSET 4 /* @brief PDLP precision mode constants */ #define CUOPT_PDLP_DEFAULT_PRECISION -1 diff --git a/cpp/include/cuopt/linear_programming/cpu_optimization_problem_solution.hpp b/cpp/include/cuopt/linear_programming/cpu_optimization_problem_solution.hpp index ba22e81480..e86dd0341a 100644 --- a/cpp/include/cuopt/linear_programming/cpu_optimization_problem_solution.hpp +++ b/cpp/include/cuopt/linear_programming/cpu_optimization_problem_solution.hpp @@ -46,7 +46,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { l2_dual_residual_(std::numeric_limits::signaling_NaN()), gap_(std::numeric_limits::signaling_NaN()), num_iterations_(0), - solved_by_pdlp_(false) + solved_by_(Unset) { } @@ -65,7 +65,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { f_t l2_dual_residual, f_t gap, i_t num_iterations, - bool solved_by_pdlp) + method_t solved_by) : primal_solution_(std::move(primal_solution)), dual_solution_(std::move(dual_solution)), reduced_cost_(std::move(reduced_cost)), @@ -78,7 +78,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { l2_dual_residual_(l2_dual_residual), gap_(gap), num_iterations_(num_iterations), - solved_by_pdlp_(solved_by_pdlp) + solved_by_(solved_by) { } @@ -97,7 +97,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { f_t l2_dual_residual, f_t gap, i_t num_iterations, - bool solved_by_pdlp, + method_t solved_by, cpu_pdlp_warm_start_data_t&& warmstart_data) : primal_solution_(std::move(primal_solution)), dual_solution_(std::move(dual_solution)), @@ -111,7 +111,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { l2_dual_residual_(l2_dual_residual), gap_(gap), num_iterations_(num_iterations), - solved_by_pdlp_(solved_by_pdlp), + solved_by_(solved_by), pdlp_warm_start_data_(std::move(warmstart_data)) { } @@ -149,7 +149,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { i_t get_num_iterations(i_t = 0) const override { return num_iterations_; } - bool is_solved_by_pdlp(i_t = 0) const override { return solved_by_pdlp_; } + method_t solved_by(i_t = 0) const override { return solved_by_; } const pdlp_warm_start_data_t& get_pdlp_warm_start_data() const override { @@ -266,7 +266,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t { f_t l2_dual_residual_; f_t gap_; i_t num_iterations_; - bool solved_by_pdlp_; + method_t solved_by_; // PDLP warm start data (embedded struct, CPU-backed using std::vector) cpu_pdlp_warm_start_data_t pdlp_warm_start_data_; diff --git a/cpp/include/cuopt/linear_programming/optimization_problem_solution.hpp b/cpp/include/cuopt/linear_programming/optimization_problem_solution.hpp index bca0df3936..ac55256973 100644 --- a/cpp/include/cuopt/linear_programming/optimization_problem_solution.hpp +++ b/cpp/include/cuopt/linear_programming/optimization_problem_solution.hpp @@ -122,9 +122,9 @@ class gpu_lp_solution_t : public lp_solution_interface_t { return solution_.get_additional_termination_information(id).number_of_steps_taken; } - bool is_solved_by_pdlp(i_t id = 0) const override + method_t solved_by(i_t id = 0) const override { - return solution_.get_additional_termination_information(id).solved_by_pdlp; + return solution_.get_additional_termination_information(id).solved_by; } const pdlp_warm_start_data_t& get_pdlp_warm_start_data() const override @@ -338,7 +338,7 @@ class gpu_lp_solution_t : public lp_solution_interface_t { get_l2_dual_residual(), get_gap(), get_num_iterations(), - is_solved_by_pdlp(), + solved_by(), std::move(cpu_ws)); } @@ -353,7 +353,7 @@ class gpu_lp_solution_t : public lp_solution_interface_t { get_l2_dual_residual(), get_gap(), get_num_iterations(), - is_solved_by_pdlp()); + solved_by()); } /** diff --git a/cpp/include/cuopt/linear_programming/optimization_problem_solution_interface.hpp b/cpp/include/cuopt/linear_programming/optimization_problem_solution_interface.hpp index 8c1386f059..3dc1678c8b 100644 --- a/cpp/include/cuopt/linear_programming/optimization_problem_solution_interface.hpp +++ b/cpp/include/cuopt/linear_programming/optimization_problem_solution_interface.hpp @@ -291,10 +291,10 @@ class lp_solution_interface_t : public optimization_problem_solution_interface_t virtual i_t get_num_iterations(i_t id = 0) const = 0; /** - * @brief Check if solved by PDLP - * @return true if solved by PDLP + * @brief Method used for solving the LP. + * @return the method used for solving the LP. */ - virtual bool is_solved_by_pdlp(i_t id = 0) const = 0; + virtual method_t solved_by(i_t id = 0) const = 0; /** * @brief Get PDLP warm start data (GPU solutions only) diff --git a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp index 86ce4d8db5..09fc56e6b1 100644 --- a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp @@ -50,9 +50,11 @@ enum pdlp_solver_mode_t : int { * @brief Enum representing the different methods that can be used to solve the * linear programming problem. * - * Concurrent: Use both PDLP and DualSimplex in parallel. + * Concurrent: Use PDLP, Barrier and DualSimplex in parallel. * PDLP: Use the PDLP method. * DualSimplex: Use the dual simplex method. + * Barrier: Use the barrier method + * Unset: The value was not set. * * @note Default method is Concurrent. */ @@ -60,9 +62,22 @@ enum method_t : int { Concurrent = CUOPT_METHOD_CONCURRENT, PDLP = CUOPT_METHOD_PDLP, DualSimplex = CUOPT_METHOD_DUAL_SIMPLEX, - Barrier = CUOPT_METHOD_BARRIER + Barrier = CUOPT_METHOD_BARRIER, + Unset = CUOPT_METHOD_UNSET }; +/// Returns the corresponding string from the enum `method_t`. +inline std::string method_to_string(method_t method) +{ + switch (method) { + case method_t::DualSimplex: return "Dual Simplex"; + case method_t::PDLP: return "PDLP"; + case method_t::Barrier: return "Barrier"; + case method_t::Concurrent: return "Concurrent"; + default: return "Unset"; + } +} + /** * @brief Enum representing the PDLP precision modes. * diff --git a/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp b/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp index 8681690540..81e911df62 100644 --- a/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp +++ b/cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -89,8 +90,8 @@ class optimization_problem_solution_t : public base_solution_t { /** Solve time in seconds */ double solve_time{std::numeric_limits::signaling_NaN()}; - /** Whether the problem was solved by PDLP or Dual Simplex */ - bool solved_by_pdlp{false}; + /** Whether the problem was solved by PDLP, Barrier or Dual Simplex */ + method_t solved_by = method_t::Unset; }; /** diff --git a/cpp/include/cuopt/linear_programming/utilities/cython_types.hpp b/cpp/include/cuopt/linear_programming/utilities/cython_types.hpp index 29b4963663..20db133512 100644 --- a/cpp/include/cuopt/linear_programming/utilities/cython_types.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/cython_types.hpp @@ -83,7 +83,7 @@ struct linear_programming_ret_t { double gap_{}; int nb_iterations_{}; double solve_time_{}; - bool solved_by_pdlp_{}; + linear_programming::method_t solved_by_{}; bool is_gpu() const { return std::holds_alternative(solutions_); } }; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 7106d8dce3..7e8f21aec6 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -1974,18 +1974,21 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms); user_objective = root_crossover_soln_.user_objective; iter = root_crossover_soln_.iterations; - solver_name = "Barrier/PDLP and Crossover"; + solver_name = method_to_string(root_relax_solved_by); + } else { - root_status = root_status_future.get(); - user_objective = root_relax_soln_.user_objective; - iter = root_relax_soln_.iterations; - solver_name = "Dual Simplex"; + root_status = root_status_future.get(); + user_objective = root_relax_soln_.user_objective; + iter = root_relax_soln_.iterations; + root_relax_solved_by = DualSimplex; + solver_name = "Dual Simplex"; } } else { - root_status = root_status_future.get(); - user_objective = root_relax_soln_.user_objective; - iter = root_relax_soln_.iterations; - solver_name = "Dual Simplex"; + root_status = root_status_future.get(); + user_objective = root_relax_soln_.user_objective; + iter = root_relax_soln_.iterations; + root_relax_solved_by = DualSimplex; + solver_name = "Dual Simplex"; } settings_.log.printf("\n"); diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index 4faadcc6b8..0d07cf12a5 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include @@ -89,7 +91,8 @@ class branch_and_bound_t { const std::vector& reduced_costs, f_t objective, f_t user_objective, - i_t iterations) + i_t iterations, + method_t method) { if (!is_root_solution_set) { root_crossover_soln_.x = primal; @@ -99,6 +102,7 @@ class branch_and_bound_t { root_crossover_soln_.objective = objective; root_crossover_soln_.user_objective = user_objective; root_crossover_soln_.iterations = iterations; + root_relax_solved_by = method; root_crossover_solution_set_.store(true, std::memory_order_release); } } @@ -218,6 +222,7 @@ class branch_and_bound_t { f_t root_objective_; lp_solution_t root_relax_soln_; lp_solution_t root_crossover_soln_; + method_t root_relax_solved_by{Unset}; std::vector edge_norms_; std::atomic root_crossover_solution_set_{false}; omp_atomic_t root_lp_current_lower_bound_; diff --git a/cpp/src/dual_simplex/crossover.cpp b/cpp/src/dual_simplex/crossover.cpp index f55ee0837d..f83c37b922 100644 --- a/cpp/src/dual_simplex/crossover.cpp +++ b/cpp/src/dual_simplex/crossover.cpp @@ -612,7 +612,7 @@ i_t dual_push(const lp_problem_t& lp, return TIME_LIMIT_RETURN; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { - settings.log.printf("Concurrent halt\n"); + if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); } return CONCURRENT_HALT_RETURN; } } @@ -989,7 +989,7 @@ i_t primal_push(const lp_problem_t& lp, return TIME_LIMIT_RETURN; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { - settings.log.printf("Concurrent halt\n"); + if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); } return CONCURRENT_HALT_RETURN; } } @@ -1353,7 +1353,7 @@ crossover_status_t crossover(const lp_problem_t& lp, return crossover_status_t::TIME_LIMIT; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { - settings.log.printf("Concurrent halt\n"); + if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); } return crossover_status_t::CONCURRENT_LIMIT; } @@ -1415,7 +1415,7 @@ crossover_status_t crossover(const lp_problem_t& lp, return crossover_status_t::TIME_LIMIT; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { - settings.log.printf("Concurrent halt\n"); + if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); } return crossover_status_t::CONCURRENT_LIMIT; } primal_infeas = primal_infeasibility(lp, settings, vstatus, solution.x); @@ -1577,7 +1577,7 @@ crossover_status_t crossover(const lp_problem_t& lp, return crossover_status_t::TIME_LIMIT; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { - settings.log.printf("Concurrent halt\n"); + if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); } return crossover_status_t::CONCURRENT_LIMIT; } solution.iterations += iter; diff --git a/cpp/src/dual_simplex/right_looking_lu.cpp b/cpp/src/dual_simplex/right_looking_lu.cpp index 37202000f8..5cb0185c8c 100644 --- a/cpp/src/dual_simplex/right_looking_lu.cpp +++ b/cpp/src/dual_simplex/right_looking_lu.cpp @@ -1258,7 +1258,7 @@ i_t right_looking_lu_row_permutation_only(const csc_matrix_t& A, } if (toc(start_time) > settings.time_limit) { return TIME_LIMIT_RETURN; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { - settings.log.printf("Concurrent halt\n"); + if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); } return CONCURRENT_HALT_RETURN; } } diff --git a/cpp/src/grpc/cuopt_remote.proto b/cpp/src/grpc/cuopt_remote.proto index d2617e0ef8..f638100c13 100644 --- a/cpp/src/grpc/cuopt_remote.proto +++ b/cpp/src/grpc/cuopt_remote.proto @@ -202,7 +202,7 @@ message LPSolution { double gap = 24; int32 nb_iterations = 25; double solve_time = 26; - bool solved_by_pdlp = 27; + int32 solved_by = 27; } enum PDLPTerminationStatus { diff --git a/cpp/src/grpc/cuopt_remote_service.proto b/cpp/src/grpc/cuopt_remote_service.proto index 86777baba6..24c7517781 100644 --- a/cpp/src/grpc/cuopt_remote_service.proto +++ b/cpp/src/grpc/cuopt_remote_service.proto @@ -205,7 +205,7 @@ message ChunkedResultHeader { double gap = 16; int32 nb_iterations = 17; double solve_time = 18; - bool solved_by_pdlp = 19; + int32 solved_by = 19; // MIP result scalars MIPTerminationStatus mip_termination_status = 30; diff --git a/cpp/src/grpc/grpc_solution_mapper.cpp b/cpp/src/grpc/grpc_solution_mapper.cpp index 700fd12c98..096b466804 100644 --- a/cpp/src/grpc/grpc_solution_mapper.cpp +++ b/cpp/src/grpc/grpc_solution_mapper.cpp @@ -110,7 +110,7 @@ void map_lp_solution_to_proto(const cpu_lp_solution_t& solution, pb_solution->set_gap(solution.get_gap()); pb_solution->set_nb_iterations(solution.get_num_iterations()); pb_solution->set_solve_time(solution.get_solve_time()); - pb_solution->set_solved_by_pdlp(solution.is_solved_by_pdlp()); + pb_solution->set_solved_by(static_cast(solution.solved_by())); if (solution.has_warm_start_data()) { auto* pb_ws = pb_solution->mutable_warm_start_data(); @@ -157,15 +157,15 @@ cpu_lp_solution_t map_proto_to_lp_solution(const cuopt::remote::LPSolu std::vector reduced_cost(pb_solution.reduced_cost().begin(), pb_solution.reduced_cost().end()); - auto status = from_proto_pdlp_status(pb_solution.termination_status()); - auto obj = static_cast(pb_solution.primal_objective()); - auto dual_obj = static_cast(pb_solution.dual_objective()); - auto solve_t = pb_solution.solve_time(); - auto l2_pr = static_cast(pb_solution.l2_primal_residual()); - auto l2_dr = static_cast(pb_solution.l2_dual_residual()); - auto g = static_cast(pb_solution.gap()); - auto iters = static_cast(pb_solution.nb_iterations()); - auto by_pdlp = pb_solution.solved_by_pdlp(); + auto status = from_proto_pdlp_status(pb_solution.termination_status()); + auto obj = static_cast(pb_solution.primal_objective()); + auto dual_obj = static_cast(pb_solution.dual_objective()); + auto solve_t = pb_solution.solve_time(); + auto l2_pr = static_cast(pb_solution.l2_primal_residual()); + auto l2_dr = static_cast(pb_solution.l2_dual_residual()); + auto g = static_cast(pb_solution.gap()); + auto iters = static_cast(pb_solution.nb_iterations()); + auto solved_by = static_cast(pb_solution.solved_by()); if (pb_solution.has_warm_start_data()) { const auto& pb_ws = pb_solution.warm_start_data(); @@ -211,7 +211,7 @@ cpu_lp_solution_t map_proto_to_lp_solution(const cuopt::remote::LPSolu l2_dr, g, iters, - by_pdlp, + solved_by, std::move(ws)); } @@ -226,7 +226,7 @@ cpu_lp_solution_t map_proto_to_lp_solution(const cuopt::remote::LPSolu l2_dr, g, iters, - by_pdlp); + solved_by); } template @@ -354,7 +354,7 @@ void populate_chunked_result_header_lp(const cpu_lp_solution_t& soluti header->set_gap(solution.get_gap()); header->set_nb_iterations(solution.get_num_iterations()); header->set_solve_time(solution.get_solve_time()); - header->set_solved_by_pdlp(solution.is_solved_by_pdlp()); + header->set_solved_by(static_cast(solution.solved_by())); const auto& primal = solution.get_primal_solution_host(); const auto& dual = solution.get_dual_solution_host(); @@ -551,15 +551,15 @@ cpu_lp_solution_t chunked_result_to_lp_solution( auto dual = bytes_to_typed(arrays, cuopt::remote::RESULT_DUAL_SOLUTION); auto reduced_cost = bytes_to_typed(arrays, cuopt::remote::RESULT_REDUCED_COST); - auto status = from_proto_pdlp_status(h.lp_termination_status()); - auto obj = static_cast(h.primal_objective()); - auto dual_obj = static_cast(h.dual_objective()); - auto solve_t = h.solve_time(); - auto l2_pr = static_cast(h.l2_primal_residual()); - auto l2_dr = static_cast(h.l2_dual_residual()); - auto g = static_cast(h.gap()); - auto iters = static_cast(h.nb_iterations()); - auto by_pdlp = h.solved_by_pdlp(); + auto status = from_proto_pdlp_status(h.lp_termination_status()); + auto obj = static_cast(h.primal_objective()); + auto dual_obj = static_cast(h.dual_objective()); + auto solve_t = h.solve_time(); + auto l2_pr = static_cast(h.l2_primal_residual()); + auto l2_dr = static_cast(h.l2_dual_residual()); + auto g = static_cast(h.gap()); + auto iters = static_cast(h.nb_iterations()); + auto solved_by = static_cast(h.solved_by()); auto ws_primal = bytes_to_typed(arrays, cuopt::remote::RESULT_WS_CURRENT_PRIMAL); if (!ws_primal.empty()) { @@ -598,7 +598,7 @@ cpu_lp_solution_t chunked_result_to_lp_solution( l2_dr, g, iters, - by_pdlp, + solved_by, std::move(ws)); } @@ -613,7 +613,7 @@ cpu_lp_solution_t chunked_result_to_lp_solution( l2_dr, g, iters, - by_pdlp); + solved_by); } template diff --git a/cpp/src/grpc/server/grpc_worker.cpp b/cpp/src/grpc/server/grpc_worker.cpp index 943212b80d..99b0e35b5e 100644 --- a/cpp/src/grpc/server/grpc_worker.cpp +++ b/cpp/src/grpc/server/grpc_worker.cpp @@ -378,7 +378,7 @@ static SolveResult run_lp_solve(DeserializedJob& dj, term_info.l2_dual_residual, term_info.gap, term_info.number_of_steps_taken, - term_info.solved_by_pdlp, + term_info.solved_by, std::move(cpu_ws)); populate_chunked_result_header_lp(cpu_solution, &sr.header); diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 0ded8337d8..55be779331 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -516,9 +516,10 @@ solution_t diversity_manager_t::run_solver() auto user_obj = lp_result.get_objective_value(); auto solver_obj = problem_ptr->get_solver_obj_from_user_obj(user_obj); auto iterations = lp_result.get_additional_termination_information().number_of_steps_taken; + auto method = lp_result.get_additional_termination_information().solved_by; // Set for the B&B (param4 expects solver space, param5 expects user space) problem_ptr->set_root_relaxation_solution_callback( - host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations); + host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations, method); } // in case the pdlp returned var boudns that are out of bounds diff --git a/cpp/src/mip_heuristics/problem/problem.cuh b/cpp/src/mip_heuristics/problem/problem.cuh index 4c8bf3caad..d18e49d906 100644 --- a/cpp/src/mip_heuristics/problem/problem.cuh +++ b/cpp/src/mip_heuristics/problem/problem.cuh @@ -18,6 +18,7 @@ #include #include +#include #include #include "host_helper.cuh" #include "problem_fixing.cuh" @@ -249,8 +250,13 @@ class problem_t { rmm::device_uvector integer_fixed_variable_map; std::function&)> branch_and_bound_callback; - std::function&, const std::vector&, const std::vector&, f_t, f_t, i_t)> + std::function&, + const std::vector&, + const std::vector&, + f_t, + f_t, + i_t, + method_t)> set_root_relaxation_solution_callback; typename mip_solver_settings_t::tolerances_t tolerances{}; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 5d2f043ee4..4eb3adbae5 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -441,7 +441,8 @@ solution_t mip_solver_t::run_solver() std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, - std::placeholders::_6); + std::placeholders::_6, + std::placeholders::_7); if (timer_.check_time_limit()) { CUOPT_LOG_INFO("Time limit reached during B&B setup"); diff --git a/cpp/src/pdlp/pdlp.cu b/cpp/src/pdlp/pdlp.cu index 82e79098a7..d183fc7007 100644 --- a/cpp/src/pdlp/pdlp.cu +++ b/cpp/src/pdlp/pdlp.cu @@ -820,10 +820,13 @@ pdlp_solver_t::check_batch_termination(const timer_t& timer) batch_solution_to_return_ .get_additional_termination_informations()[climber_strategies_[i].original_index] .total_number_of_attempted_steps = pdhg_solver_.get_total_pdhg_iterations(); - batch_solution_to_return_ - .get_additional_termination_informations()[climber_strategies_[i].original_index] - .solved_by_pdlp = (current_termination_strategy_.get_termination_status(i) != - pdlp_termination_status_t::ConcurrentLimit); + + if (current_termination_strategy_.get_termination_status(i) != + pdlp_termination_status_t::ConcurrentLimit) { + batch_solution_to_return_ + .get_additional_termination_informations()[climber_strategies_[i].original_index] + .solved_by = method_t::PDLP; + } } current_termination_strategy_.fill_gpu_terms_stats(total_pdlp_iterations_); RAFT_CUDA_TRY(cudaStreamSynchronize(stream_view_)); @@ -888,10 +891,13 @@ pdlp_solver_t::check_batch_termination(const timer_t& timer) batch_solution_to_return_ .get_additional_termination_informations()[climber_strategies_[i].original_index] .total_number_of_attempted_steps = pdhg_solver_.get_total_pdhg_iterations(); - batch_solution_to_return_ - .get_additional_termination_informations()[climber_strategies_[i].original_index] - .solved_by_pdlp = (current_termination_strategy_.get_termination_status(i) != - pdlp_termination_status_t::ConcurrentLimit); + + if (current_termination_strategy_.get_termination_status(i) != + pdlp_termination_status_t::ConcurrentLimit) { + batch_solution_to_return_ + .get_additional_termination_informations()[climber_strategies_[i].original_index] + .solved_by = method_t::PDLP; + } } } if (to_remove.size() > 0) { diff --git a/cpp/src/pdlp/solution_conversion.cu b/cpp/src/pdlp/solution_conversion.cu index 7993445a08..8ec3c20b27 100644 --- a/cpp/src/pdlp/solution_conversion.cu +++ b/cpp/src/pdlp/solution_conversion.cu @@ -95,7 +95,7 @@ cuopt::cython::linear_programming_ret_t gpu_lp_solution_t::to_linear_p ret.gap_ = term_info.gap; ret.nb_iterations_ = term_info.number_of_steps_taken; ret.solve_time_ = term_info.solve_time; - ret.solved_by_pdlp_ = term_info.solved_by_pdlp; + ret.solved_by_ = term_info.solved_by; } return ret; @@ -181,7 +181,7 @@ cpu_lp_solution_t::to_cpu_linear_programming_ret_t() ret.gap_ = gap_; ret.nb_iterations_ = num_iterations_; ret.solve_time_ = solve_time_; - ret.solved_by_pdlp_ = solved_by_pdlp_; + ret.solved_by_ = solved_by_; return ret; } diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 6e23c81a34..b68896ff61 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -351,7 +351,7 @@ optimization_problem_solution_t convert_dual_simplex_sol( f_t duration, f_t norm_user_objective, f_t norm_rhs, - i_t method) + method_t method) { auto to_termination_status = [](dual_simplex::lp_status_t status) { switch (status) { @@ -389,7 +389,7 @@ optimization_problem_solution_t convert_dual_simplex_sol( std::vector< typename optimization_problem_solution_t::additional_termination_information_t> info(1); - info[0].solved_by_pdlp = false; + info[0].solved_by = method; info[0].primal_objective = solution.user_objective; info[0].dual_objective = solution.user_objective; info[0].gap = 0.0; @@ -420,7 +420,7 @@ optimization_problem_solution_t convert_dual_simplex_sol( termination_status != pdlp_termination_status_t::TimeLimit && termination_status != pdlp_termination_status_t::ConcurrentLimit) { CUOPT_LOG_INFO("%s Solve status %s", - method == 0 ? "Dual Simplex" : "Barrier", + method == method_t::DualSimplex ? "Dual Simplex" : "Barrier", sol.get_termination_status_string().c_str()); } @@ -494,7 +494,7 @@ optimization_problem_solution_t run_barrier( std::get<2>(sol_dual_simplex), std::get<3>(sol_dual_simplex), std::get<4>(sol_dual_simplex), - 1); + method_t::Barrier); } template @@ -568,7 +568,7 @@ optimization_problem_solution_t run_dual_simplex( std::get<2>(sol_dual_simplex), std::get<3>(sol_dual_simplex), std::get<4>(sol_dual_simplex), - 0); + method_t::DualSimplex); } #if PDLP_INSTANTIATE_FLOAT || CUOPT_INSTANTIATE_FLOAT @@ -670,7 +670,7 @@ static optimization_problem_solution_t run_pdlp_solver_in_fp32( di.max_dual_ray_infeasibility = static_cast(fi.max_dual_ray_infeasibility); di.dual_ray_linear_objective = static_cast(fi.dual_ray_linear_objective); di.solve_time = fi.solve_time; - di.solved_by_pdlp = fi.solved_by_pdlp; + di.solved_by = fi.solved_by; term_infos.push_back(di); } @@ -1203,7 +1203,7 @@ optimization_problem_solution_t run_concurrent( std::get<2>(*sol_dual_simplex_ptr), std::get<3>(*sol_dual_simplex_ptr), std::get<4>(*sol_dual_simplex_ptr), - 0) + method_t::DualSimplex) : optimization_problem_solution_t{pdlp_termination_status_t::ConcurrentLimit, problem.handle_ptr->get_stream()}; @@ -1214,7 +1214,7 @@ optimization_problem_solution_t run_concurrent( std::get<2>(*sol_barrier_ptr), std::get<3>(*sol_barrier_ptr), std::get<4>(*sol_barrier_ptr), - 1); + method_t::Barrier); f_t end_time = timer.elapsed_time(); CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Concurrent time: %.3fs", end_time); diff --git a/cpp/src/pdlp/solver_solution.cu b/cpp/src/pdlp/solver_solution.cu index ba68d20740..ec0492dac3 100644 --- a/cpp/src/pdlp/solver_solution.cu +++ b/cpp/src/pdlp/solver_solution.cu @@ -168,12 +168,12 @@ void optimization_problem_solution_t::write_additional_termination_sta myfile << "\t\"Additional termination information\" : { " << std::endl; myfile << "\t\"Number of steps taken\" : " << termination_stats.number_of_steps_taken << "," << std::endl; - if (termination_stats.solved_by_pdlp) { + if (termination_stats.solved_by == method_t::PDLP) { myfile << "\t\"Total number of attempted steps\" : " << termination_stats.total_number_of_attempted_steps << "," << std::endl; } myfile << "\t\"Total solve time\" : " << termination_stats.solve_time; - if (termination_stats.solved_by_pdlp) { + if (termination_stats.solved_by == method_t::PDLP) { myfile << "," << std::endl; myfile << "\t\t\"Convergence measures\" : { " << std::endl; myfile << "\t\t\t\"Absolute primal residual\" : " << termination_stats.l2_primal_residual << "," diff --git a/cpp/src/pdlp/termination_strategy/termination_strategy.cu b/cpp/src/pdlp/termination_strategy/termination_strategy.cu index 7179df6a49..0db3d5e5e0 100644 --- a/cpp/src/pdlp/termination_strategy/termination_strategy.cu +++ b/cpp/src/pdlp/termination_strategy/termination_strategy.cu @@ -596,8 +596,10 @@ pdlp_termination_strategy_t::fill_return_problem_solution( &infeasibility_information_view.dual_ray_linear_objective[i], 1, stream_view_); - term_stats_vector[i].solved_by_pdlp = - (termination_status[i] != pdlp_termination_status_t::ConcurrentLimit); + + if (termination_status[i] != pdlp_termination_status_t::ConcurrentLimit) { + term_stats_vector[i].solved_by = method_t::PDLP; + } } RAFT_CUDA_TRY(cudaStreamSynchronize(stream_view_)); 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 689c8ed742..639aa8c379 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 @@ -18,7 +18,6 @@ #error "This file must be compiled as C code" #endif - int check_problem(cuOptOptimizationProblem problem, cuopt_int_t num_constraints, cuopt_int_t num_variables, @@ -58,27 +57,24 @@ const char* termination_status_to_string(cuopt_int_t termination_status) return "Unknown"; } +int test_int_size() { return cuOptGetIntSize(); } -int test_int_size() { - return cuOptGetIntSize(); -} - -int test_float_size() { - return cuOptGetFloatSize(); -} +int test_float_size() { return cuOptGetFloatSize(); } -cuopt_int_t test_missing_file() { +cuopt_int_t test_missing_file() +{ cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; - cuopt_int_t status = cuOptReadProblem("missing_file.mps", &problem); + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t status = cuOptReadProblem("missing_file.mps", &problem); cuOptDestroyProblem(&problem); cuOptDestroySolverSettings(&settings); cuOptDestroySolution(&solution); return status; } -cuopt_int_t test_bad_parameter_name() { +cuopt_int_t test_bad_parameter_name() +{ cuOptSolverSettings settings = NULL; cuopt_int_t status; cuopt_int_t value; @@ -152,16 +148,13 @@ static void mip_get_solution_callback(const cuopt_float_t* solution, if (context == NULL) { return; } context->get_calls += 1; if (context->last_solution == NULL) { - context->last_solution = - (cuopt_float_t*)malloc(context->n_variables * sizeof(cuopt_float_t)); + context->last_solution = (cuopt_float_t*)malloc(context->n_variables * sizeof(cuopt_float_t)); if (context->last_solution == NULL) { context->error = 1; return; } } - memcpy(context->last_solution, - solution, - context->n_variables * sizeof(cuopt_float_t)); + memcpy(context->last_solution, solution, context->n_variables * sizeof(cuopt_float_t)); memcpy(&context->last_objective, objective_value, sizeof(cuopt_float_t)); memcpy(&context->last_solution_bound, solution_bound, sizeof(cuopt_float_t)); } @@ -176,18 +169,16 @@ static void mip_set_solution_callback(cuopt_float_t* solution, context->set_calls += 1; memcpy(&context->last_solution_bound, solution_bound, sizeof(cuopt_float_t)); if (context->last_solution == NULL) { return; } - memcpy(solution, - context->last_solution, - context->n_variables * sizeof(cuopt_float_t)); + memcpy(solution, context->last_solution, context->n_variables * sizeof(cuopt_float_t)); memcpy(objective_value, &context->last_objective, sizeof(cuopt_float_t)); } static cuopt_int_t test_mip_callbacks_internal(int include_set_callback) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; - mip_callback_context_t context = {0}; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + mip_callback_context_t context = {0}; #define NUM_ITEMS 8 #define NUM_CONSTRAINTS 1 @@ -202,7 +193,7 @@ static cuopt_int_t test_mip_callbacks_internal(int include_set_callback) cuopt_int_t row_offsets[] = {0, NUM_ITEMS}; cuopt_int_t column_indices[NUM_ITEMS]; - cuopt_float_t rhs[] = {max_weight}; + cuopt_float_t rhs[] = {max_weight}; char constraint_sense[] = {CUOPT_LESS_THAN}; cuopt_float_t lower_bounds[NUM_ITEMS]; cuopt_float_t upper_bounds[NUM_ITEMS]; @@ -296,15 +287,9 @@ static cuopt_int_t test_mip_callbacks_internal(int include_set_callback) return status; } -cuopt_int_t test_mip_get_callbacks_only() -{ - return test_mip_callbacks_internal(0); -} +cuopt_int_t test_mip_get_callbacks_only() { return test_mip_callbacks_internal(0); } -cuopt_int_t test_mip_get_set_callbacks() -{ - return test_mip_callbacks_internal(1); -} +cuopt_int_t test_mip_get_set_callbacks() { return test_mip_callbacks_internal(1); } cuopt_int_t burglar_problem() { @@ -332,7 +317,7 @@ cuopt_int_t burglar_problem() cuopt_int_t row_offsets[] = {0, NUM_ITEMS}; cuopt_int_t column_indices[NUM_ITEMS]; - cuopt_float_t rhs[] = {max_weight}; + cuopt_float_t rhs[] = {max_weight}; char constraint_sense[] = {CUOPT_LESS_THAN}; cuopt_float_t lower_bounds[NUM_ITEMS]; cuopt_float_t upper_bounds[NUM_ITEMS]; @@ -453,11 +438,16 @@ cuopt_int_t burglar_problem() return status; } -int solve_mps_file(const char* filename, double time_limit, double iteration_limit, int* termination_status_ptr, double* solve_time_ptr, int method) +int solve_mps_file(const char* filename, + double time_limit, + double iteration_limit, + int* termination_status_ptr, + double* solve_time_ptr, + int method) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; cuopt_int_t status; cuopt_int_t is_mip; cuopt_int_t termination_status = -1; @@ -500,9 +490,10 @@ int solve_mps_file(const char* filename, double time_limit, double iteration_lim } status = cuOptSolve(problem, settings, &solution); if (status != CUOPT_SUCCESS) { - #define ERROR_BUFFER_SIZE 1024 +#define ERROR_BUFFER_SIZE 1024 char error_string[ERROR_BUFFER_SIZE]; - cuopt_int_t error_string_status = cuOptGetErrorString(solution, error_string, ERROR_BUFFER_SIZE); + cuopt_int_t error_string_status = + cuOptGetErrorString(solution, error_string, ERROR_BUFFER_SIZE); if (error_string_status != CUOPT_SUCCESS) { printf("Error getting error string\n"); goto DONE; @@ -572,14 +563,14 @@ int check_problem(cuOptOptimizationProblem problem, char* check_variable_types; cuopt_int_t status; check_objective_coefficients = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); - check_row_offsets = (cuopt_int_t*)malloc((num_constraints + 1) * sizeof(cuopt_int_t)); - check_column_indices = (cuopt_int_t*)malloc(nnz * sizeof(cuopt_int_t)); - check_values = (cuopt_float_t*)malloc(nnz * sizeof(cuopt_float_t)); - check_constraint_sense = (char*)malloc(num_constraints * sizeof(char)); - check_rhs = (cuopt_float_t*)malloc(num_constraints * sizeof(cuopt_float_t)); - check_var_lower_bounds = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); - check_var_upper_bounds = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); - check_variable_types = (char*)malloc(num_variables * sizeof(char)); + check_row_offsets = (cuopt_int_t*)malloc((num_constraints + 1) * sizeof(cuopt_int_t)); + check_column_indices = (cuopt_int_t*)malloc(nnz * sizeof(cuopt_int_t)); + check_values = (cuopt_float_t*)malloc(nnz * sizeof(cuopt_float_t)); + check_constraint_sense = (char*)malloc(num_constraints * sizeof(char)); + check_rhs = (cuopt_float_t*)malloc(num_constraints * sizeof(cuopt_float_t)); + check_var_lower_bounds = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + check_var_upper_bounds = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); + check_variable_types = (char*)malloc(num_variables * sizeof(char)); status = cuOptGetNumConstraints(problem, &check_num_constraints); if (status != CUOPT_SUCCESS) { @@ -637,7 +628,9 @@ int check_problem(cuOptOptimizationProblem problem, goto DONE; } if (check_objective_offset != objective_offset) { - printf("Error: expected objective offset to be %f, but got %f\n", objective_offset, check_objective_offset); + printf("Error: expected objective offset to be %f, but got %f\n", + objective_offset, + check_objective_offset); status = -1; goto DONE; } @@ -791,9 +784,8 @@ int check_problem(cuOptOptimizationProblem problem, cuopt_int_t test_infeasible_problem() { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; - + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; /* Solve the following problem minimize 0 @@ -812,21 +804,28 @@ cuopt_int_t test_infeasible_problem() 0 1 2 3 */ - cuopt_int_t num_variables = 4; + cuopt_int_t num_variables = 4; cuopt_int_t num_constraints = 9; - cuopt_int_t nnz = 17; - cuopt_int_t row_offsets[] = {0, 2, 4, 6, 7, 9, 10, 12, 15, 17}; + cuopt_int_t nnz = 17; + cuopt_int_t row_offsets[] = {0, 2, 4, 6, 7, 9, 10, 12, 15, 17}; // clang-format off // row1, row2, row3, row4, row5,row6, row7, row8, row9 cuopt_int_t column_indices[] = {0, 1, 0, 1, 0, 1, 3, 2, 3, 2, 0, 3, 0, 1, 2, 1, 2}; cuopt_float_t values[] = {-0.5, 1.0, 2.0, -1.0, 3.0, 1.0, 1.0, 3.0, -1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 1.0}; // clang-format on - cuopt_float_t rhs[] = {0.5, 3.0, 6.0, 2.0, 2.0, 5.0, 10.0, 14.0, 1.0}; - char constraint_sense[] = {CUOPT_GREATER_THAN, CUOPT_GREATER_THAN, - CUOPT_LESS_THAN, CUOPT_LESS_THAN, CUOPT_LESS_THAN, - CUOPT_GREATER_THAN, CUOPT_LESS_THAN, CUOPT_LESS_THAN, CUOPT_GREATER_THAN}; + cuopt_float_t rhs[] = {0.5, 3.0, 6.0, 2.0, 2.0, 5.0, 10.0, 14.0, 1.0}; + char constraint_sense[] = {CUOPT_GREATER_THAN, + CUOPT_GREATER_THAN, + CUOPT_LESS_THAN, + CUOPT_LESS_THAN, + CUOPT_LESS_THAN, + CUOPT_GREATER_THAN, + CUOPT_LESS_THAN, + CUOPT_LESS_THAN, + CUOPT_GREATER_THAN}; cuopt_float_t var_lower_bounds[] = {0.0, 0.0, 0.0, 0.0}; - cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; + cuopt_float_t var_upper_bounds[] = { + CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; cuopt_float_t objective_coefficients[] = {0.0, 0.0, 0.0, 0.0}; @@ -835,19 +834,19 @@ cuopt_int_t test_infeasible_problem() cuopt_float_t objective_value; cuopt_int_t status = cuOptCreateProblem(num_constraints, - num_variables, - CUOPT_MINIMIZE, - 0.0, - objective_coefficients, - row_offsets, - column_indices, - values, - constraint_sense, - rhs, - var_lower_bounds, - var_upper_bounds, - variable_types, - &problem); + num_variables, + CUOPT_MINIMIZE, + 0.0, + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_sense, + rhs, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); if (status != CUOPT_SUCCESS) { printf("Error creating problem\n"); goto DONE; @@ -923,12 +922,11 @@ cuopt_int_t test_infeasible_problem() return status; } - -cuopt_int_t test_ranged_problem(cuopt_int_t *termination_status_ptr, cuopt_float_t *objective_ptr) +cuopt_int_t test_ranged_problem(cuopt_int_t* termination_status_ptr, cuopt_float_t* objective_ptr) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; // maximize obj: 5 * x + 8 * y; // subject to c1: 2*x + 3*y <= 12; @@ -937,22 +935,22 @@ cuopt_int_t test_ranged_problem(cuopt_int_t *termination_status_ptr, cuopt_float // subject to x_limit: 0 <= x <= 10; // subject to y_limit: 0 <= y <= 10; - cuopt_int_t num_variables = 2; - cuopt_int_t num_constraints = 3; - cuopt_int_t nnz = 6; - cuopt_int_t objective_sense = CUOPT_MAXIMIZE; - cuopt_float_t objective_offset = 0.0; - cuopt_float_t objective_coefficients[] = {5.0, 8.0}; - cuopt_int_t row_offsets[] = {0, 2, 4, 6}; - cuopt_int_t column_indices[] = {0, 1, 0, 1, 0, 1}; - cuopt_float_t values[] = {2.0, 3.0, 3.0, 1.0, 1.0, 2.0}; - cuopt_float_t constraint_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY, 2.0}; - cuopt_float_t constraint_upper_bounds[] = {12.0, 6.0, 8.0}; + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 3; + cuopt_int_t nnz = 6; + cuopt_int_t objective_sense = CUOPT_MAXIMIZE; + cuopt_float_t objective_offset = 0.0; + cuopt_float_t objective_coefficients[] = {5.0, 8.0}; + cuopt_int_t row_offsets[] = {0, 2, 4, 6}; + cuopt_int_t column_indices[] = {0, 1, 0, 1, 0, 1}; + cuopt_float_t values[] = {2.0, 3.0, 3.0, 1.0, 1.0, 2.0}; + cuopt_float_t constraint_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY, 2.0}; + cuopt_float_t constraint_upper_bounds[] = {12.0, 6.0, 8.0}; cuopt_float_t constraint_lower_bounds_check[] = {1.0, 1.0, 1.0}; cuopt_float_t constraint_upper_bounds_check[] = {1.0, 1.0, 1.0}; - cuopt_float_t variable_lower_bounds[] = {0.0, 0.0}; - cuopt_float_t variable_upper_bounds[] = {10.0, 10.0}; - char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + cuopt_float_t variable_lower_bounds[] = {0.0, 0.0}; + cuopt_float_t variable_upper_bounds[] = {10.0, 10.0}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; cuopt_int_t status; status = cuOptCreateRangedProblem(num_constraints, @@ -989,13 +987,17 @@ cuopt_int_t test_ranged_problem(cuopt_int_t *termination_status_ptr, cuopt_float for (cuopt_int_t i = 0; i < num_constraints; i++) { if (constraint_lower_bounds_check[i] != constraint_lower_bounds[i]) { printf("Error: expected constraint lower bound %d to be %f, but got %f\n", - i, constraint_lower_bounds[i], constraint_lower_bounds_check[i]); + i, + constraint_lower_bounds[i], + constraint_lower_bounds_check[i]); status = -1; goto DONE; } if (constraint_upper_bounds_check[i] != constraint_upper_bounds[i]) { printf("Error: expected constraint upper bound %d to be %f, but got %f\n", - i, constraint_upper_bounds[i], constraint_upper_bounds_check[i]); + i, + constraint_upper_bounds[i], + constraint_upper_bounds_check[i]); status = -1; goto DONE; } @@ -1043,8 +1045,8 @@ cuopt_int_t test_ranged_problem(cuopt_int_t *termination_status_ptr, cuopt_float cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; /* Test the invalid bounds scenario: maximize 2*x @@ -1059,17 +1061,17 @@ cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) - Result: 1.0 <= x <= 0.0 (INVALID!) */ - cuopt_int_t num_variables = 1; + cuopt_int_t num_variables = 1; cuopt_int_t num_constraints = 2; - cuopt_int_t nnz = 2; + cuopt_int_t nnz = 2; // CSR format constraint matrix // From the constraints: // x >= 0.2 // x <= 0.5 - cuopt_int_t row_offsets[] = {0, 1, 2}; + cuopt_int_t row_offsets[] = {0, 1, 2}; cuopt_int_t column_indices[] = {0, 0}; - cuopt_float_t values[] = {1.0, 1.0}; + cuopt_float_t values[] = {1.0, 1.0}; // Objective coefficients // From the objective function: maximize 2*x @@ -1101,19 +1103,19 @@ cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) // Create the problem status = cuOptCreateRangedProblem(num_constraints, - num_variables, - CUOPT_MAXIMIZE, // maximize - 0.0, // objective offset - objective_coefficients, - row_offsets, - column_indices, - values, - constraint_lower_bounds, - constraint_upper_bounds, - var_lower_bounds, - var_upper_bounds, - variable_types, - &problem); + num_variables, + CUOPT_MAXIMIZE, // maximize + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_lower_bounds, + constraint_upper_bounds, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); printf("cuOptCreateRangedProblem returned: %d\n", status); @@ -1157,8 +1159,7 @@ cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) termination_status); status = CUOPT_VALIDATION_ERROR; goto DONE; - } - else { + } else { printf("✓ Problem found infeasible as expected\n"); status = CUOPT_SUCCESS; goto DONE; @@ -1173,13 +1174,15 @@ cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) // Print results printf("\nResults:\n"); printf("--------\n"); - printf("Termination status: %s (%d)\n", termination_status_to_string(termination_status), termination_status); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); printf("Solve time: %f seconds\n", time); printf("Objective value: %f\n", objective_value); // Get and print solution variables cuopt_float_t* solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); - status = cuOptGetPrimalSolution(solution, solution_values); + status = cuOptGetPrimalSolution(solution, solution_values); if (status != CUOPT_SUCCESS) { printf("Error getting solution values: %d\n", status); free(solution_values); @@ -1200,11 +1203,12 @@ cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip) return status; } -cuopt_int_t test_quadratic_problem(cuopt_int_t* termination_status_ptr, cuopt_float_t* objective_ptr) +cuopt_int_t test_quadratic_problem(cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; // minimize x1^2 + 4*x2^2 - 8*x1 - 16*x2 // subject to x1 + x2 >= 5 @@ -1213,22 +1217,22 @@ cuopt_int_t test_quadratic_problem(cuopt_int_t* termination_status_ptr, cuopt_fl // x1 <= 10 // x2 <= 10 - cuopt_int_t num_variables = 2; - cuopt_int_t num_constraints = 1; - cuopt_int_t objective_sense = CUOPT_MINIMIZE; - cuopt_float_t objective_offset = 0.0; + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 1; + cuopt_int_t objective_sense = CUOPT_MINIMIZE; + cuopt_float_t objective_offset = 0.0; cuopt_float_t objective_coefficients[] = {-8.0, -16.0}; - cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; - cuopt_int_t quadratic_objective_matrix_column_indices[] = {0, 1}; + cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; + cuopt_int_t quadratic_objective_matrix_column_indices[] = {0, 1}; cuopt_float_t quadratic_objective_matrix_coefficent_values[] = {1.0, 4.0}; - cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t row_offsets[] = {0, 2}; cuopt_int_t column_indices[] = {0, 1}; - cuopt_float_t values[] = {1.0, 1.0}; + cuopt_float_t values[] = {1.0, 1.0}; cuopt_float_t constraint_bounds[] = {5.0}; - char constraint_sense[] = {'G'}; + char constraint_sense[] = {'G'}; cuopt_float_t var_lower_bounds[] = {3.0, 0.0}; cuopt_float_t var_upper_bounds[] = {10.0, 10.0}; @@ -1281,20 +1285,20 @@ cuopt_int_t test_quadratic_problem(cuopt_int_t* termination_status_ptr, cuopt_fl goto DONE; } - DONE: -cuOptDestroyProblem(&problem); -cuOptDestroySolverSettings(&settings); -cuOptDestroySolution(&solution); + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); -return status; + return status; } -cuopt_int_t test_quadratic_ranged_problem(cuopt_int_t* termination_status_ptr, cuopt_float_t* objective_ptr) +cuopt_int_t test_quadratic_ranged_problem(cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; // minimize x1^2 + 4*x2^2 - 8*x1 - 16*x2 // subject to x1 + x2 >= 5 @@ -1302,18 +1306,18 @@ cuopt_int_t test_quadratic_ranged_problem(cuopt_int_t* termination_status_ptr, c // x2 >= 0 // x1 <= 10 // x2 <= 10 - cuopt_int_t num_variables = 2; - cuopt_int_t num_constraints = 1; - cuopt_int_t objective_sense = CUOPT_MINIMIZE; - cuopt_float_t objective_offset = 0.0; - cuopt_float_t objective_coefficients[] = {-8.0, -16.0}; - cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; - cuopt_int_t quadratic_objective_matrix_column_indices[] = {0, 1}; + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 1; + cuopt_int_t objective_sense = CUOPT_MINIMIZE; + cuopt_float_t objective_offset = 0.0; + cuopt_float_t objective_coefficients[] = {-8.0, -16.0}; + cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; + cuopt_int_t quadratic_objective_matrix_column_indices[] = {0, 1}; cuopt_float_t quadratic_objective_matrix_coefficent_values[] = {1.0, 4.0}; - cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t row_offsets[] = {0, 2}; cuopt_int_t column_indices[] = {0, 1}; - cuopt_float_t values[] = {1.0, 1.0}; + cuopt_float_t values[] = {1.0, 1.0}; cuopt_float_t constraint_lower_bounds[] = {5.0}; cuopt_float_t constraint_upper_bounds[] = {100.0}; @@ -1324,21 +1328,21 @@ cuopt_int_t test_quadratic_ranged_problem(cuopt_int_t* termination_status_ptr, c cuopt_int_t status; status = cuOptCreateQuadraticRangedProblem(num_constraints, - num_variables, - objective_sense, - objective_offset, - objective_coefficients, - quadratic_objective_matrix_row_offsets, - quadratic_objective_matrix_column_indices, - quadratic_objective_matrix_coefficent_values, - row_offsets, - column_indices, - values, - constraint_lower_bounds, - constraint_upper_bounds, - var_lower_bounds, - var_upper_bounds, - &problem); + num_variables, + objective_sense, + objective_offset, + objective_coefficients, + quadratic_objective_matrix_row_offsets, + quadratic_objective_matrix_column_indices, + quadratic_objective_matrix_coefficent_values, + row_offsets, + column_indices, + values, + constraint_lower_bounds, + constraint_upper_bounds, + var_lower_bounds, + var_upper_bounds, + &problem); if (status != CUOPT_SUCCESS) { printf("Error creating problem: %d\n", status); @@ -1370,19 +1374,19 @@ cuopt_int_t test_quadratic_ranged_problem(cuopt_int_t* termination_status_ptr, c } DONE: -cuOptDestroyProblem(&problem); -cuOptDestroySolverSettings(&settings); -cuOptDestroySolution(&solution); + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); -return status; + return status; } cuopt_int_t test_write_problem(const char* input_filename, const char* output_filename) { - cuOptOptimizationProblem problem = NULL; + cuOptOptimizationProblem problem = NULL; cuOptOptimizationProblem problem_read = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; cuopt_int_t status; cuopt_int_t termination_status; cuopt_float_t objective_value; @@ -1458,12 +1462,16 @@ cuopt_int_t test_write_problem(const char* input_filename, const char* output_fi return status; } - -cuopt_int_t test_maximize_problem_dual_variables(cuopt_int_t method, cuopt_int_t* termination_status_ptr, cuopt_float_t* objective_ptr, cuopt_float_t* dual_variables, cuopt_float_t* reduced_costs, cuopt_float_t *dual_obj_ptr) +cuopt_int_t test_maximize_problem_dual_variables(cuopt_int_t method, + cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr, + cuopt_float_t* dual_variables, + cuopt_float_t* reduced_costs, + cuopt_float_t* dual_obj_ptr) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; /* Solve the following problem maximize 4*x1 + x2 + 5*x3 + 3*x4 @@ -1473,20 +1481,17 @@ cuopt_int_t test_maximize_problem_dual_variables(cuopt_int_t method, cuopt_int_t x1, x2, x3, x4 >= 0 */ - cuopt_int_t num_variables = 4; - cuopt_int_t num_constraints = 3; - cuopt_int_t nnz = 12; - cuopt_int_t row_offsets[] = {0, 4, 8, 12}; - cuopt_int_t column_indices[] = {0, 1, 2, 3, - 0, 1, 2, 3, - 0, 1, 2, 3}; - cuopt_float_t values[] = {1.0, -1.0, -1.0, 3.0, - 5.0, 1.0, 3.0, 8.0, - -1.0, 2.0, 3.0, -5.0}; - cuopt_float_t rhs[] = {1.0, 55.0, 3.0}; - char constraint_sense[] = {CUOPT_LESS_THAN, CUOPT_LESS_THAN, CUOPT_LESS_THAN}; + cuopt_int_t num_variables = 4; + cuopt_int_t num_constraints = 3; + cuopt_int_t nnz = 12; + cuopt_int_t row_offsets[] = {0, 4, 8, 12}; + cuopt_int_t column_indices[] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; + cuopt_float_t values[] = {1.0, -1.0, -1.0, 3.0, 5.0, 1.0, 3.0, 8.0, -1.0, 2.0, 3.0, -5.0}; + cuopt_float_t rhs[] = {1.0, 55.0, 3.0}; + char constraint_sense[] = {CUOPT_LESS_THAN, CUOPT_LESS_THAN, CUOPT_LESS_THAN}; cuopt_float_t var_lower_bounds[] = {0.0, 0.0, 0.0, 0.0}; - cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; + cuopt_float_t var_upper_bounds[] = { + CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; cuopt_float_t objective_coefficients[] = {4.0, 1.0, 5.0, 3.0}; @@ -1569,10 +1574,9 @@ cuopt_int_t test_maximize_problem_dual_variables(cuopt_int_t method, cuopt_int_t time); printf("Objective value: %f\n", *objective_ptr); - /* Get and print solution variables */ cuopt_float_t* solution_values = (cuopt_float_t*)malloc(num_variables * sizeof(cuopt_float_t)); - status = cuOptGetPrimalSolution(solution, solution_values); + status = cuOptGetPrimalSolution(solution, solution_values); if (status != CUOPT_SUCCESS) { printf("Error getting solution values: %d\n", status); free(solution_values); @@ -1627,7 +1631,8 @@ cuopt_int_t test_deterministic_bb(const char* filename, cuopt_int_t status; cuopt_int_t run; - printf("Testing deterministic B&B: %s with %d threads, %d runs\n", filename, num_threads, num_runs); + printf( + "Testing deterministic B&B: %s with %d threads, %d runs\n", filename, num_threads, num_runs); status = cuOptReadProblem(filename, &problem); if (status != CUOPT_SUCCESS) { @@ -1766,21 +1771,20 @@ cuopt_int_t test_lp_solution_mip_methods() cuopt_float_t mip_gap; cuopt_float_t solution_bound; - cuopt_float_t obj[] = {1.0, 2.0}; - cuopt_int_t offsets[] = {0, 2}; - cuopt_int_t indices[] = {0, 1}; - cuopt_float_t vals[] = {1.0, 1.0}; - char sense[] = {CUOPT_LESS_THAN}; - cuopt_float_t rhs[] = {10.0}; - cuopt_float_t lb[] = {0.0, 0.0}; - cuopt_float_t ub[] = {100.0, 100.0}; - char vtypes[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + cuopt_float_t obj[] = {1.0, 2.0}; + cuopt_int_t offsets[] = {0, 2}; + cuopt_int_t indices[] = {0, 1}; + cuopt_float_t vals[] = {1.0, 1.0}; + char sense[] = {CUOPT_LESS_THAN}; + cuopt_float_t rhs[] = {10.0}; + cuopt_float_t lb[] = {0.0, 0.0}; + cuopt_float_t ub[] = {100.0, 100.0}; + char vtypes[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; printf("Testing LP solution with MIP-only methods...\n"); - status = cuOptCreateProblem(1, 2, CUOPT_MINIMIZE, 0.0, - obj, offsets, indices, vals, - sense, rhs, lb, ub, vtypes, &problem); + status = cuOptCreateProblem( + 1, 2, CUOPT_MINIMIZE, 0.0, obj, offsets, indices, vals, sense, rhs, lb, ub, vtypes, &problem); if (status != CUOPT_SUCCESS) { printf("Error creating LP problem: %d\n", status); goto DONE; @@ -1840,21 +1844,20 @@ cuopt_int_t test_mip_solution_lp_methods() cuopt_float_t dual_solution[1]; cuopt_float_t reduced_costs[2]; - cuopt_float_t obj[] = {3.0, 5.0}; - cuopt_int_t offsets[] = {0, 2}; - cuopt_int_t indices[] = {0, 1}; - cuopt_float_t vals[] = {1.0, 2.0}; - char sense[] = {CUOPT_LESS_THAN}; - cuopt_float_t rhs[] = {4.0}; - cuopt_float_t lb[] = {0.0, 0.0}; - cuopt_float_t ub[] = {1.0, 1.0}; - char vtypes[] = {CUOPT_INTEGER, CUOPT_INTEGER}; + cuopt_float_t obj[] = {3.0, 5.0}; + cuopt_int_t offsets[] = {0, 2}; + cuopt_int_t indices[] = {0, 1}; + cuopt_float_t vals[] = {1.0, 2.0}; + char sense[] = {CUOPT_LESS_THAN}; + cuopt_float_t rhs[] = {4.0}; + cuopt_float_t lb[] = {0.0, 0.0}; + cuopt_float_t ub[] = {1.0, 1.0}; + char vtypes[] = {CUOPT_INTEGER, CUOPT_INTEGER}; printf("Testing MIP solution with LP-only methods...\n"); - status = cuOptCreateProblem(1, 2, CUOPT_MAXIMIZE, 0.0, - obj, offsets, indices, vals, - sense, rhs, lb, ub, vtypes, &problem); + status = cuOptCreateProblem( + 1, 2, CUOPT_MAXIMIZE, 0.0, obj, offsets, indices, vals, sense, rhs, lb, ub, vtypes, &problem); if (status != CUOPT_SUCCESS) { printf("Error creating MIP problem: %d\n", status); goto DONE; @@ -1929,8 +1932,10 @@ cuopt_int_t test_cpu_only_execution(const char* filename) cuopt_float_t* primal_solution = NULL; printf("Testing CPU-only execution (simulated remote mode)...\n"); - printf(" CUDA_VISIBLE_DEVICES=%s\n", getenv("CUDA_VISIBLE_DEVICES") ? getenv("CUDA_VISIBLE_DEVICES") : "(not set)"); - printf(" CUOPT_REMOTE_HOST=%s\n", getenv("CUOPT_REMOTE_HOST") ? getenv("CUOPT_REMOTE_HOST") : "(not set)"); + printf(" CUDA_VISIBLE_DEVICES=%s\n", + getenv("CUDA_VISIBLE_DEVICES") ? getenv("CUDA_VISIBLE_DEVICES") : "(not set)"); + printf(" CUOPT_REMOTE_HOST=%s\n", + getenv("CUOPT_REMOTE_HOST") ? getenv("CUOPT_REMOTE_HOST") : "(not set)"); status = cuOptReadProblem(filename, &problem); if (status != CUOPT_SUCCESS) { @@ -2008,9 +2013,7 @@ cuopt_int_t test_cpu_only_execution(const char* filename) printf(" Termination status: %s\n", termination_status_to_string(termination_status)); printf(" Objective value: %f\n", objective_value); printf(" Solve time: %f\n", solve_time); - if (num_variables > 0) { - printf(" Primal solution[0]: %f\n", primal_solution[0]); - } + if (num_variables > 0) { printf(" Primal solution[0]: %f\n", primal_solution[0]); } status = CUOPT_SUCCESS; @@ -2190,8 +2193,8 @@ cuopt_int_t test_pdlp_precision_mixed(const char* filename, } cuopt_int_t test_pdlp_precision_single(const char* filename, - cuopt_int_t* termination_status_ptr, - cuopt_float_t* objective_ptr) + cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr) { cuOptOptimizationProblem problem = NULL; cuOptSolverSettings settings = NULL; diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index 50d4a8fb45..0d9e2bedd5 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -1036,7 +1036,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_TERMINATION_STATUS_OPTIMAL); - EXPECT_FALSE(solution.get_additional_termination_information().solved_by_pdlp); + EXPECT_EQ(solution.get_additional_termination_information().solved_by, method_t::DualSimplex); } TEST(pdlp_class, test_max) 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 08da011a3d..4683a5e3cc 100644 --- a/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu +++ b/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu @@ -87,7 +87,7 @@ static std::unique_ptr> make_cpu_lp_solution(bool /*l2_dual_residual=*/2e-8, /*gap=*/0.5, /*num_iterations=*/100, - /*solved_by_pdlp=*/true); + /*solved_by=*/method_t::PDLP); } cpu_pdlp_warm_start_data_t ws; @@ -120,7 +120,7 @@ static std::unique_ptr> make_cpu_lp_solution(bool /*l2_dual_residual=*/2e-8, /*gap=*/0.5, /*num_iterations=*/100, - /*solved_by_pdlp=*/true, + /*solved_by=*/method_t::PDLP, std::move(ws)); } @@ -167,7 +167,7 @@ static gpu_lp_solution_t make_gpu_lp_solution() term_stats[0].l2_dual_residual = 2e-8; term_stats[0].gap = 0.5; term_stats[0].number_of_steps_taken = 100; - term_stats[0].solved_by_pdlp = true; + term_stats[0].solved_by = method_t::PDLP; std::vector term_status = {pdlp_termination_status_t::Optimal}; 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 6d80679514..c61a29bd95 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 @@ -39,8 +39,8 @@ const char* termination_status_to_string(cuopt_int_t termination_status) cuopt_int_t solve_mps_file(const char* filename) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; cuopt_int_t status; cuopt_float_t time; cuopt_int_t termination_status; @@ -115,18 +115,20 @@ cuopt_int_t solve_mps_file(const char* filename) printf("\nResults:\n"); printf("--------\n"); printf("Number of variables: %d\n", num_variables); - printf("Termination status: %s (%d)\n", termination_status_to_string(termination_status), termination_status); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); printf("Solve time: %f seconds\n", time); printf("Objective value: %f\n", objective_value); // Get and print solution variables 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; - } + 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"); @@ -145,7 +147,8 @@ cuopt_int_t solve_mps_file(const char* filename) return status; } -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) +{ if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; 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 ac32e19afb..696d59cecd 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 @@ -60,8 +60,8 @@ const char* termination_status_to_string(cuopt_int_t termination_status) cuopt_int_t solve_mps_file(const char* filename) { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; cuopt_int_t status; cuopt_float_t time; cuopt_int_t termination_status; @@ -129,13 +129,15 @@ cuopt_int_t solve_mps_file(const char* filename) printf("\nResults:\n"); printf("--------\n"); printf("Number of variables: %d\n", num_variables); - printf("Termination status: %s (%d)\n", termination_status_to_string(termination_status), termination_status); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); printf("Solve time: %f seconds\n", time); 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); + status = cuOptGetPrimalSolution(solution, solution_values); if (status != CUOPT_SUCCESS) { printf("Error getting solution values: %d\n", status); goto DONE; @@ -158,7 +160,8 @@ cuopt_int_t solve_mps_file(const char* filename) return status; } -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) +{ if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; 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 2d675094c3..977a173c7b 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 @@ -62,8 +62,8 @@ const char* termination_status_to_string(cuopt_int_t termination_status) cuopt_int_t test_simple_lp() { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; /* Solve the following LP: minimize -0.2*x1 + 0.1*x2 @@ -73,18 +73,18 @@ cuopt_int_t test_simple_lp() x1, x2 >= 0 */ - cuopt_int_t num_variables = 2; + cuopt_int_t num_variables = 2; cuopt_int_t num_constraints = 2; - cuopt_int_t nnz = 4; + cuopt_int_t nnz = 4; // CSR format constraint matrix // https://docs.nvidia.com/nvpl/latest/sparse/storage_format/sparse_matrix.html#compressed-sparse-row-csr // From the constraints: // 3.0*x1 + 4.0*x2 <= 5.4 // 2.7*x1 + 10.1*x2 <= 4.9 - cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t row_offsets[] = {0, 2, 4}; cuopt_int_t column_indices[] = {0, 1, 0, 1}; - cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; // Objective coefficients // From the objective function: minimize -0.2*x1 + 0.1*x2 @@ -119,19 +119,19 @@ cuopt_int_t test_simple_lp() // Create the problem status = cuOptCreateRangedProblem(num_constraints, - num_variables, - CUOPT_MINIMIZE, - 0.0, // objective offset - objective_coefficients, - row_offsets, - column_indices, - values, - constraint_lower_bounds, - constraint_upper_bounds, - var_lower_bounds, - var_upper_bounds, - variable_types, - &problem); + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_lower_bounds, + constraint_upper_bounds, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); if (status != CUOPT_SUCCESS) { printf("Error creating problem: %d\n", status); goto DONE; @@ -180,7 +180,9 @@ cuopt_int_t test_simple_lp() // Print results printf("\nResults:\n"); printf("--------\n"); - printf("Termination status: %s (%d)\n", termination_status_to_string(termination_status), termination_status); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); printf("Solve time: %f seconds\n", time); printf("Objective value: %f\n", objective_value); @@ -211,7 +213,8 @@ cuopt_int_t test_simple_lp() return status; } -int main() { +int main() +{ // Run the test cuopt_int_t status = test_simple_lp(); 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 d406cc8e12..401c87a2dc 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 @@ -40,8 +40,8 @@ const char* termination_status_to_string(cuopt_int_t termination_status) cuopt_int_t test_simple_milp() { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; /* Solve the following MILP: minimize -0.2*x1 + 0.1*x2 @@ -53,18 +53,18 @@ cuopt_int_t test_simple_milp() x2 is continuous */ - cuopt_int_t num_variables = 2; + cuopt_int_t num_variables = 2; cuopt_int_t num_constraints = 2; - cuopt_int_t nnz = 4; + cuopt_int_t nnz = 4; // CSR format constraint matrix // https://docs.nvidia.com/nvpl/latest/sparse/storage_format/sparse_matrix.html#compressed-sparse-row-csr // From the constraints: // 3.0*x1 + 4.0*x2 <= 5.4 // 2.7*x1 + 10.1*x2 <= 4.9 - cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t row_offsets[] = {0, 2, 4}; cuopt_int_t column_indices[] = {0, 1, 0, 1}; - cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; // Objective coefficients // From the objective function: minimize -0.2*x1 + 0.1*x2 @@ -99,19 +99,19 @@ cuopt_int_t test_simple_milp() // Create the problem status = cuOptCreateRangedProblem(num_constraints, - num_variables, - CUOPT_MINIMIZE, - 0.0, // objective offset - objective_coefficients, - row_offsets, - column_indices, - values, - constraint_lower_bounds, - constraint_upper_bounds, - var_lower_bounds, - var_upper_bounds, - variable_types, - &problem); + num_variables, + CUOPT_MINIMIZE, + 0.0, // objective offset + objective_coefficients, + row_offsets, + column_indices, + values, + constraint_lower_bounds, + constraint_upper_bounds, + var_lower_bounds, + var_upper_bounds, + variable_types, + &problem); if (status != CUOPT_SUCCESS) { printf("Error creating problem: %d\n", status); goto DONE; @@ -160,7 +160,9 @@ cuopt_int_t test_simple_milp() // Print results printf("\nResults:\n"); printf("--------\n"); - printf("Termination status: %s (%d)\n", termination_status_to_string(termination_status), termination_status); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); printf("Solve time: %f seconds\n", time); printf("Objective value: %f\n", objective_value); @@ -191,7 +193,8 @@ cuopt_int_t test_simple_milp() return status; } -int main() { +int main() +{ // Run the test cuopt_int_t status = test_simple_milp(); 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 a68f360e3b..77b3f21f10 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 @@ -1,6 +1,6 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights + * reserved. SPDX-License-Identifier: Apache-2.0 */ /* * Simple QP C API Example @@ -57,8 +57,8 @@ const char* termination_status_to_string(cuopt_int_t termination_status) cuopt_int_t test_simple_qp() { cuOptOptimizationProblem problem = NULL; - cuOptSolverSettings settings = NULL; - cuOptSolution solution = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; /* Solve the following QP: minimize x^2 + y^2 @@ -67,15 +67,15 @@ cuopt_int_t test_simple_qp() x, y >= 0 */ - cuopt_int_t num_variables = 2; + cuopt_int_t num_variables = 2; cuopt_int_t num_constraints = 1; - cuopt_int_t nnz = 2; + cuopt_int_t nnz = 2; // CSR format constraint matrix // https://docs.nvidia.com/nvpl/latest/sparse/storage_format/sparse_matrix.html#compressed-sparse-row-csr - cuopt_int_t row_offsets[] = {0, 2}; + cuopt_int_t row_offsets[] = {0, 2}; cuopt_int_t column_indices[] = {0, 1}; - cuopt_float_t values[] = {1.0, 1.0}; + cuopt_float_t values[] = {1.0, 1.0}; // Objective coefficients // From the objective function: minimize x^2 + y^2 @@ -87,16 +87,15 @@ cuopt_int_t test_simple_qp() // From the objective function: minimize x^2 + y^2 // 1 is the coefficient of the quadratic term on x^2 // 1 is the coefficient of the quadratic term on y^2 - cuopt_float_t quadratic_objective_matrix_values[] = {1.0, 1.0}; - cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; + cuopt_float_t quadratic_objective_matrix_values[] = {1.0, 1.0}; + cuopt_int_t quadratic_objective_matrix_row_offsets[] = {0, 1, 2}; cuopt_int_t quadratic_objective_matrix_column_indices[] = {0, 1}; // Constraint bounds // From the constraints: // x + y >= 1 cuopt_float_t constraint_rhs[] = {1.0}; - char constraint_sense[] = { CUOPT_GREATER_THAN }; - + char constraint_sense[] = {CUOPT_GREATER_THAN}; // Variable bounds // From the constraints: @@ -174,7 +173,9 @@ cuopt_int_t test_simple_qp() // Print results printf("\nResults:\n"); printf("--------\n"); - printf("Termination status: %s (%d)\n", termination_status_to_string(termination_status), termination_status); + printf("Termination status: %s (%d)\n", + termination_status_to_string(termination_status), + termination_status); printf("Solve time: %f seconds\n", time); printf("Objective value: %f\n", objective_value); @@ -205,7 +206,8 @@ cuopt_int_t test_simple_qp() return status; } -int main() { +int main() +{ // Run the test cuopt_int_t status = test_simple_qp(); diff --git a/python/cuopt/cuopt/linear_programming/solution/solution.py b/python/cuopt/cuopt/linear_programming/solution/solution.py index e2533da8c1..93d224fdbd 100644 --- a/python/cuopt/cuopt/linear_programming/solution/solution.py +++ b/python/cuopt/cuopt/linear_programming/solution/solution.py @@ -1,6 +1,9 @@ # SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +from cuopt.linear_programming.solver_settings.solver_settings import ( + SolverMethod, +) from cuopt.linear_programming.solver.solver_wrapper import ( LPTerminationStatus, MILPTerminationStatus, @@ -116,8 +119,10 @@ class Solution: Time used for pre-solve solve_time: Float64 Solve time in seconds - solved_by_pdlp: bool - Whether the problem was solved by PDLP or Dual Simplex + solved_by: enum + Note: Applicable to only LP + Whether the LP was solved by Dual Simplex, PDLP or Barrier. This is populated + by the solver using the values from SolverMethod. """ def __init__( @@ -154,7 +159,7 @@ def __init__( dual_objective=0.0, gap=0.0, nb_iterations=0, - solved_by_pdlp=None, + solved_by=SolverMethod.Unset, mip_gap=0.0, solution_bound=0.0, presolve_time=0.0, @@ -196,7 +201,7 @@ def __init__( self.primal_objective = primal_objective self.dual_objective = dual_objective self.solve_time = solve_time - self.solved_by_pdlp = solved_by_pdlp + self.solved_by = SolverMethod(solved_by) self.vars = vars self.lp_stats = { "primal_residual": primal_residual, @@ -302,10 +307,23 @@ def get_solve_time(self): return self.solve_time def get_solved_by_pdlp(self): + from warnings import warn + + warn( + "get_solved_by_pdlp() will be deprecated in 26.08. Use get_solved_by() instead. ", + DeprecationWarning, + ) + + """ + Returns whether the problem was solved by PDLP or not. + """ + return self.solved_by == SolverMethod.PDLP + + def get_solved_by(self): """ - Returns whether the problem was solved by PDLP or Dual Simplex + Returns whether the LP was solved by Dual Simplex, PDLP or Barrier. See SolverMethod for all possible values. """ - return self.solved_by_pdlp + return self.solved_by def get_vars(self): """ diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.pxd b/python/cuopt/cuopt/linear_programming/solver/solver.pxd index f5ed90055f..3bb2cba34a 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.pxd +++ b/python/cuopt/cuopt/linear_programming/solver/solver.pxd @@ -29,6 +29,13 @@ cdef extern from "cuopt/linear_programming/pdlp/solver_settings.hpp" namespace " Fast1 "cuopt::linear_programming::pdlp_solver_mode_t::Fast1" # noqa Stable3 "cuopt::linear_programming::pdlp_solver_mode_t::Stable3" # noqa + ctypedef enum method_t "cuopt::linear_programming::method_t": # noqa + Concurrent "cuopt::linear_programming::method_t::Concurrent" # noqa + PDLP "cuopt::linear_programming::method_t::PDLP" # noqa + DualSimplex "cuopt::linear_programming::method_t::DualSimplex" # noqa + Barrier "cuopt::linear_programming::method_t::Barrier" # noqa + Unset "cuopt::linear_programming::method_t::Unset" # noqa + cdef extern from "cuopt/linear_programming/solver_settings.hpp" namespace "cuopt::linear_programming": # noqa cdef cppclass solver_settings_t[i_t, f_t]: @@ -178,7 +185,7 @@ cdef extern from "cuopt/linear_programming/utilities/cython_solve.hpp" namespace double gap_ int nb_iterations_ double solve_time_ - bool solved_by_pdlp_ + method_t solved_by_ bool is_gpu() # Unified MIP solution struct — solution_ variant accessed via helpers diff --git a/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx b/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx index cb4831a367..f5a7aaab48 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx +++ b/python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx @@ -62,6 +62,7 @@ import cudf from cuopt.linear_programming.solver_settings.solver_settings import ( PDLPSolverMode, + SolverMethod, SolverSettings, ) from cuopt.utilities import InputValidationError, series_from_buf @@ -479,7 +480,7 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr, lp_ptr.dual_objective_, lp_ptr.gap_, lp_ptr.nb_iterations_, - lp_ptr.solved_by_pdlp_, + lp_ptr.solved_by_, ) else: return Solution( @@ -498,7 +499,7 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr, dual_objective=lp_ptr.dual_objective_, gap=lp_ptr.gap_, nb_iterations=lp_ptr.nb_iterations_, - solved_by_pdlp=lp_ptr.solved_by_pdlp_, + solved_by=lp_ptr.solved_by_, ) diff --git a/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py b/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py index 19db315349..dc689b75fe 100644 --- a/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py +++ b/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py @@ -18,6 +18,7 @@ class SolverMethod(IntEnum): PDLP = auto() DualSimplex = auto() Barrier = auto() + Unset = auto() def __str__(self): """Convert the solver method to a string. 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 08cb0b4a70..291c80d925 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -87,7 +87,7 @@ def test_solver(): assert solution.get_primal_objective() == pytest.approx(0.0) assert solution.get_dual_objective() == pytest.approx(0.0) assert solution.get_lp_stats()["gap"] == pytest.approx(0.0) - assert solution.get_solved_by_pdlp() + assert solution.get_solved_by() == SolverMethod.PDLP def test_parser_and_solver(): @@ -600,7 +600,7 @@ def test_dual_simplex(): assert solution.get_termination_status() == LPTerminationStatus.Optimal assert solution.get_primal_objective() == pytest.approx(-464.7531) - assert not solution.get_solved_by_pdlp() + assert solution.get_solved_by() == SolverMethod.DualSimplex def test_barrier(): @@ -768,7 +768,7 @@ def test_pdlp_precision_single(): assert solution.get_primal_objective() == pytest.approx( -464.7531, rel=1e-1 ) - assert solution.get_solved_by_pdlp() + assert solution.get_solved_by() == SolverMethod.PDLP def test_pdlp_precision_single_crossover(): diff --git a/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py b/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py index 066e81b026..8a4156c592 100644 --- a/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py +++ b/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py @@ -200,7 +200,7 @@ def create_solution_obj(solver_response): nb_iterations=sol["lp_statistics"]["nb_iterations"], primal_objective=sol["primal_objective"], dual_objective=sol["dual_objective"], - solved_by_pdlp=sol["solved_by_pdlp"], + solved_by=sol["solved_by"], ) return status, solution_obj diff --git a/python/cuopt_self_hosted/cuopt_sh_client/thin_client_solution.py b/python/cuopt_self_hosted/cuopt_sh_client/thin_client_solution.py index aa169abed9..1d519211fe 100644 --- a/python/cuopt_self_hosted/cuopt_sh_client/thin_client_solution.py +++ b/python/cuopt_self_hosted/cuopt_sh_client/thin_client_solution.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 @@ -66,8 +66,8 @@ class ThinClientSolution: Time used for pre-solve solve_time: Float64 Solve time in seconds - solved_by_pdlp: bool - Whether the problem was solved by PDLP or Dual Simplex + solved_by: str + Whether the problem was solved by PDLP, Barrier or Dual Simplex """ def __init__( @@ -87,7 +87,7 @@ def __init__( dual_objective=0.0, gap=0.0, nb_iterations=0, - solved_by_pdlp=None, + solved_by=None, mip_gap=0.0, solution_bound=0.0, presolve_time=0.0, @@ -107,7 +107,7 @@ def __init__( self.primal_objective = primal_objective self.dual_objective = dual_objective self.solve_time = solve_time - self.solved_by_pdlp = solved_by_pdlp + self.solved_by = solved_by self.vars = vars self.lp_stats = { "primal_residual": primal_residual, @@ -191,11 +191,11 @@ def get_solve_time(self): """ return self.solve_time - def get_solved_by_pdlp(self): + def get_solved_by(self): """ - Returns whether the problem was solved by PDLP or Dual Simplex + Returns whether the problem was solved by PDLP, Barrier or Dual Simplex """ - return self.solved_by_pdlp + return self.solved_by def get_vars(self): """ 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 dba48f010a..8eb58f975c 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 @@ -700,10 +700,10 @@ class SolutionData(StrictModel): default=None, description=("Returns the engine solve time in seconds"), ) - solved_by_pdlp: bool = Field( + solved_by: int = Field( default=None, description=( - "Returns whether problem was solved by PDLP or Dual Simplex" + "Returns whether problem was solved by PDLP, Barrier or Dual Simplex" ), ) primal_objective: float = Field( diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py index 87524f8715..0bc05a9b9f 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py @@ -317,7 +317,7 @@ def create_solution(sol): sol.get_dual_objective ) solution["solver_time"] = sol.get_solve_time() - solution["solved_by_pdlp"] = sol.get_solved_by_pdlp() + solution["solved_by"] = sol.get_solved_by().name solution["vars"] = sol.get_vars() solution["lp_statistics"] = {} if lp_stats is None else lp_stats solution["reduced_cost"] = reduced_cost