Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions cpp/src/barrier/cusparse_view.cu
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ void my_cusparsespmv_preprocess(cusparseHandle_t handle,
}
#endif

static cusparseSpMVAlg_t get_spmv_alg(int num_rows)
static cusparseSpMVAlg_t get_spmv_alg([[maybe_unused]] int num_rows)
{
// The older version of ALG2 has a bug with single row matrices
if (num_rows == 1 &&
(CUSPARSE_VER_MAJOR * 1000 + CUSPARSE_VER_MINOR * 100 + CUSPARSE_VER_PATCH < 12603)) {
return CUSPARSE_SPMV_CSR_ALG1;
}
// ALG2 has a bug in cuSPARSE < 13.0 where beta=1 accumulate mode ignores existing y values.
// ALG1 uses a deterministic row-split algorithm, while ALG2 uses a merge-based
// algorithm that may be faster but can use atomics. ALG1 is safe for reproducibility.
constexpr int cusparse_version =
CUSPARSE_VER_MAJOR * 1000 + CUSPARSE_VER_MINOR * 100 + CUSPARSE_VER_PATCH;
if (cusparse_version < 13000) { return CUSPARSE_SPMV_CSR_ALG1; }
return CUSPARSE_SPMV_CSR_ALG2;
}
Comment on lines +117 to 126

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Can cusparseGetProperty runtime cuSPARSE version differ from CUSPARSE_VER_MAJOR/MINOR compile-time macros when linked dynamically?

💡 Result:

Yes, the version reported by the runtime function cusparseGetProperty can differ from the versions defined by the CUSPARSE_VER_MAJOR and CUSPARSE_VER_MINOR compile-time macros [1][2]. Compile-time macros such as CUSPARSE_VER_MAJOR, CUSPARSE_VER_MINOR, and CUSPARSE_VER_PATCH are defined in the cuSPARSE header files (e.g., cusparse.h) available at the time the application is compiled [3][4][5]. These reflect the version of the cuSPARSE SDK used during the build process [1][2]. In contrast, cusparseGetProperty is a runtime API that queries the version of the specific cuSPARSE library (libcusparse.so or.dll) that is dynamically loaded into the application's process at execution time [6][7]. When linking dynamically, the library found at runtime by the system's dynamic linker may be different from the one against which the application was originally compiled (e.g., due to differences in the environment's LD_LIBRARY_PATH or PATH) [8]. This discrepancy is a known challenge in complex deployment environments where the runtime environment may contain a different version of the NVIDIA CUDA Toolkit or individual libraries than the development environment [1][2]. Developers are generally advised to rely on the runtime API (cusparseGetProperty) for features dependent on the loaded library version to ensure compatibility with the actual environment [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file and call sites.
git grep -n "get_spmv_alg" -- cpp/src/barrier/cusparse_view.cu
sed -n '1,170p' cpp/src/barrier/cusparse_view.cu
sed -n '170,390p' cpp/src/barrier/cusparse_view.cu

# Check whether the runtime-version handling comment is present nearby.
git grep -n "There can be a missmatch between compiled CUDA version and the runtime CUDA version" -- cpp/src/barrier/cusparse_view.cu

Repository: NVIDIA/cuopt

Length of output: 18107


Use the loaded cuSPARSE version for ALG selection CUSPARSE_VER_* is build-time only, but this file already accounts for runtime/library mismatches elsewhere. Query the runtime cuSPARSE version here too (or cache it in the existing loader helper) so ALG1/ALG2 selection matches the actual libcusparse.so and doesn’t reintroduce the beta=1 accumulate bug.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/barrier/cusparse_view.cu` around lines 117 - 126, The ALG selection
in get_spmv_alg is using the build-time CUSPARSE_VER_* macros, which can diverge
from the actually loaded cuSPARSE library. Update this helper to use the
runtime/library version instead, preferably by reusing or extending the existing
cuSPARSE loader/cache path already used elsewhere in cusparse_view.cu, so the
ALG1/ALG2 choice reflects the real libcusparse.so and avoids the beta=1
accumulate bug.


Expand Down
5 changes: 0 additions & 5 deletions cpp/src/barrier/translate_soc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,11 +871,6 @@ void convert_quadratic_constraints_to_second_order_cones(

// Convert rotated SOC cones to standard SOC cones.
if (!rotated_cones.empty()) {
cuopt_expects(user_problem.Q_values.empty(),
error_type_t::ValidationError,
"Rotated SOC conversion is currently not supported when the objective has "
"quadratic terms");

const f_t inf = std::numeric_limits<f_t>::infinity();
const f_t inv_sqrt_2 = f_t(1) / std::sqrt(f_t(2));
const f_t half = f_t(0.5);
Expand Down
38 changes: 38 additions & 0 deletions cpp/tests/socp/general_quadratic_test.cu
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <cuopt/error.hpp>
#include <cuopt/mathematical_optimization/io/parser.hpp>
#include <cuopt/mathematical_optimization/optimization_problem_interface.hpp>
#include <cuopt/mathematical_optimization/pdlp/solver_settings.hpp>
#include <cuopt/mathematical_optimization/solve.hpp>
#include <dual_simplex/solve.hpp>
#include <dual_simplex/user_problem.hpp>
#include <linear_algebra/sparse_matrix.hpp>
Expand Down Expand Up @@ -907,4 +909,40 @@ TEST(general_quadratic, rotated_soc_heads_free_rejected)
cuopt::logic_error);
}

// Test QCQP with rotated SOC constraint and quadratic objective using high-level API.
// This is the recommended way to solve QCQP problems.
// minimize (1/2)*x^2 - x (quadratic objective)
// subject to x^2 - 2*t*u <= 0 (rotated SOC)
// t = 1, u = 0.5
// Optimal: x = 1, objective = -0.5
TEST(general_quadratic, qcqp_rotated_soc)
{
raft::handle_t handle{};

auto problem = io::read_lp_from_string<i_t, f_t>(R"LP(
Minimize
- x + [ x ^2 ] / 2
Subject To
t_eq: t = 1
u_eq: u = 0.5
rsoc: [ x ^2 - 2 t * u ] <= 0
Bounds
x free
t >= 0
u >= 0
End
)LP");

ASSERT_TRUE(problem.has_quadratic_objective());
ASSERT_TRUE(problem.has_quadratic_constraints());
ASSERT_EQ(problem.get_quadratic_constraints().size(), 1u);

pdlp_solver_settings_t<i_t, f_t> settings;
auto solution = solve_lp(&handle, problem, settings);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind leaving a TODO to redo all the other tests in this form?


EXPECT_EQ(solution.get_termination_status(), pdlp_termination_status_t::Optimal);
// Optimal: x=1, objective = (1/2)*1 - 1 = -0.5
EXPECT_NEAR(solution.get_objective_value(), -0.5, 1e-4);
}

} // namespace cuopt::mathematical_optimization::barrier::test