139class SparseSolverBase {
141 using Scalar = Scalar_;
143 using StorageIndex = int;
149 SparseSolverBase() { init_context(
nullptr,
false); }
158 destroy_cudss_objects();
159 if (handle_) (void)cudssDestroy(handle_);
160 if (owns_stream_ && stream_) (void)cudaStreamDestroy(stream_);
187#if !EIGEN_HAS_CUDSS_SOLVER_CONFIG
188 eigen_assert(cfg.
isDefault() &&
"SparseSolverConfig knobs require cuDSS >= 0.8");
195 analysis_done_ =
false;
201 const bool hybrid_mode_changed =
203 eigen_assert((!hybrid_mode_changed || !analysis_done_) &&
204 "hybridMemory/hybridExecute change what analyzePattern() builds, so they must be set before it");
209 if (hybrid_mode_changed && analysis_done_) {
212 analysis_done_ =
false;
222 template <
typename InputType>
233 template <
typename InputType>
235#if !EIGEN_HAS_CUDSS_SOLVER_CONFIG
236 if (!config_opts_.isDefault()) {
240 analysis_done_ =
false;
245 const InputType& input = A.
derived();
246 check_storage_index_bounds<StorageIndex>(input.rows(), input.cols(), input.nonZeros());
247 eigen_assert(input.rows() == input.cols() &&
"GpuSparseSolver requires a square matrix");
251 analysis_done_ =
false;
256 analysis_done_ =
true;
265 if (Derived::needs_csr_conversion()) {
266 const CsrMat csr(input);
273 const SpMat& csc = bind_sparse<SpMat>(input, storage);
277 create_cudss_matrix();
279 if (data_) EIGEN_CUDSS_CHECK(cudssDataDestroy(handle_, data_));
280 EIGEN_CUDSS_CHECK(cudssDataCreate(handle_, &data_));
282 create_placeholder_dense();
284 EIGEN_CUDSS_CHECK(cudssExecute(handle_, CUDSS_PHASE_ANALYSIS, config_, data_, d_A_cudss_, d_x_cudss_, d_b_cudss_));
286 analysis_done_ =
true;
299 template <
typename InputType>
301 eigen_assert(analysis_done_ &&
"factorize() requires analyzePattern() first");
302 if (!analysis_done_) {
320 const InputType& input = A.
derived();
321 check_storage_index_bounds<StorageIndex>(input.rows(), input.cols(), input.nonZeros());
322 eigen_assert(input.rows() == n_ && input.cols() == n_);
324 const Scalar* value_ptr;
328 if (Derived::needs_csr_conversion()) {
329 csr_tmp = CsrMat(input);
333 const SpMat& csc = bind_sparse<SpMat>(input, csc_storage);
337 eigen_assert(value_nnz == nnz_);
338 EIGEN_UNUSED_VARIABLE(value_nnz);
340 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpyAsync(d_values_.get(), value_ptr,
static_cast<size_t>(nnz_) *
sizeof(Scalar),
341 cudaMemcpyHostToDevice, stream_));
343 EIGEN_CUDSS_CHECK(cudssMatrixSetValues(d_A_cudss_, d_values_.get()));
346 info_synced_ =
false;
348 cudssExecute(handle_, CUDSS_PHASE_FACTORIZATION, config_, data_, d_A_cudss_, d_x_cudss_, d_b_cudss_));
355 template <
typename Rhs>
360 eigen_assert(info() ==
Success &&
"GpuSparseSolver::solve requires a successful factorization");
361 eigen_assert(B.rows() == n_);
363 if (n_ == 0)
return DenseMatrix(0, B.cols());
366 const int64_t nrhs =
static_cast<int64_t
>(rhs.cols());
369 const size_t rhs_bytes =
static_cast<size_t>(n_) *
static_cast<size_t>(nrhs) *
sizeof(Scalar);
370 ensure_solve_buffer(d_b_solve_, rhs_bytes);
371 ensure_solve_buffer(d_x_solve_, rhs_bytes);
372 internal::upload_host_matrix(
static_cast<Scalar*
>(d_b_solve_.get()), n_, rhs.data(), rhs.outerStride(), n_, nrhs,
375 update_solve_descriptors(nrhs, d_b_solve_.get(), d_x_solve_.get());
377 cudssExecute(handle_, CUDSS_PHASE_SOLVE, config_, data_, d_A_cudss_, x_solve_cudss_, b_solve_cudss_));
379 DenseMatrix X(n_, rhs.cols());
380 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpyAsync(X.
data(), d_x_solve_.get(), rhs_bytes, cudaMemcpyDeviceToHost, stream_));
381 EIGEN_CUDA_RUNTIME_CHECK(cudaStreamSynchronize(stream_));
392 eigen_assert(info() ==
Success &&
"GpuSparseSolver::solve requires a successful factorization");
393 eigen_assert(d_B.rows() == n_);
395 const int64_t nrhs =
static_cast<int64_t
>(d_B.cols());
397 if (n_ == 0 || nrhs == 0)
return X;
400 update_solve_descriptors(nrhs,
const_cast<Scalar*
>(d_B.data()), X.data());
402 cudssExecute(handle_, CUDSS_PHASE_SOLVE, config_, data_, d_A_cudss_, x_solve_cudss_, b_solve_cudss_));
411 Index rows()
const {
return n_; }
412 Index cols()
const {
return n_; }
414 cudaStream_t stream()
const {
return stream_; }
417 cudaStream_t stream_ =
nullptr;
418 bool owns_stream_ =
true;
419 cudssHandle_t handle_ =
nullptr;
420 cudssConfig_t config_ =
nullptr;
421 cudssData_t data_ =
nullptr;
422 cudssMatrix_t d_A_cudss_ =
nullptr;
423 cudssMatrix_t d_x_cudss_ =
nullptr;
424 cudssMatrix_t d_b_cudss_ =
nullptr;
426 DeviceBuffer d_rowPtr_;
427 DeviceBuffer d_colIdx_;
428 DeviceBuffer d_values_;
433 DenseVector conj_values_;
435 mutable DeviceBuffer d_b_solve_;
436 mutable DeviceBuffer d_x_solve_;
440 mutable cudssMatrix_t b_solve_cudss_ =
nullptr;
441 mutable cudssMatrix_t x_solve_cudss_ =
nullptr;
442 mutable int64_t solve_desc_nrhs_ = -1;
447 mutable bool info_synced_ =
true;
448 bool analysis_done_ =
false;
449 SparseSolverConfig config_opts_;
452 Derived& derived() {
return static_cast<Derived&
>(*this); }
453 const Derived& derived()
const {
return static_cast<const Derived&
>(*this); }
455 void init_context(cudaStream_t stream,
bool borrow_stream) {
460 owns_stream_ =
false;
462 EIGEN_CUDA_RUNTIME_CHECK(cudaStreamCreate(&stream_));
465 EIGEN_CUDSS_CHECK(cudssCreate(&handle_));
466 EIGEN_CUDSS_CHECK(cudssSetStream(handle_, stream_));
467 EIGEN_CUDSS_CHECK(cudssConfigCreate(&config_));
473 void apply_config() {
474#if EIGEN_HAS_CUDSS_SOLVER_CONFIG
479 EIGEN_CUDA_RUNTIME_CHECK(cudaStreamSynchronize(stream_));
480 (void)cudssConfigDestroy(config_);
483 EIGEN_CUDSS_CHECK(cudssConfigCreate(&config_));
484 const SparseSolverConfig& c = config_opts_;
485 if (c.reordering != SparseReordering::Default) {
486 const cudssReorderingAlg_t v =
static_cast<cudssReorderingAlg_t
>(c.reordering);
487 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_REORDERING_ALG, &v,
sizeof(v)));
489 if (c.matching != SparseMatching::Default) {
490 const cudssMatchingAlg_t v =
static_cast<cudssMatchingAlg_t
>(c.matching);
491 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_MATCHING_ALG, &v,
sizeof(v)));
493 if (c.pivoting != SparsePivoting::Default) {
494 const cudssPivotType_t v =
static_cast<cudssPivotType_t
>(c.pivoting);
495 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_PIVOT_TYPE, &v,
sizeof(v)));
497 if (c.pivotThreshold >= 0) {
499 cudssConfigSet(config_, CUDSS_CONFIG_PIVOT_THRESHOLD, &c.pivotThreshold,
sizeof(c.pivotThreshold)));
501 if (c.pivotEpsilon >= 0) {
502 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_PIVOT_EPSILON, &c.pivotEpsilon,
sizeof(c.pivotEpsilon)));
504 if (c.refinementSteps >= 0) {
506 cudssConfigSet(config_, CUDSS_CONFIG_IR_N_STEPS, &c.refinementSteps,
sizeof(c.refinementSteps)));
508 if (c.refinementTolerance >= 0) {
510 cudssConfigSet(config_, CUDSS_CONFIG_IR_TOL, &c.refinementTolerance,
sizeof(c.refinementTolerance)));
512 if (c.hybridMemory) {
514 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_HYBRID_MEMORY_MODE, &v,
sizeof(v)));
515 if (c.hybridMemoryDeviceLimit >= 0) {
516 const int64_t limit = c.hybridMemoryDeviceLimit;
517 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_HYBRID_DEVICE_MEMORY_LIMIT, &limit,
sizeof(limit)));
520 if (c.hybridExecute) {
522 EIGEN_CUDSS_CHECK(cudssConfigSet(config_, CUDSS_CONFIG_HYBRID_EXECUTE_MODE, &v,
sizeof(v)));
526 eigen_internal_assert(config_opts_.isDefault());
530 void ensure_solve_buffer(DeviceBuffer& buf,
size_t needed)
const {
531 if (needed > buf.size()) {
532 if (buf) EIGEN_CUDA_RUNTIME_CHECK(cudaStreamSynchronize(stream_));
533 buf = DeviceBuffer(needed);
539 void update_solve_descriptors(int64_t nrhs,
void* b_ptr,
void* x_ptr)
const {
540 constexpr cudss_value_type_t dtype = to_cudss_data_type(cuda_data_type<Scalar>::value);
541 if (!b_solve_cudss_ || solve_desc_nrhs_ != nrhs) {
542 destroy_solve_descriptors();
543 EIGEN_CUDSS_CHECK(cudssMatrixCreateDn(&b_solve_cudss_, n_, nrhs, n_, b_ptr, dtype, CUDSS_LAYOUT_COL_MAJOR));
544 EIGEN_CUDSS_CHECK(cudssMatrixCreateDn(&x_solve_cudss_, n_, nrhs, n_, x_ptr, dtype, CUDSS_LAYOUT_COL_MAJOR));
545 solve_desc_nrhs_ = nrhs;
547 EIGEN_CUDSS_CHECK(cudssMatrixSetValues(b_solve_cudss_, b_ptr));
548 EIGEN_CUDSS_CHECK(cudssMatrixSetValues(x_solve_cudss_, x_ptr));
552 void destroy_solve_descriptors()
const {
553 if (b_solve_cudss_) {
554 (void)cudssMatrixDestroy(b_solve_cudss_);
555 b_solve_cudss_ =
nullptr;
557 if (x_solve_cudss_) {
558 (void)cudssMatrixDestroy(x_solve_cudss_);
559 x_solve_cudss_ =
nullptr;
561 solve_desc_nrhs_ = -1;
564 void sync_info()
const {
569 EIGEN_CUDSS_CHECK(cudssDataGet(handle_, data_, CUDSS_DATA_INFO, &cudss_info,
sizeof(cudss_info),
nullptr));
576 void destroy_cudss_objects() {
580 (void)cudaStreamSynchronize(stream_);
581 destroy_solve_descriptors();
583 (void)cudssMatrixDestroy(d_A_cudss_);
584 d_A_cudss_ =
nullptr;
587 (void)cudssMatrixDestroy(d_x_cudss_);
588 d_x_cudss_ =
nullptr;
591 (void)cudssMatrixDestroy(d_b_cudss_);
592 d_b_cudss_ =
nullptr;
595 (void)cudssDataDestroy(handle_, data_);
599 (void)cudssConfigDestroy(config_);
607 static constexpr bool needs_value_conjugation() {
608 return Derived::cudss_matrix_type() == CUDSS_MTYPE_HPD || Derived::cudss_matrix_type() == CUDSS_MTYPE_HERMITIAN;
614 const Scalar* csc_upload_values(
const Scalar* values, Index nnz) {
615 if (!needs_value_conjugation())
return values;
616 conj_values_ = Map<const DenseVector>(values, nnz).conjugate();
617 return conj_values_.data();
620 void upload_compressed(
const StorageIndex* outer,
const StorageIndex* inner,
const Scalar* values) {
621 const size_t rowptr_bytes =
static_cast<size_t>(n_ + 1) *
sizeof(StorageIndex);
622 const size_t colidx_bytes =
static_cast<size_t>(nnz_) *
sizeof(StorageIndex);
623 const size_t values_bytes =
static_cast<size_t>(nnz_) *
sizeof(Scalar);
625 d_rowPtr_ = DeviceBuffer(rowptr_bytes);
626 d_colIdx_ = DeviceBuffer(colidx_bytes);
627 d_values_ = DeviceBuffer(values_bytes);
629 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpyAsync(d_rowPtr_.get(), outer, rowptr_bytes, cudaMemcpyHostToDevice, stream_));
630 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpyAsync(d_colIdx_.get(), inner, colidx_bytes, cudaMemcpyHostToDevice, stream_));
631 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpyAsync(d_values_.get(), values, values_bytes, cudaMemcpyHostToDevice, stream_));
634 void create_cudss_matrix() {
635 if (d_A_cudss_) EIGEN_CUDSS_CHECK(cudssMatrixDestroy(d_A_cudss_));
637 constexpr cudss_value_type_t idx_type = to_cudss_data_type(cudss_index_type<StorageIndex>::value);
638 constexpr cudss_value_type_t val_type = to_cudss_data_type(cuda_data_type<Scalar>::value);
639 constexpr cudssMatrixType_t mtype = Derived::cudss_matrix_type();
640 constexpr cudssMatrixViewType_t mview = Derived::cudss_matrix_view();
642#if defined(CUDSS_VERSION) && CUDSS_VERSION >= 800
644 EIGEN_CUDSS_CHECK(cudssMatrixCreateCsr(&d_A_cudss_, n_, n_, nnz_, d_rowPtr_.get(),
645 nullptr, d_colIdx_.get(), d_values_.get(), idx_type, idx_type,
646 val_type, mtype, mview, CUDSS_BASE_ZERO));
648 EIGEN_CUDSS_CHECK(cudssMatrixCreateCsr(&d_A_cudss_, n_, n_, nnz_, d_rowPtr_.get(),
649 nullptr, d_colIdx_.get(), d_values_.get(), idx_type, val_type,
650 mtype, mview, CUDSS_BASE_ZERO));
654 void create_placeholder_dense() {
656 destroy_solve_descriptors();
657 if (d_x_cudss_) EIGEN_CUDSS_CHECK(cudssMatrixDestroy(d_x_cudss_));
658 if (d_b_cudss_) EIGEN_CUDSS_CHECK(cudssMatrixDestroy(d_b_cudss_));
659 constexpr cudss_value_type_t dtype = to_cudss_data_type(cuda_data_type<Scalar>::value);
660 EIGEN_CUDSS_CHECK(cudssMatrixCreateDn(&d_x_cudss_, n_, 1, n_,
nullptr, dtype, CUDSS_LAYOUT_COL_MAJOR));
661 EIGEN_CUDSS_CHECK(cudssMatrixCreateDn(&d_b_cudss_, n_, 1, n_,
nullptr, dtype, CUDSS_LAYOUT_COL_MAJOR));