From 2db68ebc09cff0afc15ded161ea6680793a4b215 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Mon, 18 Aug 2025 18:10:29 -0700 Subject: [PATCH 1/3] Add maximization support for root node presolve --- cpp/src/linear_programming/solve.cu | 1 - cpp/src/mip/presolve/third_party_presolve.cpp | 30 ++++++++++++------- cpp/src/mip/problem/problem_helpers.cuh | 14 +++++---- cpp/src/mip/solve.cu | 1 - 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 504d87bd3e..d447f53b53 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -591,7 +591,6 @@ optimization_problem_solution_t solve_lp(optimization_problem_t> presolver; auto run_presolve = settings.presolve; - run_presolve = run_presolve && op_problem.get_sense() == false; run_presolve = run_presolve && settings.get_pdlp_warm_start_data().total_pdlp_iterations_ == -1; if (!run_presolve) { CUOPT_LOG_INFO("Presolve is disabled, skipping"); } diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 3d87007564..5f1ef1ca51 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -15,18 +15,19 @@ * limitations under the License. */ +#include +#include #include #include + #include #include -// #include -#include -#include namespace cuopt::linear_programming::detail { static papilo::PostsolveStorage post_solve_storage_; static int presolve_calls_ = 0; +static bool maximize_ = false; template papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem) @@ -39,10 +40,6 @@ papilo::Problem build_papilo_problem(const optimization_problem_t const i_t num_rows = op_problem.get_n_constraints(); const i_t nnz = op_problem.get_nnz(); - cuopt_expects(op_problem.get_sense() == false, - error_type_t::ValidationError, - "Papilo does not support maximization problems"); - builder.reserve(nnz, num_rows, num_cols); // Get problem data from optimization problem @@ -83,6 +80,13 @@ papilo::Problem build_papilo_problem(const optimization_problem_t std::vector h_var_types(var_types.size()); raft::copy(h_var_types.data(), var_types.data(), var_types.size(), stream_view); + maximize_ = op_problem.get_sense(); + if (maximize_) { + for (size_t i = 0; i < h_obj_coeffs.size(); ++i) { + h_obj_coeffs[i] = -h_obj_coeffs[i]; + } + } + auto constr_bounds_empty = h_constr_lb.empty() && h_constr_ub.empty(); if (constr_bounds_empty) { for (size_t i = 0; i < h_row_types.size(); ++i) { @@ -103,7 +107,8 @@ papilo::Problem build_papilo_problem(const optimization_problem_t builder.setNumRows(num_rows); builder.setObjAll(h_obj_coeffs); - builder.setObjOffset(op_problem.get_objective_offset()); + builder.setObjOffset(maximize_ ? -op_problem.get_objective_offset() + : op_problem.get_objective_offset()); if (!h_var_lb.empty() && !h_var_ub.empty()) { builder.setColLbAll(h_var_lb); @@ -149,7 +154,8 @@ optimization_problem_t build_optimization_problem( optimization_problem_t op_problem(handle_ptr); auto obj = papilo_problem.getObjective(); - op_problem.set_objective_offset(obj.offset); + op_problem.set_objective_offset(maximize_ ? -obj.offset : obj.offset); + op_problem.set_maximize(maximize_); if (papilo_problem.getNRows() == 0 && papilo_problem.getNCols() == 0) { // FIXME: Shouldn't need to set offsets @@ -165,7 +171,11 @@ optimization_problem_t build_optimization_problem( return op_problem; } - + if (maximize_) { + for (size_t i = 0; i < obj.coefficients.size(); ++i) { + obj.coefficients[i] = -obj.coefficients[i]; + } + } op_problem.set_objective_coefficients(obj.coefficients.data(), obj.coefficients.size()); auto& constraint_matrix = papilo_problem.getConstraintMatrix(); diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index dfcebc9d34..bdc4635c03 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -129,12 +129,14 @@ static void convert_to_maximization_problem(detail::problem_t& op_prob { raft::common::nvtx::range scope("convert_to_maximization_problem"); - // Negate objective coefficient - raft::linalg::unaryOp(op_problem.objective_coefficients.data(), - op_problem.objective_coefficients.data(), - op_problem.objective_coefficients.size(), - detail::negate(), - op_problem.handle_ptr->get_stream()); + if (op_problem.objective_coefficients.size()) { + // Negate objective coefficient + raft::linalg::unaryOp(op_problem.objective_coefficients.data(), + op_problem.objective_coefficients.data(), + op_problem.objective_coefficients.size(), + detail::negate(), + op_problem.handle_ptr->get_stream()); + } // Negate objective scaling factor and objective offset so that primal / dual stay same sign after // negating objective coeffs op_problem.presolve_data.objective_scaling_factor = diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 7071c33fea..271fada382 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -183,7 +183,6 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, detail::problem_t problem(op_problem, settings.get_tolerances()); auto run_presolve = settings.presolve; - run_presolve = run_presolve && op_problem.get_sense() == false; run_presolve = run_presolve && settings.get_mip_callbacks().empty(); if (!run_presolve) { CUOPT_LOG_INFO("Presolve is disabled, skipping"); } From 5fec2369284e9459f6880d5e892a0d79800eca57 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Tue, 19 Aug 2025 20:17:37 -0700 Subject: [PATCH 2/3] Disable presolve for doc test --- cpp/tests/mip/doc_example_test.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/tests/mip/doc_example_test.cu b/cpp/tests/mip/doc_example_test.cu index c021da0b1c..3506eb00d2 100644 --- a/cpp/tests/mip/doc_example_test.cu +++ b/cpp/tests/mip/doc_example_test.cu @@ -135,6 +135,7 @@ TEST(docs, user_problem_file) settings.time_limit = test_time_limit; settings.user_problem_file = user_problem_path; + settings.presolve = false; EXPECT_EQ(solve_mip(&handle_, problem, settings).get_termination_status(), mip_termination_status_t::Optimal); From a0e5804a458dfdeb91589e54652af9b383eaa0c0 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 21 Aug 2025 11:37:28 -0700 Subject: [PATCH 3/3] Write problem to mps after presolve for LP --- cpp/src/linear_programming/solve.cu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 549b1ffab6..8dc1909c87 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -583,11 +583,6 @@ optimization_problem_solution_t solve_lp(optimization_problem_t problem(op_problem); - if (settings.user_problem_file != "") { - CUOPT_LOG_INFO("Writing user problem to file: %s", settings.user_problem_file.c_str()); - problem.write_as_mps(settings.user_problem_file); - } - double presolve_time = 0.0; std::unique_ptr> presolver; auto run_presolve = settings.presolve; @@ -614,6 +609,11 @@ optimization_problem_solution_t solve_lp(optimization_problem_t