From ef660460fd7cb40e4136b0cbb3656fd875e5721e Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Tue, 2 Sep 2025 14:23:43 -0700 Subject: [PATCH 01/12] Enable parallelism for root node presolve --- ci/build_wheel_libcuopt.sh | 4 +- ci/utils/install_boost.sh | 40 ------------------- .../all_cuda-129_arch-aarch64.yaml | 1 + .../all_cuda-129_arch-x86_64.yaml | 1 + conda/recipes/libcuopt/recipe.yaml | 2 + cpp/CMakeLists.txt | 2 +- cpp/src/mip/presolve/third_party_presolve.cpp | 5 ++- dependencies.yaml | 1 + python/libcuopt/CMakeLists.txt | 2 +- 9 files changed, 12 insertions(+), 46 deletions(-) delete mode 100644 ci/utils/install_boost.sh diff --git a/ci/build_wheel_libcuopt.sh b/ci/build_wheel_libcuopt.sh index 7651b52f8c..2b3a8b0fb5 100755 --- a/ci/build_wheel_libcuopt.sh +++ b/ci/build_wheel_libcuopt.sh @@ -21,8 +21,8 @@ source rapids-init-pip package_name="libcuopt" package_dir="python/libcuopt" -# Install Boost -bash ci/utils/install_boost.sh +# Install Boost and TBB +bash ci/utils/install_boost_tbb.sh export SKBUILD_CMAKE_ARGS="-DCUOPT_BUILD_WHEELS=ON;-DDISABLE_DEPRECATION_WARNING=ON" diff --git a/ci/utils/install_boost.sh b/ci/utils/install_boost.sh deleted file mode 100644 index bd85d41e90..0000000000 --- a/ci/utils/install_boost.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -euo pipefail - -# Install Boost -if [ -f /etc/os-release ]; then - . /etc/os-release - if [[ "$ID" == "rocky" ]]; then - echo "Detected Rocky Linux. Installing Boost via dnf..." - dnf install -y boost-devel - if [[ "$(uname -m)" == "x86_64" ]]; then - dnf install -y gcc-toolset-14-libquadmath-devel - fi - elif [[ "$ID" == "ubuntu" ]]; then - echo "Detected Ubuntu. Installing Boost via apt..." - apt-get update - apt-get install -y libboost-dev - else - echo "Unknown OS: $ID. Please install Boost development libraries manually." - exit 1 - fi -else - echo "/etc/os-release not found. Cannot determine OS. Please install Boost development libraries manually." - exit 1 -fi diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 9f9a642c26..3e0c54a008 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -78,6 +78,7 @@ dependencies: - sphinxcontrib-openapi - sphinxcontrib-websupport - sysroot_linux-aarch64==2.28 +- tbb-devel - uvicorn==0.34.* - pip: - nvidia_sphinx_theme diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index f2a73c08a4..3c05263606 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -78,6 +78,7 @@ dependencies: - sphinxcontrib-openapi - sphinxcontrib-websupport - sysroot_linux-64==2.28 +- tbb-devel - uvicorn==0.34.* - pip: - nvidia_sphinx_theme diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 9924107d27..74c42871c7 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -57,6 +57,7 @@ cache: - cuda-version =${{ cuda_version }} - cmake ${{ cmake_version }} - ninja + - tbb-devel host: - cpp-argparse - cuda-version =${{ cuda_version }} @@ -70,6 +71,7 @@ cache: - libcusparse-dev - cuda-cudart-dev - boost + - tbb-devel outputs: - package: diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 76c856e0e5..0814a72ccb 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -169,7 +169,7 @@ FetchContent_Declare( ) set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") -set(TBB OFF CACHE BOOL "Disable TBB") +# set(TBB OFF CACHE BOOL "Disable TBB") set(PAPILO_NO_BINARIES ON) option(LUSOL "Disable LUSOL" OFF) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 7c9867d0e7..c44b071439 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -306,7 +306,8 @@ void set_presolve_options(papilo::Presolve& presolver, f_t relative_tolerance, double time_limit) { - presolver.getPresolveOptions().tlim = time_limit; + presolver.getPresolveOptions().tlim = time_limit; + presolver.getPresolveOptions().threads = 0; } template @@ -334,7 +335,7 @@ std::pair, bool> third_party_presolve_t Date: Tue, 2 Sep 2025 14:25:26 -0700 Subject: [PATCH 02/12] Add missing file --- ci/utils/install_boost_tbb.sh | 40 +++++++++++++++++++ cpp/CMakeLists.txt | 1 - cpp/src/mip/presolve/third_party_presolve.cpp | 16 ++++---- cpp/src/mip/presolve/third_party_presolve.hpp | 3 +- cpp/src/mip/solve.cu | 1 + python/libcuopt/CMakeLists.txt | 1 - 6 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 ci/utils/install_boost_tbb.sh diff --git a/ci/utils/install_boost_tbb.sh b/ci/utils/install_boost_tbb.sh new file mode 100644 index 0000000000..c3e1eb0bdc --- /dev/null +++ b/ci/utils/install_boost_tbb.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +# Install Boost and TBB +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 + if [[ "$(uname -m)" == "x86_64" ]]; then + dnf install -y gcc-toolset-14-libquadmath-devel + fi + elif [[ "$ID" == "ubuntu" ]]; then + echo "Detected Ubuntu. Installing Boost and TBB via apt..." + apt-get update + apt-get install -y libboost-dev libtbb-dev + else + echo "Unknown OS: $ID. Please install Boost development libraries manually." + exit 1 + fi +else + echo "/etc/os-release not found. Cannot determine OS. Please install Boost development libraries manually." + exit 1 +fi diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 0814a72ccb..cab5156697 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -169,7 +169,6 @@ FetchContent_Declare( ) set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") -# set(TBB OFF CACHE BOOL "Disable TBB") set(PAPILO_NO_BINARIES ON) option(LUSOL "Disable LUSOL" OFF) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index c44b071439..10dd256bfa 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -299,15 +299,16 @@ void set_presolve_methods(papilo::Presolve& presolver, problem_category_t c presolver.addPresolveMethod(uptr(new papilo::Substitution())); } -template +template void set_presolve_options(papilo::Presolve& presolver, problem_category_t category, f_t absolute_tolerance, f_t relative_tolerance, - double time_limit) + double time_limit, + i_t num_cpu_threads) { presolver.getPresolveOptions().tlim = time_limit; - presolver.getPresolveOptions().threads = 0; + presolver.getPresolveOptions().threads = num_cpu_threads; // user setting or 0 (automatic) } template @@ -316,7 +317,8 @@ std::pair, bool> third_party_presolve_t, bool> third_party_presolve_t presolver; set_presolve_methods(presolver, category); - set_presolve_options( - presolver, category, absolute_tolerance, relative_tolerance, time_limit); + set_presolve_options( + presolver, category, absolute_tolerance, relative_tolerance, time_limit, num_cpu_threads); // Disable papilo logs - // presolver.setVerbosityLevel(papilo::VerbosityLevel::kQuiet); + presolver.setVerbosityLevel(papilo::VerbosityLevel::kQuiet); auto result = presolver.apply(papilo_problem); check_presolve_status(result.status); diff --git a/cpp/src/mip/presolve/third_party_presolve.hpp b/cpp/src/mip/presolve/third_party_presolve.hpp index d7096a1a2f..5631fc12e2 100644 --- a/cpp/src/mip/presolve/third_party_presolve.hpp +++ b/cpp/src/mip/presolve/third_party_presolve.hpp @@ -31,7 +31,8 @@ class third_party_presolve_t { problem_category_t category, f_t absolute_tolerance, f_t relative_tolerance, - double time_limit); + double time_limit, + i_t num_cpu_threads = 0); void undo(rmm::device_uvector& primal_solution, rmm::device_uvector& dual_solution, diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index de84e2c236..373e107653 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -197,6 +197,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, cuopt::linear_programming::problem_category_t::MIP, settings.tolerances.absolute_tolerance, settings.tolerances.relative_tolerance, + settings.num_cpu_threads, presolve_time_limit); if (!feasible) { return mip_solution_t(mip_termination_status_t::Infeasible, diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index 5296faeb25..f4124477de 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -43,7 +43,6 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(argparse) -# set(TBB OFF CACHE BOOL "Disable TBB") set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") set(PAPILO_NO_BINARIES ON) option(LUSOL "Disable LUSOL" OFF) From 670e042998216b591520f013c562419ca45408e6 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Wed, 3 Sep 2025 16:38:56 -0700 Subject: [PATCH 03/12] Cannot compile sparse storage, missing implem --- cpp/src/mip/presolve/third_party_presolve.cpp | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 10dd256bfa..f60872fcd5 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ static bool maximize_ = false; template papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem) { + auto building_timer = cuopt::timer_t(3600); // Build papilo problem from optimization problem papilo::ProblemBuilder builder; @@ -123,20 +125,35 @@ papilo::Problem build_papilo_problem(const optimization_problem_t builder.setRowLhsAll(h_constr_lb); builder.setRowRhsAll(h_constr_ub); } + CUOPT_LOG_INFO("Setting col integrality time: %f", building_timer.elapsed_time()); - // Add constraints row by row + std::vector h_row_flags(h_constr_lb.size()); + // // Add constraints row by row for (size_t i = 0; i < h_constr_lb.size(); ++i) { - // Get row entries - i_t row_start = h_offsets[i]; - i_t row_end = h_offsets[i + 1]; - i_t num_entries = row_end - row_start; - builder.addRowEntries( - i, num_entries, h_variables.data() + row_start, h_coefficients.data() + row_start); - builder.setRowLhsInf(i, h_constr_lb[i] == -std::numeric_limits::infinity()); - builder.setRowRhsInf(i, h_constr_ub[i] == std::numeric_limits::infinity()); - if (h_constr_lb[i] == -std::numeric_limits::infinity()) { builder.setRowLhs(i, 0); } - if (h_constr_ub[i] == std::numeric_limits::infinity()) { builder.setRowRhs(i, 0); } + // // Get row entries + // i_t row_start = h_offsets[i]; + // i_t row_end = h_offsets[i + 1]; + // i_t num_entries = row_end - row_start; + + // builder.addRowEntries( + // i, num_entries, h_variables.data() + row_start, h_coefficients.data() + row_start); + // // CUOPT_LOG_INFO("Adding row entries time: %f", building_timer.elapsed_time()); + // builder.setRowLhsInf(i, h_constr_lb[i] == -std::numeric_limits::infinity()); + // builder.setRowRhsInf(i, h_constr_ub[i] == std::numeric_limits::infinity()); + if (h_constr_lb[i] == -std::numeric_limits::infinity()) { + h_row_flags[i] = papilo::RowFlag::kLhsInf; + } + if (h_constr_ub[i] == std::numeric_limits::infinity()) { + h_row_flags[i] = papilo::RowFlag::kRhsInf; + } + if (h_constr_lb[i] == -std::numeric_limits::infinity()) { + h_constr_lb[i] = 0; + } // builder.setRowLhs(i, 0); } + if (h_constr_ub[i] == std::numeric_limits::infinity()) { + h_constr_ub[i] = 0; + } // builder.setRowRhs(i, 0); } } + CUOPT_LOG_INFO("Setting row bounds time: %f", building_timer.elapsed_time()); for (size_t i = 0; i < h_var_lb.size(); ++i) { builder.setColLbInf(i, h_var_lb[i] == -std::numeric_limits::infinity()); @@ -144,7 +161,14 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (h_var_lb[i] == -std::numeric_limits::infinity()) { builder.setColLb(i, 0); } if (h_var_ub[i] == std::numeric_limits::infinity()) { builder.setColUb(i, 0); } } - return builder.build(); + CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); + auto problem = builder.build(); + auto csr_storage = papilo::SparseStorage( + h_coefficients.data(), h_variables.data(), h_offsets.data(), num_rows, num_cols, nnz); + problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); + + CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); + return problem; } template From ed18ded79a9e34af22d0788638ed9221b0f24b72 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Wed, 3 Sep 2025 16:51:18 -0700 Subject: [PATCH 04/12] Build csr with sorted entries --- cpp/src/mip/presolve/third_party_presolve.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index f60872fcd5..5320bd4e96 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -128,12 +128,16 @@ papilo::Problem build_papilo_problem(const optimization_problem_t CUOPT_LOG_INFO("Setting col integrality time: %f", building_timer.elapsed_time()); std::vector h_row_flags(h_constr_lb.size()); + std::vector> h_entries(h_constr_lb.size()); // // Add constraints row by row for (size_t i = 0; i < h_constr_lb.size(); ++i) { // // Get row entries - // i_t row_start = h_offsets[i]; - // i_t row_end = h_offsets[i + 1]; - // i_t num_entries = row_end - row_start; + i_t row_start = h_offsets[i]; + i_t row_end = h_offsets[i + 1]; + i_t num_entries = row_end - row_start; + for (size_t j = 0; j < num_entries; ++j) { + h_entries[i] = std::make_tuple(i, h_variables[row_start + j], h_coefficients[row_start + j]); + } // builder.addRowEntries( // i, num_entries, h_variables.data() + row_start, h_coefficients.data() + row_start); @@ -162,9 +166,9 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (h_var_ub[i] == std::numeric_limits::infinity()) { builder.setColUb(i, 0); } } CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); - auto problem = builder.build(); - auto csr_storage = papilo::SparseStorage( - h_coefficients.data(), h_variables.data(), h_offsets.data(), num_rows, num_cols, nnz); + auto problem = builder.build(); + auto constexpr const sorted_entries = true; + auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries); problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); From f8305287dd118e1bc77eb75f06e81bd03c362927 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Wed, 3 Sep 2025 17:15:19 -0700 Subject: [PATCH 05/12] Fix row flags --- cpp/src/mip/presolve/third_party_presolve.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 5320bd4e96..85c2ccc732 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -145,11 +145,16 @@ papilo::Problem build_papilo_problem(const optimization_problem_t // builder.setRowLhsInf(i, h_constr_lb[i] == -std::numeric_limits::infinity()); // builder.setRowRhsInf(i, h_constr_ub[i] == std::numeric_limits::infinity()); if (h_constr_lb[i] == -std::numeric_limits::infinity()) { - h_row_flags[i] = papilo::RowFlag::kLhsInf; + h_row_flags[i].set(papilo::RowFlag::kLhsInf); + } else { + h_row_flags[i].unset(papilo::RowFlag::kLhsInf); } if (h_constr_ub[i] == std::numeric_limits::infinity()) { - h_row_flags[i] = papilo::RowFlag::kRhsInf; + h_row_flags[i].set(papilo::RowFlag::kRhsInf); + } else { + h_row_flags[i].unset(papilo::RowFlag::kRhsInf); } + if (h_constr_lb[i] == -std::numeric_limits::infinity()) { h_constr_lb[i] = 0; } // builder.setRowLhs(i, 0); } @@ -170,7 +175,13 @@ papilo::Problem build_papilo_problem(const optimization_problem_t auto constexpr const sorted_entries = true; auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries); problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); - + papilo::ConstraintMatrix& matrix = problem.getConstraintMatrix(); + for (int i = 0; i < problem.getNRows(); i++) { + papilo::RowFlags rowFlag = matrix.getRowFlags()[i]; + if (!rowFlag.test(papilo::RowFlag::kRhsInf) && !rowFlag.test(papilo::RowFlag::kLhsInf) && + matrix.getLeftHandSides()[i] == matrix.getRightHandSides()[i]) + matrix.getRowFlags()[i].set(papilo::RowFlag::kEquation); + } CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); return problem; } From 90964550292546a8be752f7fe34ad0ae5b6106ac Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Wed, 3 Sep 2025 17:54:26 -0700 Subject: [PATCH 06/12] Fix entries size --- cpp/src/mip/presolve/third_party_presolve.cpp | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 85c2ccc732..df56f42f9b 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -33,7 +33,6 @@ static bool maximize_ = false; template papilo::Problem build_papilo_problem(const optimization_problem_t& op_problem) { - auto building_timer = cuopt::timer_t(3600); // Build papilo problem from optimization problem papilo::ProblemBuilder builder; @@ -125,10 +124,9 @@ papilo::Problem build_papilo_problem(const optimization_problem_t builder.setRowLhsAll(h_constr_lb); builder.setRowRhsAll(h_constr_ub); } - CUOPT_LOG_INFO("Setting col integrality time: %f", building_timer.elapsed_time()); std::vector h_row_flags(h_constr_lb.size()); - std::vector> h_entries(h_constr_lb.size()); + std::vector> h_entries; // // Add constraints row by row for (size_t i = 0; i < h_constr_lb.size(); ++i) { // // Get row entries @@ -136,14 +134,10 @@ papilo::Problem build_papilo_problem(const optimization_problem_t i_t row_end = h_offsets[i + 1]; i_t num_entries = row_end - row_start; for (size_t j = 0; j < num_entries; ++j) { - h_entries[i] = std::make_tuple(i, h_variables[row_start + j], h_coefficients[row_start + j]); + h_entries.push_back( + std::make_tuple(i, h_variables[row_start + j], h_coefficients[row_start + j])); } - // builder.addRowEntries( - // i, num_entries, h_variables.data() + row_start, h_coefficients.data() + row_start); - // // CUOPT_LOG_INFO("Adding row entries time: %f", building_timer.elapsed_time()); - // builder.setRowLhsInf(i, h_constr_lb[i] == -std::numeric_limits::infinity()); - // builder.setRowRhsInf(i, h_constr_ub[i] == std::numeric_limits::infinity()); if (h_constr_lb[i] == -std::numeric_limits::infinity()) { h_row_flags[i].set(papilo::RowFlag::kLhsInf); } else { @@ -155,14 +149,9 @@ papilo::Problem build_papilo_problem(const optimization_problem_t h_row_flags[i].unset(papilo::RowFlag::kRhsInf); } - if (h_constr_lb[i] == -std::numeric_limits::infinity()) { - h_constr_lb[i] = 0; - } // builder.setRowLhs(i, 0); } - if (h_constr_ub[i] == std::numeric_limits::infinity()) { - h_constr_ub[i] = 0; - } // builder.setRowRhs(i, 0); } + if (h_constr_lb[i] == -std::numeric_limits::infinity()) { h_constr_lb[i] = 0; } + if (h_constr_ub[i] == std::numeric_limits::infinity()) { h_constr_ub[i] = 0; } } - CUOPT_LOG_INFO("Setting row bounds time: %f", building_timer.elapsed_time()); for (size_t i = 0; i < h_var_lb.size(); ++i) { builder.setColLbInf(i, h_var_lb[i] == -std::numeric_limits::infinity()); @@ -170,19 +159,20 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (h_var_lb[i] == -std::numeric_limits::infinity()) { builder.setColLb(i, 0); } if (h_var_ub[i] == std::numeric_limits::infinity()) { builder.setColUb(i, 0); } } - CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); - auto problem = builder.build(); + auto problem = builder.build(); + auto constexpr const sorted_entries = true; auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries); problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); + papilo::ConstraintMatrix& matrix = problem.getConstraintMatrix(); - for (int i = 0; i < problem.getNRows(); i++) { + for (int i = 0; i < problem.getNRows(); ++i) { papilo::RowFlags rowFlag = matrix.getRowFlags()[i]; if (!rowFlag.test(papilo::RowFlag::kRhsInf) && !rowFlag.test(papilo::RowFlag::kLhsInf) && matrix.getLeftHandSides()[i] == matrix.getRightHandSides()[i]) matrix.getRowFlags()[i].set(papilo::RowFlag::kEquation); } - CUOPT_LOG_INFO("Building time: %f", building_timer.elapsed_time()); + return problem; } From 13938b34757f080851d2da71963586846967c226 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 4 Sep 2025 11:17:23 -0700 Subject: [PATCH 07/12] Skip constraint matrix if empty --- cpp/src/mip/presolve/third_party_presolve.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index c1b57bd98d..0bcff146ed 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -159,18 +159,21 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (h_var_lb[i] == -std::numeric_limits::infinity()) { builder.setColLb(i, 0); } if (h_var_ub[i] == std::numeric_limits::infinity()) { builder.setColUb(i, 0); } } - auto problem = builder.build(); - auto constexpr const sorted_entries = true; - auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries); - problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); + auto problem = builder.build(); - papilo::ConstraintMatrix& matrix = problem.getConstraintMatrix(); - for (int i = 0; i < problem.getNRows(); ++i) { - papilo::RowFlags rowFlag = matrix.getRowFlags()[i]; - if (!rowFlag.test(papilo::RowFlag::kRhsInf) && !rowFlag.test(papilo::RowFlag::kLhsInf) && - matrix.getLeftHandSides()[i] == matrix.getRightHandSides()[i]) - matrix.getRowFlags()[i].set(papilo::RowFlag::kEquation); + if (h_entries.size()) { + auto constexpr const sorted_entries = true; + auto csr_storage = papilo::SparseStorage(h_entries, num_rows, num_cols, sorted_entries); + problem.setConstraintMatrix(csr_storage, h_constr_lb, h_constr_ub, h_row_flags); + + papilo::ConstraintMatrix& matrix = problem.getConstraintMatrix(); + for (int i = 0; i < problem.getNRows(); ++i) { + papilo::RowFlags rowFlag = matrix.getRowFlags()[i]; + if (!rowFlag.test(papilo::RowFlag::kRhsInf) && !rowFlag.test(papilo::RowFlag::kLhsInf) && + matrix.getLeftHandSides()[i] == matrix.getRightHandSides()[i]) + matrix.getRowFlags()[i].set(papilo::RowFlag::kEquation); + } } return problem; From 9d310cdb0a89343588fd276c4a3528892449d8d8 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 4 Sep 2025 11:50:56 -0700 Subject: [PATCH 08/12] Fix call to apply presolve --- cpp/src/mip/solve.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 373e107653..243bb47dac 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -197,8 +197,8 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, cuopt::linear_programming::problem_category_t::MIP, settings.tolerances.absolute_tolerance, settings.tolerances.relative_tolerance, - settings.num_cpu_threads, - presolve_time_limit); + presolve_time_limit, + settings.num_cpu_threads); if (!feasible) { return mip_solution_t(mip_termination_status_t::Infeasible, solver_stats_t{}, From c32b38bb493cdb501ee1555ed59d7c1d63ea18dd Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 4 Sep 2025 13:16:23 -0700 Subject: [PATCH 09/12] Link tbb required for python --- cpp/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index a5174edb1e..76766e2f32 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -176,6 +176,7 @@ FetchContent_Declare( SYSTEM ) +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) @@ -252,6 +253,7 @@ target_include_directories(cuopt set(CUOPT_PRIVATE_CUDA_LIBS CUDA::curand CUDA::cusolver + TBB::tbb OpenMP::OpenMP_CXX) list(PREPEND CUOPT_PRIVATE_CUDA_LIBS CUDA::cublasLt) From 9a4add43daa11bf88d9e65769f37e415219270e5 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 4 Sep 2025 15:01:53 -0700 Subject: [PATCH 10/12] Add find tbb --- cpp/cmake/thirdparty/FindTBB.cmake | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 cpp/cmake/thirdparty/FindTBB.cmake diff --git a/cpp/cmake/thirdparty/FindTBB.cmake b/cpp/cmake/thirdparty/FindTBB.cmake new file mode 100644 index 0000000000..f71f816208 --- /dev/null +++ b/cpp/cmake/thirdparty/FindTBB.cmake @@ -0,0 +1,90 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# FindTBB.cmake - Find TBB (Threading Building Blocks) library +# +# This module defines the following variables: +# TBB_FOUND - True if TBB is found +# TBB_INCLUDE_DIRS - TBB include directories +# TBB_LIBRARIES - TBB libraries +# TBB::tbb - Imported target for TBB + +# Try pkg-config first +find_package(PkgConfig QUIET) +if(PkgConfig_FOUND) + pkg_check_modules(PC_TBB QUIET tbb) +endif() + +find_path(TBB_INCLUDE_DIR + NAMES tbb/tbb.h + PATHS + ${PC_TBB_INCLUDE_DIRS} + /usr/include + /usr/local/include + /opt/intel/tbb/include + /opt/intel/oneapi/tbb/latest/include +) + +find_library(TBB_LIBRARY + NAMES tbb + PATHS + ${PC_TBB_LIBRARY_DIRS} + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/intel/tbb/lib + /opt/intel/oneapi/tbb/latest/lib +) + +find_library(TBB_MALLOC_LIBRARY + NAMES tbbmalloc + PATHS + ${PC_TBB_LIBRARY_DIRS} + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/intel/tbb/lib + /opt/intel/oneapi/tbb/latest/lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(TBB + REQUIRED_VARS TBB_INCLUDE_DIR TBB_LIBRARY +) + +if(TBB_FOUND AND NOT TARGET TBB::tbb) + add_library(TBB::tbb UNKNOWN IMPORTED) + set_target_properties(TBB::tbb PROPERTIES + IMPORTED_LOCATION "${TBB_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${TBB_INCLUDE_DIR}" + ) + + if(TBB_MALLOC_LIBRARY) + set_target_properties(TBB::tbb PROPERTIES + INTERFACE_LINK_LIBRARIES "${TBB_MALLOC_LIBRARY}" + ) + endif() + + # Add compile definitions from pkg-config if available + if(PC_TBB_CFLAGS_OTHER) + set_target_properties(TBB::tbb PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_TBB_CFLAGS_OTHER}" + ) + endif() +endif() + +mark_as_advanced(TBB_INCLUDE_DIR TBB_LIBRARY TBB_MALLOC_LIBRARY) From cc3b92b9968c36dc5cf13151515223da8c54dcc2 Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 4 Sep 2025 15:55:36 -0700 Subject: [PATCH 11/12] Ignore boost warning --- cpp/src/mip/presolve/third_party_presolve.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 0bcff146ed..3503fff04f 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -21,8 +21,11 @@ #include #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow" // ignore boost error for pip wheel build #include #include +#pragma GCC diagnostic pop namespace cuopt::linear_programming::detail { From a973c1b8a7163ab6ac0f3dd9c481dbfc3ca7727a Mon Sep 17 00:00:00 2001 From: Hugo Linsenmaier Date: Thu, 4 Sep 2025 16:19:15 -0700 Subject: [PATCH 12/12] Clean up --- cpp/src/mip/presolve/third_party_presolve.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 3503fff04f..4c9dcb5743 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -130,9 +130,9 @@ papilo::Problem build_papilo_problem(const optimization_problem_t std::vector h_row_flags(h_constr_lb.size()); std::vector> h_entries; - // // Add constraints row by row + // Add constraints row by row for (size_t i = 0; i < h_constr_lb.size(); ++i) { - // // Get row entries + // Get row entries i_t row_start = h_offsets[i]; i_t row_end = h_offsets[i + 1]; i_t num_entries = row_end - row_start; @@ -342,8 +342,7 @@ void set_presolve_options(papilo::Presolve& presolver, double time_limit, i_t num_cpu_threads) { - presolver.getPresolveOptions().tlim = time_limit; - CUOPT_LOG_INFO("Setting presolve options:: time limit: %f", time_limit); + presolver.getPresolveOptions().tlim = time_limit; presolver.getPresolveOptions().threads = num_cpu_threads; // user setting or 0 (automatic) } @@ -373,7 +372,7 @@ std::pair, bool> third_party_presolve_t