Skip to content
Open
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
2 changes: 2 additions & 0 deletions cpp/include/cuopt/mathematical_optimization/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
#define CUOPT_ORDERING "ordering"
#define CUOPT_BARRIER_DUAL_INITIAL_POINT "barrier_dual_initial_point"
#define CUOPT_BARRIER_ITERATIVE_REFINEMENT "barrier_iterative_refinement"
#define CUOPT_BARRIER_CSR_IR_MATVEC "barrier_csr_ir_matvec"
#define CUOPT_BARRIER_SOC_THRESHOLD "barrier_soc_threshold"

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.

is this parameter needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These are parameters I want to tune, so I want to expose them during the developing phase. They would be removed when I'm going to merge PR.

#define CUOPT_BARRIER_STEP_SCALE "barrier_step_scale"
#define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns"
#define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ class pdlp_solver_settings_t {
bool eliminate_dense_columns{true};
pdlp_precision_t pdlp_precision{pdlp_precision_t::DefaultPrecision};
bool barrier_iterative_refinement{true};
bool barrier_csr_ir_matvec{false};

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.

Lets remove this if its faster always faster to avoid the operator based version.

@yuwenchen95 yuwenchen95 Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So far, I haven't see significant speedup for others but fail optimality for several examples in cblib. It's better to keep it as an option but not default until I can improve the numerical stability of it.

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.

which problem are you seeing the issue with?

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.

We want to avoid code divergence as much as possible. This definitely should not be a user facing option.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We want to avoid code divergence as much as possible. This definitely should not be a user facing option.

I agree not to expose it when the PR is merged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

which problem are you seeing the issue with?

sched_ problems in cblib suffer numerical degradation on it.

i_t barrier_soc_threshold{5};
f_t barrier_step_scale{0.9};
bool save_best_primal_so_far{false};
/**
Expand Down
745 changes: 591 additions & 154 deletions cpp/src/barrier/barrier.cu

Large diffs are not rendered by default.

143 changes: 92 additions & 51 deletions cpp/src/barrier/cusparse_view.cu
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ static cusparseSpMVAlg_t get_spmv_alg([[maybe_unused]] int num_rows)
return CUSPARSE_SPMV_CSR_ALG2;
}

template <typename i_t, typename f_t>
void cusparse_view_t<i_t, f_t>::init_spmv_buffer_and_preprocess(cusparseSpMatDescr_t mat,
cusparseDnVecDescr_t x,
cusparseDnVecDescr_t y,
rmm::device_buffer& buffer,
i_t rows)
{
const auto spmv_alg = get_spmv_alg(rows);
size_t buffer_size_spmv = 0;
RAFT_CUSPARSE_TRY(
raft::sparse::detail::cusparsespmv_buffersize(handle_ptr_->get_cusparse_handle(),
CUSPARSE_OPERATION_NON_TRANSPOSE,
d_one_.data(),
mat,
x,
d_one_.data(),
y,
spmv_alg,
&buffer_size_spmv,
handle_ptr_->get_stream()));
buffer.resize(buffer_size_spmv, handle_ptr_->get_stream());

my_cusparsespmv_preprocess(handle_ptr_->get_cusparse_handle(),
CUSPARSE_OPERATION_NON_TRANSPOSE,
d_one_.data(),
mat,
x,
d_one_.data(),
y,
spmv_alg,
buffer.data(),
handle_ptr_->get_stream());
}

template <typename i_t, typename f_t>
cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,
const csc_matrix_t<i_t, f_t>& A)
Expand All @@ -136,6 +170,7 @@ cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,
A_T_indices_(0, handle_ptr->get_stream()),
A_T_data_(0, handle_ptr->get_stream()),
spmv_buffer_(0, handle_ptr->get_stream()),
spmv_buffer_transpose_(0, handle_ptr->get_stream()),
d_one_(f_t(1), handle_ptr->get_stream()),
d_minus_one_(f_t(-1), handle_ptr->get_stream()),
d_zero_(f_t(0), handle_ptr->get_stream())
Expand All @@ -149,7 +184,7 @@ cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,
if (debug) { printf("A hash: %zu\n", A.hash()); }
csr_matrix_t<i_t, f_t> A_csr(A.m, A.n, 1);
A.to_compressed_row(A_csr);
i_t rows = A_csr.m;
rows_ = A_csr.m;
i_t cols = A_csr.n;
i_t nnz = A_csr.x.size();
const std::vector<i_t>& offsets = A_csr.row_start;
Expand All @@ -165,7 +200,7 @@ cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,
A_T_data_ = device_copy(A.x, handle_ptr->get_stream());

cusparseCreateCsr(&A_,
rows,
rows_,
cols,
nnz,
A_offsets_.data(),
Expand All @@ -178,7 +213,7 @@ cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,

cusparseCreateCsr(&A_T_,
cols,
rows,
rows_,
nnz,
A_T_offsets_.data(),
A_T_indices_.data(),
Expand All @@ -192,58 +227,61 @@ cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,
cusparseDnVecDescr_t x;
cusparseDnVecDescr_t y;
rmm::device_uvector<f_t> d_x(cols, handle_ptr_->get_stream());
rmm::device_uvector<f_t> d_y(rows, handle_ptr_->get_stream());
rmm::device_uvector<f_t> d_y(rows_, handle_ptr_->get_stream());
RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsecreatednvec(&x, d_x.size(), d_x.data()));
RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsecreatednvec(&y, d_y.size(), d_y.data()));

size_t buffer_size_spmv = 0;
RAFT_CUSPARSE_TRY(
raft::sparse::detail::cusparsespmv_buffersize(handle_ptr_->get_cusparse_handle(),
CUSPARSE_OPERATION_NON_TRANSPOSE,
d_one_.data(),
A_,
x,
d_one_.data(),
y,
get_spmv_alg(A_offsets_.size() - 1),
&buffer_size_spmv,
handle_ptr_->get_stream()));
spmv_buffer_.resize(buffer_size_spmv, handle_ptr_->get_stream());
init_spmv_buffer_and_preprocess(A_, x, y, spmv_buffer_, rows_);
init_spmv_buffer_and_preprocess(A_T_, y, x, spmv_buffer_transpose_, A_T_offsets_.size() - 1);

my_cusparsespmv_preprocess(handle_ptr_->get_cusparse_handle(),
CUSPARSE_OPERATION_NON_TRANSPOSE,
d_one_.data(),
A_,
x,
d_one_.data(),
y,
get_spmv_alg(A_offsets_.size() - 1),
spmv_buffer_.data(),
handle_ptr->get_stream());
RAFT_CUSPARSE_TRY(cusparseDestroyDnVec(x));
RAFT_CUSPARSE_TRY(cusparseDestroyDnVec(y));
}

RAFT_CUSPARSE_TRY(
raft::sparse::detail::cusparsespmv_buffersize(handle_ptr_->get_cusparse_handle(),
CUSPARSE_OPERATION_NON_TRANSPOSE,
d_one_.data(),
A_T_,
y,
d_one_.data(),
x,
get_spmv_alg(A_T_offsets_.size() - 1),
&buffer_size_spmv,
handle_ptr_->get_stream()));
spmv_buffer_transpose_.resize(buffer_size_spmv, handle_ptr_->get_stream());
template <typename i_t, typename f_t>
cusparse_view_t<i_t, f_t>::cusparse_view_t(raft::handle_t const* handle_ptr,
device_csr_matrix_t<i_t, f_t>& matrix)
: handle_ptr_(handle_ptr),
A_offsets_(0, handle_ptr->get_stream()),
A_indices_(0, handle_ptr->get_stream()),
A_data_(0, handle_ptr->get_stream()),
A_T_offsets_(0, handle_ptr->get_stream()),
A_T_indices_(0, handle_ptr->get_stream()),
A_T_data_(0, handle_ptr->get_stream()),
spmv_buffer_(0, handle_ptr->get_stream()),
spmv_buffer_transpose_(0, handle_ptr->get_stream()),
d_one_(f_t(1), handle_ptr->get_stream()),
d_minus_one_(f_t(-1), handle_ptr->get_stream()),
d_zero_(f_t(0), handle_ptr->get_stream()),
rows_(matrix.m)
{
RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode(
handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream()));

const i_t cols = matrix.n;
const i_t nnz = matrix.nz_max;

RAFT_CUSPARSE_TRY(cusparseCreateCsr(&A_,
rows_,
cols,
nnz,
matrix.row_start.data(),
matrix.j.data(),
matrix.x.data(),
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_BASE_ZERO,
CUDA_R_64F));

cusparseDnVecDescr_t x;
cusparseDnVecDescr_t y;
rmm::device_uvector<f_t> d_x(cols, handle_ptr_->get_stream());
rmm::device_uvector<f_t> d_y(rows_, handle_ptr_->get_stream());
RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsecreatednvec(&x, d_x.size(), d_x.data()));
RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsecreatednvec(&y, d_y.size(), d_y.data()));

init_spmv_buffer_and_preprocess(A_, x, y, spmv_buffer_, rows_);

my_cusparsespmv_preprocess(handle_ptr_->get_cusparse_handle(),
CUSPARSE_OPERATION_NON_TRANSPOSE,
d_one_.data(),
A_T_,
y,
d_one_.data(),
x,
get_spmv_alg(A_T_offsets_.size() - 1),
spmv_buffer_transpose_.data(),
handle_ptr->get_stream());
RAFT_CUSPARSE_TRY(cusparseDestroyDnVec(x));
RAFT_CUSPARSE_TRY(cusparseDestroyDnVec(y));
}
Expand All @@ -252,7 +290,7 @@ template <typename i_t, typename f_t>
cusparse_view_t<i_t, f_t>::~cusparse_view_t()
{
CUOPT_CUSPARSE_TRY_NO_THROW(cusparseDestroySpMat(A_));
CUOPT_CUSPARSE_TRY_NO_THROW(cusparseDestroySpMat(A_T_));
if (A_T_ != nullptr) { CUOPT_CUSPARSE_TRY_NO_THROW(cusparseDestroySpMat(A_T_)); }
}

template <typename i_t, typename f_t>
Expand Down Expand Up @@ -311,7 +349,7 @@ void cusparse_view_t<i_t, f_t>::spmv(f_t alpha,
x,
d_beta->data(),
y,
get_spmv_alg(A_offsets_.size() - 1),
get_spmv_alg(rows_),
(f_t*)spmv_buffer_.data(),
handle_ptr_->get_stream());
}
Expand All @@ -323,6 +361,7 @@ void cusparse_view_t<i_t, f_t>::transpose_spmv(f_t alpha,
f_t beta,
std::vector<f_t, AllocatorB>& y)
{
cuopt_assert(A_T_ != nullptr, "transpose_spmv requires an A^T descriptor");
auto d_x = device_copy(x, handle_ptr_->get_stream());
auto d_y = device_copy(y, handle_ptr_->get_stream());
transpose_spmv(alpha, d_x, beta, d_y);
Expand All @@ -335,6 +374,7 @@ void cusparse_view_t<i_t, f_t>::transpose_spmv(f_t alpha,
f_t beta,
rmm::device_uvector<f_t>& y)
{
cuopt_assert(A_T_ != nullptr, "transpose_spmv requires an A^T descriptor");
pdlp::cusparse_dn_vec_descr_wrapper_t<f_t> x_cusparse = create_vector(x);
pdlp::cusparse_dn_vec_descr_wrapper_t<f_t> y_cusparse = create_vector(y);
transpose_spmv(alpha, x_cusparse, beta, y_cusparse);
Expand All @@ -346,6 +386,7 @@ void cusparse_view_t<i_t, f_t>::transpose_spmv(f_t alpha,
f_t beta,
pdlp::cusparse_dn_vec_descr_wrapper_t<f_t> const& y)
{
cuopt_assert(A_T_ != nullptr, "transpose_spmv requires an A^T descriptor");
// Would be simpler if we could pass host data direct;y but other cusparse calls with the same
// handler depend on device data
cuopt_assert(alpha == f_t(1) || alpha == f_t(-1), "Only alpha 1 or -1 supported");
Expand Down
23 changes: 17 additions & 6 deletions cpp/src/barrier/cusparse_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
/* clang-format on */
#pragma once

#include <barrier/device_sparse_matrix.cuh>

#include <linear_algebra/sparse_matrix.hpp>

#include <pdlp/cusparse_view.hpp>
Expand All @@ -17,17 +19,19 @@

#include <raft/core/handle.hpp>

// Lightweight cuSparse view
// Only owns data linked to the associated matrix
// Associated dense vector should be owned by the calling object
// This allows handling many different X Y vector along with one common matrix
// Lightweight cuSparse view over a sparse matrix descriptor. The dense vectors
// are owned by the caller, which allows many x/y pairs to share one matrix view.
namespace cuopt::mathematical_optimization::barrier {

template <typename i_t, typename f_t>
class cusparse_view_t {
public:
// Copy CSC -> owned CSR + CSC-transpose, with preprocess. Supports forward and transpose SpMV.
// TMP matrix data should already be on the GPU and in CSR not CSC
cusparse_view_t(raft::handle_t const* handle_ptr, const csc_matrix_t<i_t, f_t>& A);

// Wire cuSparse SpMV over existing device CSR buffers (no copy). Forward SpMV only.
cusparse_view_t(raft::handle_t const* handle_ptr, device_csr_matrix_t<i_t, f_t>& matrix);
~cusparse_view_t();

pdlp::cusparse_dn_vec_descr_wrapper_t<f_t> create_vector(rmm::device_uvector<f_t> const& vec);
Expand Down Expand Up @@ -59,18 +63,25 @@ class cusparse_view_t {
raft::handle_t const* handle_ptr_{nullptr};

private:
void init_spmv_buffer_and_preprocess(cusparseSpMatDescr_t mat,
cusparseDnVecDescr_t x,
cusparseDnVecDescr_t y,
rmm::device_buffer& buffer,
i_t rows);

rmm::device_uvector<i_t> A_offsets_;
rmm::device_uvector<i_t> A_indices_;
rmm::device_uvector<f_t> A_data_;
cusparseSpMatDescr_t A_;
cusparseSpMatDescr_t A_{nullptr};
rmm::device_uvector<i_t> A_T_offsets_;
rmm::device_uvector<i_t> A_T_indices_;
rmm::device_uvector<f_t> A_T_data_;
cusparseSpMatDescr_t A_T_;
cusparseSpMatDescr_t A_T_{nullptr};
rmm::device_buffer spmv_buffer_;
rmm::device_buffer spmv_buffer_transpose_;
rmm::device_scalar<f_t> d_one_;
rmm::device_scalar<f_t> d_minus_one_;
rmm::device_scalar<f_t> d_zero_;
i_t rows_{0};
};
} // namespace cuopt::mathematical_optimization::barrier
Loading
Loading