From dc2937bf3531a14ec39b71536590d216d32c40ee Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Wed, 24 Sep 2025 15:05:55 -0700 Subject: [PATCH 01/29] Integrate CliqueMerging presolver --- cpp/CMakeLists.txt | 4 ++- cpp/src/mip/presolve/third_party_presolve.cpp | 32 +++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 45789cedd..a54eab7d5 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -181,7 +181,9 @@ endif() FetchContent_Declare( papilo GIT_REPOSITORY "https://github.com/scipopt/papilo.git" - GIT_TAG "v2.4.3" + # Get the latest commit from development branch + GIT_TAG "bd1222a6b6608283d31a0fea6735695345bcd936" + GIT_PROGRESS TRUE SYSTEM ) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index dc2d4b00e..523449f6b 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -34,7 +34,8 @@ static papilo::PostsolveStorage post_solve_storage_; static bool maximize_ = false; template -papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem) +papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem, + problem_category_t category) { // Build papilo problem from optimization problem papilo::ProblemBuilder builder; @@ -167,7 +168,10 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (h_entries.size()) { auto constexpr const sorted_entries = true; - auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries); + // MIP reductions like clique merging and substituition require more fillin + const double spare_ratio = category == problem_category_t::MIP ? 4.0 : 2.0; + const int min_inter_row_space = category == problem_category_t::MIP ? 30 : 4; + auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries, spare_ratio, min_inter_row_space); problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); papilo::ConstraintMatrix& matrix = problem.getConstraintMatrix(); @@ -308,10 +312,6 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c { using uptr = std::unique_ptr>; - // cuopt custom presolvers - if (category == problem_category_t::MIP) - presolver.addPresolveMethod(uptr(new cuopt::linear_programming::detail::GF2Presolve())); - // fast presolvers presolver.addPresolveMethod(uptr(new papilo::SingletonCols())); presolver.addPresolveMethod(uptr(new papilo::CoefficientStrengthening())); @@ -336,6 +336,24 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c presolver.addPresolveMethod(uptr(new papilo::SimpleSubstitution())); presolver.addPresolveMethod(uptr(new papilo::Sparsify())); presolver.addPresolveMethod(uptr(new papilo::Substitution())); + + + if (category == problem_category_t::MIP) { + // cuOpt custom GF2 presolver + presolver.addPresolveMethod(uptr(new cuopt::linear_programming::detail::GF2Presolve())); + + // clique merging presolver that is part of development branch of papilo + const int max_edges_parallel = 1000000; + const int max_edges_sequential = 100000; + const int max_clique_size = 100; + const int max_greedy_calls = 20000; + // const int max_calls = 50; // FIXME:: make this change once the papilo PR is merged + papilo::CliqueMerging clique_merging; + clique_merging.setEnabled(true); // This is currently disabled in the papilo presolver + clique_merging.setParameters(max_edges_parallel, max_edges_sequential, max_clique_size, max_greedy_calls /* , max_calls */); + presolver.addPresolveMethod( + uptr(new papilo::CliqueMerging(std::move(clique_merging)))); + } } template @@ -360,7 +378,7 @@ std::pair, bool> third_party_presolve_t papilo_problem = build_papilo_problem(op_problem); + papilo::Problem papilo_problem = build_papilo_problem(op_problem, category); CUOPT_LOG_INFO("Unpresolved problem:: %d constraints, %d variables, %d nonzeros", papilo_problem.getNRows(), From f1b9d525525b90f2c1a5206a6cb318723d37bd00 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Thu, 25 Sep 2025 13:40:02 -0700 Subject: [PATCH 02/29] Tune probing parameter --- cpp/src/dual_simplex/branch_and_bound.cpp | 2 - cpp/src/linear_programming/solve.cu | 2 +- cpp/src/mip/presolve/third_party_presolve.cpp | 55 +++++++++++-------- cpp/src/mip/solve.cu | 2 +- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 3d4192105..185a1ede2 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -567,8 +567,6 @@ dual::status_t branch_and_bound_t::node_dual_simplex( leaf_problem, lp_start_time, lp_settings, leaf_solution, leaf_vstatus, leaf_edge_norms); lp_status = convert_lp_status_to_dual_status(second_status); } - } else { - log.printf("Infeasible after bounds strengthening. Fathoming node %d.\n", leaf_id); } mutex_stats_.lock(); diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index ef7528751..4c5dd22cf 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -608,7 +608,7 @@ optimization_problem_solution_t solve_lp(optimization_problem_t>(); auto [reduced_problem, feasible] = presolver->apply(op_problem, diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 523449f6b..9cbcf89a3 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -34,8 +34,8 @@ static papilo::PostsolveStorage post_solve_storage_; static bool maximize_ = false; template -papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem, - problem_category_t category) +papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem, + problem_category_t category) { // Build papilo problem from optimization problem papilo::ProblemBuilder builder; @@ -169,9 +169,10 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (h_entries.size()) { auto constexpr const sorted_entries = true; // MIP reductions like clique merging and substituition require more fillin - const double spare_ratio = category == problem_category_t::MIP ? 4.0 : 2.0; + const double spare_ratio = category == problem_category_t::MIP ? 4.0 : 2.0; const int min_inter_row_space = category == problem_category_t::MIP ? 30 : 4; - auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries, spare_ratio, min_inter_row_space); + auto csr_storage = papilo::SparseStorage( + h_entries, num_rows, num_cols, sorted_entries, spare_ratio, min_inter_row_space); problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); papilo::ConstraintMatrix& matrix = problem.getConstraintMatrix(); @@ -312,6 +313,10 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c { using uptr = std::unique_ptr>; + if (category == problem_category_t::MIP) { + // cuOpt custom GF2 presolver + presolver.addPresolveMethod(uptr(new cuopt::linear_programming::detail::GF2Presolve())); + } // fast presolvers presolver.addPresolveMethod(uptr(new papilo::SingletonCols())); presolver.addPresolveMethod(uptr(new papilo::CoefficientStrengthening())); @@ -326,34 +331,17 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c presolver.addPresolveMethod(uptr(new papilo::SingletonStuffing())); presolver.addPresolveMethod(uptr(new papilo::DualFix())); presolver.addPresolveMethod(uptr(new papilo::SimplifyInequalities())); + presolver.addPresolveMethod(uptr(new papilo::CliqueMerging())); // exhaustive presolvers presolver.addPresolveMethod(uptr(new papilo::ImplIntDetection())); presolver.addPresolveMethod(uptr(new papilo::DominatedCols())); - presolver.addPresolveMethod(uptr(new papilo::Probing())); presolver.addPresolveMethod(uptr(new papilo::DualInfer)); presolver.addPresolveMethod(uptr(new papilo::SimpleSubstitution())); presolver.addPresolveMethod(uptr(new papilo::Sparsify())); presolver.addPresolveMethod(uptr(new papilo::Substitution())); - - - if (category == problem_category_t::MIP) { - // cuOpt custom GF2 presolver - presolver.addPresolveMethod(uptr(new cuopt::linear_programming::detail::GF2Presolve())); - - // clique merging presolver that is part of development branch of papilo - const int max_edges_parallel = 1000000; - const int max_edges_sequential = 100000; - const int max_clique_size = 100; - const int max_greedy_calls = 20000; - // const int max_calls = 50; // FIXME:: make this change once the papilo PR is merged - papilo::CliqueMerging clique_merging; - clique_merging.setEnabled(true); // This is currently disabled in the papilo presolver - clique_merging.setParameters(max_edges_parallel, max_edges_sequential, max_clique_size, max_greedy_calls /* , max_calls */); - presolver.addPresolveMethod( - uptr(new papilo::CliqueMerging(std::move(clique_merging)))); - } + presolver.addPresolveMethod(uptr(new papilo::Probing())); } template @@ -369,6 +357,25 @@ void set_presolve_options(papilo::Presolve& presolver, presolver.getPresolveOptions().feastol = 1e-5; } +template +void set_presolve_parameters(papilo::Presolve& presolver, + problem_category_t category, + int nrows, + int ncols) +{ + auto params = presolver.getParameters(); + if (category == problem_category_t::MIP) { + std::cout << "setting presolve parameters " << std::endl; + // Papilo has work unit measurements for probing. Because of htis when the first batch fails to + // produce any reductions, the algorithm stops. To avoid stopping the algorithm, we set a + // minimum badge size to a huge value time limit makes sure that we exit it takes too long + params.setParameter("probing.minbadgesize", ncols / 2); + params.setParameter("cliquemerging.enabled", true); + // FIXME:: enable this when papilo supports it + // params.setParameter("cliquemerging.maxcalls", 50); + } +} + template std::pair, bool> third_party_presolve_t::apply( optimization_problem_t const& op_problem, @@ -389,6 +396,8 @@ std::pair, bool> third_party_presolve_t(presolver, category); set_presolve_options( presolver, category, absolute_tolerance, relative_tolerance, time_limit, num_cpu_threads); + set_presolve_parameters( + presolver, category, op_problem.get_n_constraints(), op_problem.get_n_variables()); // Disable papilo logs presolver.setVerbosityLevel(papilo::VerbosityLevel::kQuiet); diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 7941f41dc..058d55265 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -200,7 +200,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, if (run_presolve) { // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. - const double presolve_time_limit = 0.1 * time_limit; + const double presolve_time_limit = std::max(0.1 * time_limit, 60.0); presolver = std::make_unique>(); auto [reduced_op_problem, feasible] = presolver->apply(op_problem, From 234a5bd07e0ff98ac0a20e9a77e600fa4bae85ea Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 07:59:07 -0700 Subject: [PATCH 03/29] Update to latest papilo version --- cpp/CMakeLists.txt | 2 +- cpp/src/mip/presolve/third_party_presolve.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index a54eab7d5..2a2559262 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -182,7 +182,7 @@ FetchContent_Declare( papilo GIT_REPOSITORY "https://github.com/scipopt/papilo.git" # Get the latest commit from development branch - GIT_TAG "bd1222a6b6608283d31a0fea6735695345bcd936" + GIT_TAG "34a40781fa14f8870cb6368cffb6c0eda2f47511" GIT_PROGRESS TRUE SYSTEM ) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 9cbcf89a3..9d5550948 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -365,14 +365,12 @@ void set_presolve_parameters(papilo::Presolve& presolver, { auto params = presolver.getParameters(); if (category == problem_category_t::MIP) { - std::cout << "setting presolve parameters " << std::endl; // Papilo has work unit measurements for probing. Because of htis when the first batch fails to // produce any reductions, the algorithm stops. To avoid stopping the algorithm, we set a // minimum badge size to a huge value time limit makes sure that we exit it takes too long params.setParameter("probing.minbadgesize", ncols / 2); params.setParameter("cliquemerging.enabled", true); - // FIXME:: enable this when papilo supports it - // params.setParameter("cliquemerging.maxcalls", 50); + params.setParameter("cliquemerging.maxcalls", 50); } } From 81f0302a27256c2d3bb95982c45800f590848169 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 08:23:57 -0700 Subject: [PATCH 04/29] Add notes in CMake --- cpp/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 2a2559262..bd52a4666 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -181,7 +181,12 @@ endif() FetchContent_Declare( papilo GIT_REPOSITORY "https://github.com/scipopt/papilo.git" - # Get the latest commit from development branch + # We would want to get the main branch. However, the main branch + # does not have some of the presolvers and settings that we need + # Mainly, probing and clique merging. + # This is the reason we are using the development branch + # commit from Sep 26, 2025. Once these changes are merged into the main branch, + #we can switch to the main branch. GIT_TAG "34a40781fa14f8870cb6368cffb6c0eda2f47511" GIT_PROGRESS TRUE SYSTEM From 7c00a7062ac0c8f5938adbb61d92b83a9e7a503f Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 08:26:32 -0700 Subject: [PATCH 05/29] Fix typos in comments --- cpp/src/mip/presolve/third_party_presolve.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 9d5550948..94f4e629f 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -365,9 +365,10 @@ void set_presolve_parameters(papilo::Presolve& presolver, { auto params = presolver.getParameters(); if (category == problem_category_t::MIP) { - // Papilo has work unit measurements for probing. Because of htis when the first batch fails to + // Papilo has work unit measurements for probing. Because of this when the first batch fails to // produce any reductions, the algorithm stops. To avoid stopping the algorithm, we set a - // minimum badge size to a huge value time limit makes sure that we exit it takes too long + // minimum badge size to a huge value. The time limit makes sure that we exit if it takes too + // long params.setParameter("probing.minbadgesize", ncols / 2); params.setParameter("cliquemerging.enabled", true); params.setParameter("cliquemerging.maxcalls", 50); From d3ac1bc0ab44f5d3af0c7454fab0e565c37aa9df Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 08:29:26 -0700 Subject: [PATCH 06/29] Use Papilo instead of third party in the logs --- cpp/src/linear_programming/solve.cu | 2 +- cpp/src/mip/presolve/third_party_presolve.cpp | 2 ++ cpp/src/mip/solve.cu | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 4c5dd22cf..46441aa05 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -622,7 +622,7 @@ optimization_problem_solution_t solve_lp(optimization_problem_t(reduced_problem); presolve_time = lp_timer.elapsed_time(); - CUOPT_LOG_INFO("Third party presolve time: %f", presolve_time); + CUOPT_LOG_INFO("Papilo presolve time: %f", presolve_time); } CUOPT_LOG_INFO( diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 94f4e629f..664bcfa09 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -363,6 +363,7 @@ void set_presolve_parameters(papilo::Presolve& presolver, int nrows, int ncols) { + // It looks like a copy. But this copy has the pointers to relevant variables in papilo auto params = presolver.getParameters(); if (category == problem_category_t::MIP) { // Papilo has work unit measurements for probing. Because of this when the first batch fails to @@ -391,6 +392,7 @@ std::pair, bool> third_party_presolve_t presolver; set_presolve_methods(presolver, category); set_presolve_options( diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 058d55265..626dbee27 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -217,7 +217,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, problem = detail::problem_t(reduced_op_problem); presolve_time = timer.elapsed_time(); - CUOPT_LOG_INFO("Third party presolve time: %f", presolve_time); + CUOPT_LOG_INFO("Papilo presolve time: %f", presolve_time); } if (settings.user_problem_file != "") { CUOPT_LOG_INFO("Writing user problem to file: %s", settings.user_problem_file.c_str()); From 92ca5d66b06ee62a1f5628efc7dd1048c2e5d8af Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 08:41:32 -0700 Subject: [PATCH 07/29] style fixes --- cpp/src/mip/presolve/third_party_presolve.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 664bcfa09..8c4320aa8 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -392,7 +392,7 @@ std::pair, bool> third_party_presolve_t presolver; set_presolve_methods(presolver, category); set_presolve_options( From 547c9f6dae01d4e98cea7ec9013e0c1f531dbd3f Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 09:00:23 -0700 Subject: [PATCH 08/29] implement dual post solve option --- .../cuopt/linear_programming/constants.h | 1 + cpp/src/linear_programming/solve.cu | 1 + cpp/src/math_optimization/solver_settings.cu | 3 ++- cpp/src/mip/presolve/third_party_presolve.cpp | 27 ++++++++++++------- cpp/src/mip/presolve/third_party_presolve.hpp | 1 + cpp/src/mip/solve.cu | 2 ++ 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 72d05a6ad..5f3d77a53 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -51,6 +51,7 @@ #define CUOPT_LOG_TO_CONSOLE "log_to_console" #define CUOPT_CROSSOVER "crossover" #define CUOPT_PRESOLVE "presolve" +#define CUOPT_DUAL_POSTSOLVE "dual_postsolve" #define CUOPT_MIP_ABSOLUTE_TOLERANCE "mip_absolute_tolerance" #define CUOPT_MIP_RELATIVE_TOLERANCE "mip_relative_tolerance" #define CUOPT_MIP_INTEGRALITY_TOLERANCE "mip_integrality_tolerance" diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 46441aa05..b03903745 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -613,6 +613,7 @@ optimization_problem_solution_t solve_lp(optimization_problem_tapply(op_problem, cuopt::linear_programming::problem_category_t::LP, + settings.dual_postsolve, settings.tolerances.absolute_primal_tolerance, settings.tolerances.relative_primal_tolerance, presolve_time_limit); diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index a049d0d09..97cc263e3 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -106,7 +106,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_LOG_TO_CONSOLE, &mip_settings.log_to_console, true}, {CUOPT_CROSSOVER, &pdlp_settings.crossover, false}, {CUOPT_PRESOLVE, &pdlp_settings.presolve, false}, - {CUOPT_PRESOLVE, &mip_settings.presolve, true} + {CUOPT_PRESOLVE, &mip_settings.presolve, true}, + {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true} }; // String parameters string_parameters = { diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 8c4320aa8..7dd964a40 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -309,7 +309,9 @@ void check_postsolve_status(const papilo::PostsolveStatus& status) } template -void set_presolve_methods(papilo::Presolve& presolver, problem_category_t category) +void set_presolve_methods(papilo::Presolve& presolver, + problem_category_t category, + bool dual_postsolve) { using uptr = std::unique_ptr>; @@ -336,12 +338,14 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c // exhaustive presolvers presolver.addPresolveMethod(uptr(new papilo::ImplIntDetection())); presolver.addPresolveMethod(uptr(new papilo::DominatedCols())); - - presolver.addPresolveMethod(uptr(new papilo::DualInfer)); - presolver.addPresolveMethod(uptr(new papilo::SimpleSubstitution())); - presolver.addPresolveMethod(uptr(new papilo::Sparsify())); - presolver.addPresolveMethod(uptr(new papilo::Substitution())); presolver.addPresolveMethod(uptr(new papilo::Probing())); + + if (!dual_postsolve) { + presolver.addPresolveMethod(uptr(new papilo::DualInfer())); + presolver.addPresolveMethod(uptr(new papilo::SimpleSubstitution())); + presolver.addPresolveMethod(uptr(new papilo::Sparsify())); + presolver.addPresolveMethod(uptr(new papilo::Substitution())); + } } template @@ -380,6 +384,7 @@ template std::pair, bool> third_party_presolve_t::apply( optimization_problem_t const& op_problem, problem_category_t category, + bool dual_postsolve, f_t absolute_tolerance, f_t relative_tolerance, double time_limit, @@ -393,8 +398,9 @@ std::pair, bool> third_party_presolve_t presolver; - set_presolve_methods(presolver, category); + set_presolve_methods(presolver, category, dual_postsolve); set_presolve_options( presolver, category, absolute_tolerance, relative_tolerance, time_limit, num_cpu_threads); set_presolve_parameters( @@ -451,9 +457,12 @@ void third_party_presolve_t::undo(rmm::device_uvector& primal_sol check_postsolve_status(status); primal_solution.resize(full_sol.primal.size(), stream_view); - dual_solution.resize(full_sol.primal.size(), stream_view); - reduced_costs.resize(full_sol.primal.size(), stream_view); + dual_solution.resize(full_sol.dual.size(), stream_view); + reduced_costs.resize(full_sol.reducedCosts.size(), stream_view); raft::copy(primal_solution.data(), full_sol.primal.data(), full_sol.primal.size(), stream_view); + raft::copy(dual_solution.data(), full_sol.dual.data(), full_sol.dual.size(), stream_view); + raft::copy( + reduced_costs.data(), full_sol.reducedCosts.data(), full_sol.reducedCosts.size(), stream_view); } #if MIP_INSTANTIATE_FLOAT diff --git a/cpp/src/mip/presolve/third_party_presolve.hpp b/cpp/src/mip/presolve/third_party_presolve.hpp index 5631fc12e..6e5092de2 100644 --- a/cpp/src/mip/presolve/third_party_presolve.hpp +++ b/cpp/src/mip/presolve/third_party_presolve.hpp @@ -29,6 +29,7 @@ class third_party_presolve_t { std::pair, bool> apply( optimization_problem_t const& op_problem, problem_category_t category, + bool dual_postsolve, f_t absolute_tolerance, f_t relative_tolerance, double time_limit, diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 626dbee27..a52d07be5 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -201,10 +201,12 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. const double presolve_time_limit = std::max(0.1 * time_limit, 60.0); + const bool dual_postsolve = false; presolver = std::make_unique>(); auto [reduced_op_problem, feasible] = presolver->apply(op_problem, cuopt::linear_programming::problem_category_t::MIP, + dual_postsolve, settings.tolerances.absolute_tolerance, settings.tolerances.relative_tolerance, presolve_time_limit, From a9accd7535ae38f165121b8caf7a6f9593d04ccc Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 11:10:42 -0700 Subject: [PATCH 09/29] Support dual post solve option --- .../cuopt/linear_programming/pdlp/solver_settings.hpp | 1 + cpp/src/mip/presolve/third_party_presolve.cpp | 5 ++++- docs/cuopt/source/lp-milp-settings.rst | 5 +++++ .../cuopt/linear_programming/solver/solver_parameters.pyx | 2 ++ .../utils/linear_programming/data_definition.py | 8 ++++++++ .../cuopt_server/utils/linear_programming/solver.py | 6 ++++++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp index f4e515288..36c80244f 100644 --- a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp @@ -210,6 +210,7 @@ class pdlp_solver_settings_t { bool save_best_primal_so_far{false}; bool first_primal_feasible{false}; bool presolve{false}; + bool dual_postsolve{true}; method_t method{method_t::Concurrent}; // For concurrent termination std::atomic* concurrent_halt; diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 7dd964a40..a35114308 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -345,6 +345,8 @@ void set_presolve_methods(papilo::Presolve& presolver, presolver.addPresolveMethod(uptr(new papilo::SimpleSubstitution())); presolver.addPresolveMethod(uptr(new papilo::Sparsify())); presolver.addPresolveMethod(uptr(new papilo::Substitution())); + } else { + CUOPT_LOG_INFO("Disabling the presolver methods that do not support dual postsolve"); } } @@ -374,7 +376,8 @@ void set_presolve_parameters(papilo::Presolve& presolver, // produce any reductions, the algorithm stops. To avoid stopping the algorithm, we set a // minimum badge size to a huge value. The time limit makes sure that we exit if it takes too // long - params.setParameter("probing.minbadgesize", ncols / 2); + int min_badgesize = std::max(ncols / 2, 32); + params.setParameter("probing.minbadgesize", min_badgesize); params.setParameter("cliquemerging.enabled", true); params.setParameter("cliquemerging.maxcalls", 50); } diff --git a/docs/cuopt/source/lp-milp-settings.rst b/docs/cuopt/source/lp-milp-settings.rst index b97f31f16..2900c6f33 100644 --- a/docs/cuopt/source/lp-milp-settings.rst +++ b/docs/cuopt/source/lp-milp-settings.rst @@ -65,6 +65,11 @@ Presolve ^^^^^^^^ ``CUOPT_PRESOLVE`` controls whether presolve is enabled. Presolve can reduce problem size and improve solve time. Enabled by default for MIP, disabled by default for LP. +Dual Postsolve +^^^^^^^^ +``CUOPT_DUAL_POSTSOLVE`` controls whether dual postsolve is enabled. Disabling dual postsolve can improve solve time at the expense of not having +access to the dual solution. Enabled by default for LP when presolve is enabled. This is not relevant for MIP problems. + Linear Programming ------------------ diff --git a/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx b/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx index 413ec6e75..d033a4201 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx +++ b/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx @@ -60,6 +60,7 @@ cdef extern from "cuopt/linear_programming/constants.h": # noqa cdef const char* c_CUOPT_LOG_TO_CONSOLE "CUOPT_LOG_TO_CONSOLE" # noqa cdef const char* c_CUOPT_CROSSOVER "CUOPT_CROSSOVER" # noqa cdef const char* c_CUOPT_PRESOLVE "CUOPT_PRESOLVE" # noqa + cdef const char* c_CUOPT_DUAL_POSTSOLVE "CUOPT_DUALPOSTSOLVE" # noqa cdef const char* c_CUOPT_MIP_ABSOLUTE_TOLERANCE "CUOPT_MIP_ABSOLUTE_TOLERANCE" # noqa cdef const char* c_CUOPT_MIP_RELATIVE_TOLERANCE "CUOPT_MIP_RELATIVE_TOLERANCE" # noqa cdef const char* c_CUOPT_MIP_INTEGRALITY_TOLERANCE "CUOPT_MIP_INTEGRALITY_TOLERANCE" # noqa @@ -94,6 +95,7 @@ CUOPT_LOG_FILE = c_CUOPT_LOG_FILE.decode('utf-8') # noqa CUOPT_LOG_TO_CONSOLE = c_CUOPT_LOG_TO_CONSOLE.decode('utf-8') # noqa CUOPT_CROSSOVER = c_CUOPT_CROSSOVER.decode('utf-8') # noqa CUOPT_PRESOLVE = c_CUOPT_PRESOLVE.decode('utf-8') # noqa +CUOPT_DUALPOSTSOLVE = c_CUOPT_DUALPOSTSOLVE.decode('utf-8') # noqa CUOPT_MIP_ABSOLUTE_TOLERANCE = c_CUOPT_MIP_ABSOLUTE_TOLERANCE.decode('utf-8') # noqa CUOPT_MIP_RELATIVE_TOLERANCE = c_CUOPT_MIP_RELATIVE_TOLERANCE.decode('utf-8') # noqa CUOPT_MIP_INTEGRALITY_TOLERANCE = c_CUOPT_MIP_INTEGRALITY_TOLERANCE.decode('utf-8') # noqa 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 30dcf8695..20b660b91 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 @@ -543,6 +543,14 @@ class SolverConfig(StrictModel): "Presolve can reduce problem size and improve solve time. " "Default is True for MIP problems and False for LP problems.", ) + dual_postsolve: Optional[bool] = Field( + default=None, + description="Set True to enable dual postsolve, False to disable dual postsolve. " # noqa + "Dual postsolve can improve solve time at the expense of not having " + "access to the dual solution. " + "Default is True for LP problems when presolve is enabled. " + "This is not relevant for MIP problems.", + ) log_to_console: Optional[bool] = Field( default=True, description="Set True to write logs to console, False to " 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 05ef2e2ad..11e94ec38 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py @@ -27,6 +27,7 @@ CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, CUOPT_CROSSOVER, CUOPT_DUAL_INFEASIBLE_TOLERANCE, + CUOPT_DUAL_POSTSOLVE, CUOPT_FIRST_PRIMAL_FEASIBLE, CUOPT_INFEASIBILITY_DETECTION, CUOPT_ITERATION_LIMIT, @@ -398,6 +399,11 @@ def is_mip(var_types): CUOPT_PRESOLVE, solver_config.presolve ) + if solver_config.dual_postsolve is not None: + solver_settings.set_parameter( + CUOPT_DUAL_POSTSOLVE, solver_config.dual_postsolve + ) + if solver_config.log_to_console is not None: solver_settings.set_parameter( CUOPT_LOG_TO_CONSOLE, solver_config.log_to_console From 59c8aa7464ed821a5c76545b111e86a5e112a2bb Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 12:14:33 -0700 Subject: [PATCH 10/29] Fix typo --- .../cuopt/linear_programming/solver/solver_parameters.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx b/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx index d033a4201..6eac557ff 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx +++ b/python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx @@ -60,7 +60,7 @@ cdef extern from "cuopt/linear_programming/constants.h": # noqa cdef const char* c_CUOPT_LOG_TO_CONSOLE "CUOPT_LOG_TO_CONSOLE" # noqa cdef const char* c_CUOPT_CROSSOVER "CUOPT_CROSSOVER" # noqa cdef const char* c_CUOPT_PRESOLVE "CUOPT_PRESOLVE" # noqa - cdef const char* c_CUOPT_DUAL_POSTSOLVE "CUOPT_DUALPOSTSOLVE" # noqa + cdef const char* c_CUOPT_DUAL_POSTSOLVE "CUOPT_DUAL_POSTSOLVE" # noqa cdef const char* c_CUOPT_MIP_ABSOLUTE_TOLERANCE "CUOPT_MIP_ABSOLUTE_TOLERANCE" # noqa cdef const char* c_CUOPT_MIP_RELATIVE_TOLERANCE "CUOPT_MIP_RELATIVE_TOLERANCE" # noqa cdef const char* c_CUOPT_MIP_INTEGRALITY_TOLERANCE "CUOPT_MIP_INTEGRALITY_TOLERANCE" # noqa @@ -95,7 +95,7 @@ CUOPT_LOG_FILE = c_CUOPT_LOG_FILE.decode('utf-8') # noqa CUOPT_LOG_TO_CONSOLE = c_CUOPT_LOG_TO_CONSOLE.decode('utf-8') # noqa CUOPT_CROSSOVER = c_CUOPT_CROSSOVER.decode('utf-8') # noqa CUOPT_PRESOLVE = c_CUOPT_PRESOLVE.decode('utf-8') # noqa -CUOPT_DUALPOSTSOLVE = c_CUOPT_DUALPOSTSOLVE.decode('utf-8') # noqa +CUOPT_DUAL_POSTSOLVE = c_CUOPT_DUAL_POSTSOLVE.decode('utf-8') # noqa CUOPT_MIP_ABSOLUTE_TOLERANCE = c_CUOPT_MIP_ABSOLUTE_TOLERANCE.decode('utf-8') # noqa CUOPT_MIP_RELATIVE_TOLERANCE = c_CUOPT_MIP_RELATIVE_TOLERANCE.decode('utf-8') # noqa CUOPT_MIP_INTEGRALITY_TOLERANCE = c_CUOPT_MIP_INTEGRALITY_TOLERANCE.decode('utf-8') # noqa From d3f37b706319f801e209cc664eb17762a63354c6 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 12:52:16 -0700 Subject: [PATCH 11/29] avoid building papilo --- cpp/CMakeLists.txt | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index bd52a4666..536705d66 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -178,8 +178,12 @@ else() find_package(RAFT REQUIRED) endif() -FetchContent_Declare( - papilo +# Pull in papilo from a specific commit, since cuOpt uses it as header only, +# we do not need to build it. +include(ExternalProject) + +ExternalProject_Add( + papilo-source GIT_REPOSITORY "https://github.com/scipopt/papilo.git" # We would want to get the main branch. However, the main branch # does not have some of the presolvers and settings that we need @@ -189,15 +193,18 @@ FetchContent_Declare( #we can switch to the main branch. GIT_TAG "34a40781fa14f8870cb6368cffb6c0eda2f47511" GIT_PROGRESS TRUE - SYSTEM + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + UPDATE_COMMAND "" + PATCH_COMMAND "" ) -find_package(TBB REQUIRED) -set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") -set(PAPILO_NO_BINARIES ON) -option(LUSOL "Disable LUSOL" OFF) +# Get the source directory +ExternalProject_Get_Property(papilo-source SOURCE_DIR) +set(papilo_SOURCE_DIR ${SOURCE_DIR}) -FetchContent_MakeAvailable(papilo) +find_package(TBB REQUIRED) include(${rapids-cmake-dir}/cpm/rapids_logger.cmake) # generate logging macros @@ -249,7 +256,7 @@ add_library(cuopt::cuopt ALIAS cuopt) # ################################################################################################## # - include paths --------------------------------------------------------------------------------- -target_include_directories(cuopt SYSTEM PRIVATE "${papilo_SOURCE_DIR}/src" "${papilo_BINARY_DIR}") +target_include_directories(cuopt SYSTEM PRIVATE "${papilo_SOURCE_DIR}/src") target_include_directories(cuopt PRIVATE @@ -424,7 +431,7 @@ if(NOT BUILD_LP_ONLY) cuopt OpenMP::OpenMP_CXX PRIVATE - papilo-core + #papilo-core ) set_property(TARGET cuopt_cli PROPERTY INSTALL_RPATH "$ORIGIN/../${lib_dir}") @@ -448,7 +455,7 @@ if(BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY) cuopt OpenMP::OpenMP_CXX PRIVATE - papilo-core + #papilo-core ) endif() @@ -464,7 +471,7 @@ if(BUILD_LP_BENCHMARKS) cuopt OpenMP::OpenMP_CXX PRIVATE - papilo-core + #papilo-core ) endif() From d7a730a5ebbc7a1f9e0574969a5f02e0713341d6 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 12:58:43 -0700 Subject: [PATCH 12/29] style fixes --- cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 536705d66..5a0424485 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -178,7 +178,7 @@ else() find_package(RAFT REQUIRED) endif() -# Pull in papilo from a specific commit, since cuOpt uses it as header only, +# Pull in papilo from a specific commit, since cuOpt uses it as header only, # we do not need to build it. include(ExternalProject) From 8072747bf4f758fa992a787fe293b449c4d92ad1 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 13:00:13 -0700 Subject: [PATCH 13/29] remove linking to papilo --- cpp/CMakeLists.txt | 3 --- cpp/tests/CMakeLists.txt | 1 - cpp/tests/examples/routing/CMakeLists.txt | 1 - 3 files changed, 5 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 5a0424485..311159646 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -431,7 +431,6 @@ if(NOT BUILD_LP_ONLY) cuopt OpenMP::OpenMP_CXX PRIVATE - #papilo-core ) set_property(TARGET cuopt_cli PROPERTY INSTALL_RPATH "$ORIGIN/../${lib_dir}") @@ -455,7 +454,6 @@ if(BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY) cuopt OpenMP::OpenMP_CXX PRIVATE - #papilo-core ) endif() @@ -471,7 +469,6 @@ if(BUILD_LP_BENCHMARKS) cuopt OpenMP::OpenMP_CXX PRIVATE - #papilo-core ) endif() diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 691b6992e..ff6da8894 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -34,7 +34,6 @@ if(BUILD_TESTS) cuopt GTest::gmock GTest::gtest - papilo-core ) endif() diff --git a/cpp/tests/examples/routing/CMakeLists.txt b/cpp/tests/examples/routing/CMakeLists.txt index 3b29d259d..52bc3e682 100644 --- a/cpp/tests/examples/routing/CMakeLists.txt +++ b/cpp/tests/examples/routing/CMakeLists.txt @@ -48,6 +48,5 @@ foreach(target cuopt cuopttestutils OpenMP::OpenMP_CXX - papilo-core ) endforeach() From 9af33a1c14493b811128bdcb5429e2c55aa2e481 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 13:06:52 -0700 Subject: [PATCH 14/29] add docs --- docs/cuopt/source/lp-features.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/cuopt/source/lp-features.rst b/docs/cuopt/source/lp-features.rst index b89ace5d3..c5d589907 100644 --- a/docs/cuopt/source/lp-features.rst +++ b/docs/cuopt/source/lp-features.rst @@ -78,6 +78,13 @@ Crossover Crossover allows you to obtain a high-quality basic solution from the results of a PDLP solve. More details can be found :ref:`here `. +Presolve +-------- + +Presolve procedure is applied to the problem before the solver is called. It can be used to reduce the problem size and improve solve time. It is enabled by default for MIP problems, and disabled by default for LP problems. +Furthermore, for LP problems, when the dual solution is not needed, additional presolve procedures can be applied to further improve solve times. This is achived by turned off dual postsolve. + + Logging ------- From 0184f85d0176c07d05a783c47b4380d0f83f53f4 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 13:33:16 -0700 Subject: [PATCH 15/29] add presolve option test --- python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py | 4 ++++ 1 file changed, 4 insertions(+) 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 ed0d04ec1..0c601cb70 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -25,11 +25,13 @@ CUOPT_ABSOLUTE_GAP_TOLERANCE, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, CUOPT_DUAL_INFEASIBLE_TOLERANCE, + CUOPT_DUAL_POSTSOLVE, CUOPT_INFEASIBILITY_DETECTION, CUOPT_ITERATION_LIMIT, CUOPT_METHOD, CUOPT_MIP_HEURISTICS_ONLY, CUOPT_PDLP_SOLVER_MODE, + CUOPT_PRESOLVE, CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, CUOPT_RELATIVE_DUAL_TOLERANCE, CUOPT_RELATIVE_GAP_TOLERANCE, @@ -598,6 +600,8 @@ def test_dual_simplex(): settings = solver_settings.SolverSettings() settings.set_parameter(CUOPT_METHOD, SolverMethod.DualSimplex) + settings.set_parameter(CUOPT_PRESOLVE, True) + settings.set_parameter(CUOPT_DUAL_POSTSOLVE, False) solution = solver.Solve(data_model_obj, settings) From 98740543032b3db3dcb84b7aa995dfaf8ed73d43 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Fri, 26 Sep 2025 13:38:03 -0700 Subject: [PATCH 16/29] add presolve and dual post solve in service settings --- python/cuopt_server/cuopt_server/tests/test_lp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index 8fc85aa3a..2382aa831 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -92,6 +92,8 @@ def get_std_data_for_lp(): "primal_infeasible": 0.00000001, "dual_infeasible": 0.00000001, "integrality_tolerance": 0.00001, + "presolve": True, + "dual_postsolve": True, }, }, } From 6ef3615ffd5596785282e6b71df1a2c4db40eb2d Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Mon, 29 Sep 2025 07:37:48 -0700 Subject: [PATCH 17/29] Revert changes relating to building papilo --- cpp/CMakeLists.txt | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 311159646..5028737a3 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -178,12 +178,8 @@ else() find_package(RAFT REQUIRED) endif() -# Pull in papilo from a specific commit, since cuOpt uses it as header only, -# we do not need to build it. -include(ExternalProject) - -ExternalProject_Add( - papilo-source +FetchContent_Declare( + papilo GIT_REPOSITORY "https://github.com/scipopt/papilo.git" # We would want to get the main branch. However, the main branch # does not have some of the presolvers and settings that we need @@ -193,18 +189,15 @@ ExternalProject_Add( #we can switch to the main branch. GIT_TAG "34a40781fa14f8870cb6368cffb6c0eda2f47511" GIT_PROGRESS TRUE - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - UPDATE_COMMAND "" - PATCH_COMMAND "" + SYSTEM ) -# Get the source directory -ExternalProject_Get_Property(papilo-source SOURCE_DIR) -set(papilo_SOURCE_DIR ${SOURCE_DIR}) - find_package(TBB REQUIRED) +set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") +set(PAPILO_NO_BINARIES ON) +option(LUSOL "Disable LUSOL" OFF) + +FetchContent_MakeAvailable(papilo) include(${rapids-cmake-dir}/cpm/rapids_logger.cmake) # generate logging macros From 32d02bebb7ddcaab01dd2f0988ac9b664ac197c9 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Mon, 29 Sep 2025 08:08:17 -0700 Subject: [PATCH 18/29] include binary directories --- cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 5028737a3..b69fdbff7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -249,7 +249,7 @@ add_library(cuopt::cuopt ALIAS cuopt) # ################################################################################################## # - include paths --------------------------------------------------------------------------------- -target_include_directories(cuopt SYSTEM PRIVATE "${papilo_SOURCE_DIR}/src") +target_include_directories(cuopt SYSTEM PRIVATE "${papilo_SOURCE_DIR}/src" "${papilo_BINARY_DIR}") target_include_directories(cuopt PRIVATE From b4f41ab0e50df79b30511658ed7ceb5755124bd7 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 29 Sep 2025 17:11:14 -0500 Subject: [PATCH 19/29] Fix boost issue in wheel --- python/libcuopt/CMakeLists.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index b1cf980b7..aa6206661 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -43,6 +43,26 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(argparse) +# Force CMake to use the system Modules first +set(CMAKE_MODULE_PATH + "/usr/share/cmake/Modules" + ${CMAKE_MODULE_PATH} +) + +# Use module mode (system boost-devel has no BoostConfig.cmake) +set(Boost_NO_BOOST_CMAKE ON CACHE BOOL "" FORCE) +set(BOOST_ROOT "/usr" CACHE PATH "" FORCE) +set(BOOST_INCLUDEDIR "/usr/include" CACHE PATH "" FORCE) +set(BOOST_LIBRARYDIR "/usr/lib64" CACHE PATH "" FORCE) + + +find_package(Boost 1.65 REQUIRED COMPONENTS system filesystem thread) +if(Boost_FOUND) + message(STATUS "Found Boost ${Boost_VERSION} in ${Boost_INCLUDE_DIRS}") +else() + message(FATAL_ERROR "Boost not found. Please install boost-devel.") +endif() + set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") set(PAPILO_NO_BINARIES ON) option(LUSOL "Disable LUSOL" OFF) From 547ccf219286524e3ca08f629b52f1808bd2dd37 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 29 Sep 2025 23:08:42 -0500 Subject: [PATCH 20/29] fix path --- python/libcuopt/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index aa6206661..a152e60a2 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -45,15 +45,15 @@ FetchContent_MakeAvailable(argparse) # Force CMake to use the system Modules first set(CMAKE_MODULE_PATH - "/usr/share/cmake/Modules" - ${CMAKE_MODULE_PATH} + "${CMAKE_MODULE_PATH}" + "/usr/share/cmake/Modules" # system modules ) # Use module mode (system boost-devel has no BoostConfig.cmake) -set(Boost_NO_BOOST_CMAKE ON CACHE BOOL "" FORCE) -set(BOOST_ROOT "/usr" CACHE PATH "" FORCE) -set(BOOST_INCLUDEDIR "/usr/include" CACHE PATH "" FORCE) -set(BOOST_LIBRARYDIR "/usr/lib64" CACHE PATH "" FORCE) +#set(Boost_NO_BOOST_CMAKE ON CACHE BOOL "" FORCE) +#set(BOOST_ROOT "/usr" CACHE PATH "" FORCE) +#set(BOOST_INCLUDEDIR "/usr/include" CACHE PATH "" FORCE) +#set(BOOST_LIBRARYDIR "/usr/lib64" CACHE PATH "" FORCE) find_package(Boost 1.65 REQUIRED COMPONENTS system filesystem thread) From 5772e9a5e9afcbe5d094d0c60d8b4d5b42f40886 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 30 Sep 2025 13:50:57 -0500 Subject: [PATCH 21/29] push 1.78 boost change --- ci/utils/install_boost_tbb.sh | 4 +++- python/libcuopt/CMakeLists.txt | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ci/utils/install_boost_tbb.sh b/ci/utils/install_boost_tbb.sh index c3e1eb0bd..10788f399 100644 --- a/ci/utils/install_boost_tbb.sh +++ b/ci/utils/install_boost_tbb.sh @@ -22,7 +22,9 @@ if [ -f /etc/os-release ]; then . /etc/os-release if [[ "$ID" == "rocky" ]]; then echo "Detected Rocky Linux. Installing Boost and TBB via dnf..." - dnf install -y boost-devel tbb-devel + + dnf install -y epel-release + dnf install -y boost1.78-devel tbb-devel if [[ "$(uname -m)" == "x86_64" ]]; then dnf install -y gcc-toolset-14-libquadmath-devel fi diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index a152e60a2..dc18f91e9 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -44,10 +44,10 @@ FetchContent_Declare( FetchContent_MakeAvailable(argparse) # Force CMake to use the system Modules first -set(CMAKE_MODULE_PATH - "${CMAKE_MODULE_PATH}" - "/usr/share/cmake/Modules" # system modules -) +#set(CMAKE_MODULE_PATH +# "${CMAKE_MODULE_PATH}" +# "/usr/share/cmake/Modules" # system modules +#) # Use module mode (system boost-devel has no BoostConfig.cmake) #set(Boost_NO_BOOST_CMAKE ON CACHE BOOL "" FORCE) @@ -56,13 +56,15 @@ set(CMAKE_MODULE_PATH #set(BOOST_LIBRARYDIR "/usr/lib64" CACHE PATH "" FORCE) -find_package(Boost 1.65 REQUIRED COMPONENTS system filesystem thread) +find_package(Boost 1.65 REQUIRED) if(Boost_FOUND) message(STATUS "Found Boost ${Boost_VERSION} in ${Boost_INCLUDE_DIRS}") else() message(FATAL_ERROR "Boost not found. Please install boost-devel.") endif() +include_directories(${Boost_INCLUDE_DIRS}) + set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") set(PAPILO_NO_BINARIES ON) option(LUSOL "Disable LUSOL" OFF) From 6d5b77346b502533c68d5864e66d41bec47a519c Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 30 Sep 2025 12:07:25 -0700 Subject: [PATCH 22/29] Fix compilation error --- cpp/src/linear_programming/utils.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index ca77146f6..2b2211b27 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -202,7 +202,7 @@ void inline compute_sum_bounds(const rmm::device_uvector& constraint_lower_ rmm::cuda_stream_view stream_view) { rmm::device_buffer d_temp_storage; - size_t bytes; + size_t bytes = 0; auto main_op = [] HD(const thrust::tuple t) { const f_t lower = thrust::get<0>(t); const f_t upper = thrust::get<1>(t); From b339605401952ed01a28f5bebc739d3b9c745d2b Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 30 Sep 2025 13:11:30 -0700 Subject: [PATCH 23/29] fix a bug in setting presolve time limit --- cpp/src/linear_programming/solve.cu | 2 +- cpp/src/mip/solve.cu | 2 +- python/cuopt_server/cuopt_server/tests/test_lp.py | 14 -------------- .../cuopt_server/cuopt_server/tests/utils/utils.py | 2 +- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 36c950e22..337b29ec9 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -700,7 +700,7 @@ optimization_problem_solution_t solve_lp(optimization_problem_t>(); auto [reduced_problem, feasible] = presolver->apply(op_problem, diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 5c3729c33..547abee49 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -202,7 +202,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, if (run_presolve) { // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. - const double presolve_time_limit = std::max(0.1 * time_limit, 60.0); + const double presolve_time_limit = std::min(0.1 * time_limit, 60.0); const bool dual_postsolve = false; presolver = std::make_unique>(); auto [reduced_op_problem, feasible] = diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index 2382aa831..83afbe6c8 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -81,20 +81,6 @@ def get_std_data_for_lp(): "variable_names": ["x", "y"], "solver_config": { "time_limit": 5, - "tolerances": { - "optimality": 0.0001, - "absolute_primal": 0.0001, - "absolute_dual": 0.0001, - "absolute_gap": 0.0001, - "relative_primal": 0.0001, - "relative_dual": 0.0001, - "relative_gap": 0.0001, - "primal_infeasible": 0.00000001, - "dual_infeasible": 0.00000001, - "integrality_tolerance": 0.00001, - "presolve": True, - "dual_postsolve": True, - }, }, } diff --git a/python/cuopt_server/cuopt_server/tests/utils/utils.py b/python/cuopt_server/cuopt_server/tests/utils/utils.py index 05c5a0968..a34e0d9bf 100644 --- a/python/cuopt_server/cuopt_server/tests/utils/utils.py +++ b/python/cuopt_server/cuopt_server/tests/utils/utils.py @@ -331,7 +331,7 @@ def cuoptproc(request): global cuoptmain env = { "CUOPT_SERVER_IP": "0.0.0.0", - "CUOPT_SERVER_PORT": "5555", + "CUOPT_SERVER_PORT": "5556", "CUOPT_SERVER_LOG_LEVEL": "debug", } cuoptmain = Popen([python_path, server_script, server_module], env=env) From 9e13e0941c722fbea7417cf9f5cf8b73cad579e0 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 30 Sep 2025 15:07:26 -0700 Subject: [PATCH 24/29] Revert unintended changes --- python/cuopt_server/cuopt_server/tests/test_lp.py | 12 ++++++++++++ .../cuopt_server/cuopt_server/tests/utils/utils.py | 2 +- python/libcuopt/CMakeLists.txt | 12 ------------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index 83afbe6c8..8fc85aa3a 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -81,6 +81,18 @@ def get_std_data_for_lp(): "variable_names": ["x", "y"], "solver_config": { "time_limit": 5, + "tolerances": { + "optimality": 0.0001, + "absolute_primal": 0.0001, + "absolute_dual": 0.0001, + "absolute_gap": 0.0001, + "relative_primal": 0.0001, + "relative_dual": 0.0001, + "relative_gap": 0.0001, + "primal_infeasible": 0.00000001, + "dual_infeasible": 0.00000001, + "integrality_tolerance": 0.00001, + }, }, } diff --git a/python/cuopt_server/cuopt_server/tests/utils/utils.py b/python/cuopt_server/cuopt_server/tests/utils/utils.py index a34e0d9bf..05c5a0968 100644 --- a/python/cuopt_server/cuopt_server/tests/utils/utils.py +++ b/python/cuopt_server/cuopt_server/tests/utils/utils.py @@ -331,7 +331,7 @@ def cuoptproc(request): global cuoptmain env = { "CUOPT_SERVER_IP": "0.0.0.0", - "CUOPT_SERVER_PORT": "5556", + "CUOPT_SERVER_PORT": "5555", "CUOPT_SERVER_LOG_LEVEL": "debug", } cuoptmain = Popen([python_path, server_script, server_module], env=env) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index dc18f91e9..5cce3d029 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -43,18 +43,6 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(argparse) -# Force CMake to use the system Modules first -#set(CMAKE_MODULE_PATH -# "${CMAKE_MODULE_PATH}" -# "/usr/share/cmake/Modules" # system modules -#) - -# Use module mode (system boost-devel has no BoostConfig.cmake) -#set(Boost_NO_BOOST_CMAKE ON CACHE BOOL "" FORCE) -#set(BOOST_ROOT "/usr" CACHE PATH "" FORCE) -#set(BOOST_INCLUDEDIR "/usr/include" CACHE PATH "" FORCE) -#set(BOOST_LIBRARYDIR "/usr/lib64" CACHE PATH "" FORCE) - find_package(Boost 1.65 REQUIRED) if(Boost_FOUND) From 556d96a66af736254261603db81a4452a8ccdebd Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 30 Sep 2025 16:49:27 -0700 Subject: [PATCH 25/29] Missing imports --- .../linear_programming/solver_settings/solver_settings.py | 4 ++++ python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py | 2 ++ .../cuopt_server/utils/linear_programming/solver.py | 1 + 3 files changed, 7 insertions(+) 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 78f37b489..475a597a4 100644 --- a/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py +++ b/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py @@ -27,6 +27,8 @@ CUOPT_LOG_FILE, CUOPT_LOG_TO_CONSOLE, CUOPT_METHOD, + CUOPT_PRESOLVE, + CUOPT_DUAL_POSTSOLVE, CUOPT_MIP_ABSOLUTE_GAP, CUOPT_MIP_ABSOLUTE_TOLERANCE, CUOPT_MIP_HEURISTICS_ONLY, @@ -376,6 +378,8 @@ def toDict(self): "iteration_limit": self.get_parameter(CUOPT_ITERATION_LIMIT), "pdlp_solver_mode": self.get_parameter(CUOPT_PDLP_SOLVER_MODE), "method": self.get_parameter(CUOPT_METHOD), + "presolve": self.get_parameter(CUOPT_PRESOLVE), + "dual_postsolve": self.get_parameter(CUOPT_DUAL_POSTSOLVE), "mip_scaling": self.get_parameter(CUOPT_MIP_SCALING), "mip_heuristics_only": self.get_parameter( CUOPT_MIP_HEURISTICS_ONLY 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 a45d46bd0..157c62936 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -29,6 +29,8 @@ CUOPT_INFEASIBILITY_DETECTION, CUOPT_ITERATION_LIMIT, CUOPT_METHOD, + CUOPT_PRESOLVE, + CUOPT_DUAL_POSTSOLVE, CUOPT_MIP_HEURISTICS_ONLY, CUOPT_PDLP_SOLVER_MODE, CUOPT_PRESOLVE, 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 11e94ec38..81bf37273 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py @@ -45,6 +45,7 @@ CUOPT_PDLP_SOLVER_MODE, CUOPT_PER_CONSTRAINT_RESIDUAL, CUOPT_PRESOLVE, + CUOPT_DUAL_POSTSOLVE, CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, CUOPT_RELATIVE_DUAL_TOLERANCE, CUOPT_RELATIVE_GAP_TOLERANCE, From 1c1bdddf804bb5d5e8ec22ed7d8eeb4dd7b6c979 Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 30 Sep 2025 17:53:49 -0700 Subject: [PATCH 26/29] Fix style checks --- .../linear_programming/solver_settings/solver_settings.py | 4 ++-- python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py | 2 -- .../cuopt_server/utils/linear_programming/solver.py | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) 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 475a597a4..a14230a37 100644 --- a/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py +++ b/python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py @@ -21,14 +21,13 @@ CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, CUOPT_CROSSOVER, CUOPT_DUAL_INFEASIBLE_TOLERANCE, + CUOPT_DUAL_POSTSOLVE, CUOPT_FIRST_PRIMAL_FEASIBLE, CUOPT_INFEASIBILITY_DETECTION, CUOPT_ITERATION_LIMIT, CUOPT_LOG_FILE, CUOPT_LOG_TO_CONSOLE, CUOPT_METHOD, - CUOPT_PRESOLVE, - CUOPT_DUAL_POSTSOLVE, CUOPT_MIP_ABSOLUTE_GAP, CUOPT_MIP_ABSOLUTE_TOLERANCE, CUOPT_MIP_HEURISTICS_ONLY, @@ -39,6 +38,7 @@ CUOPT_NUM_CPU_THREADS, CUOPT_PDLP_SOLVER_MODE, CUOPT_PER_CONSTRAINT_RESIDUAL, + CUOPT_PRESOLVE, CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, CUOPT_RELATIVE_DUAL_TOLERANCE, CUOPT_RELATIVE_GAP_TOLERANCE, 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 157c62936..a45d46bd0 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -29,8 +29,6 @@ CUOPT_INFEASIBILITY_DETECTION, CUOPT_ITERATION_LIMIT, CUOPT_METHOD, - CUOPT_PRESOLVE, - CUOPT_DUAL_POSTSOLVE, CUOPT_MIP_HEURISTICS_ONLY, CUOPT_PDLP_SOLVER_MODE, CUOPT_PRESOLVE, 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 81bf37273..11e94ec38 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py @@ -45,7 +45,6 @@ CUOPT_PDLP_SOLVER_MODE, CUOPT_PER_CONSTRAINT_RESIDUAL, CUOPT_PRESOLVE, - CUOPT_DUAL_POSTSOLVE, CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, CUOPT_RELATIVE_DUAL_TOLERANCE, CUOPT_RELATIVE_GAP_TOLERANCE, From 2517554a22449caa04ff41ebf0a96129dd8ec76f Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 30 Sep 2025 20:17:31 -0700 Subject: [PATCH 27/29] Fix doc style --- docs/cuopt/source/lp-milp-settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cuopt/source/lp-milp-settings.rst b/docs/cuopt/source/lp-milp-settings.rst index 6e53c1cd7..258695419 100644 --- a/docs/cuopt/source/lp-milp-settings.rst +++ b/docs/cuopt/source/lp-milp-settings.rst @@ -66,7 +66,7 @@ Presolve ``CUOPT_PRESOLVE`` controls whether presolve is enabled. Presolve can reduce problem size and improve solve time. Enabled by default for MIP, disabled by default for LP. Dual Postsolve -^^^^^^^^ +^^^^^^^^^^^^^^ ``CUOPT_DUAL_POSTSOLVE`` controls whether dual postsolve is enabled. Disabling dual postsolve can improve solve time at the expense of not having access to the dual solution. Enabled by default for LP when presolve is enabled. This is not relevant for MIP problems. From 86844649000a20550d62a219834e5121eb86224a Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Wed, 1 Oct 2025 03:40:46 -0700 Subject: [PATCH 28/29] Remove checks --- python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py | 3 --- 1 file changed, 3 deletions(-) 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 a45d46bd0..7d3c550e2 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -166,9 +166,6 @@ def test_time_limit_solver(): assert solution.get_termination_status() == LPTerminationStatus.TimeLimit # Check that around 200 ms has passed with some tolerance assert solution.get_solve_time() <= (time_limit_seconds * 10) - # Not all 0 - assert solution.get_primal_objective() != 0.0 - assert np.any(solution.get_primal_solution()) def test_set_get_fields(): From d559a3a69fc2690695a8e94d068d40e6df75898c Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Thu, 2 Oct 2025 07:09:19 -0700 Subject: [PATCH 29/29] disable cvxpy testing for CI --- ci/test_wheel_cuopt.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ci/test_wheel_cuopt.sh b/ci/test_wheel_cuopt.sh index 02f1d64ce..1b37ed020 100755 --- a/ci/test_wheel_cuopt.sh +++ b/ci/test_wheel_cuopt.sh @@ -73,10 +73,8 @@ timeout 10m bash ./python/libcuopt/libcuopt/tests/test_cli.sh # Run Python tests RAPIDS_DATASET_ROOT_DIR=./datasets timeout 30m python -m pytest --verbose --capture=no ./python/cuopt/cuopt/tests/ -# run cvxpy integration tests -./ci/thirdparty-testing/run_cvxpy_tests.sh - -# run jump tests for only nightly builds +# run jump tests and cvxpy integration tests for only nightly builds if [[ "${RAPIDS_BUILD_TYPE}" == "nightly" ]]; then ./ci/thirdparty-testing/run_jump_tests.sh + ./ci/thirdparty-testing/run_cvxpy_tests.sh fi