3#include <AMReX_Config.H>
13namespace amrex::detail {
16template <
typename T,
template<
typename>
class V>
17CSR<T,V> spgemm_empty (
Long nrows)
20 C.row_offset.resize(nrows+1);
21 auto* p =
C.row_offset.data();
27#if !defined(AMREX_USE_GPU)
31template <
typename T,
template<
typename>
class V>
32CSR<T,V> spgemm_local_cpu (
Long nrows,
Long ncols,
33 CsrView<T const>
const& A, CsrView<T const>
const& B)
36 C.row_offset.resize(nrows+1);
45 Vector<Long> marker(ncols, -1);
47#pragma omp for schedule(dynamic,64)
49 for (
Long i = 0; i < nrows; ++i) {
51 for (
Long ap = A.row_offset[i]; ap < A.row_offset[i+1]; ++ap) {
52 Long const k = A.col_index[ap];
53 for (
Long bp = B.row_offset[k]; bp < B.row_offset[k+1]; ++bp) {
54 Long const j = B.col_index[bp];
65 for (
Long i = 0; i < nrows; ++i) { crow[i+1] += crow[i]; }
67 C.col_index.resize(
C.nnz);
76 Vector<Long> marker(ncols, -1);
80#pragma omp for schedule(dynamic,64)
82 for (
Long i = 0; i < nrows; ++i) {
84 for (
Long ap = A.row_offset[i]; ap < A.row_offset[i+1]; ++ap) {
85 Long const k = A.col_index[ap];
86 T
const a = A.mat[ap];
87 for (
Long bp = B.row_offset[k]; bp < B.row_offset[k+1]; ++bp) {
88 Long const j = B.col_index[bp];
92 acc[j] = a * B.mat[bp];
94 acc[j] += a * B.mat[bp];
98 std::sort(cols.begin(), cols.end());
100 for (
Long j : cols) {
111#elif defined(AMREX_USE_CUDA)
113template <
typename T,
template<
typename>
class V>
114CSR<T,V> spgemm_local_cusparse (
Long nrows,
Long ncols,
115 CsrView<T const>
const& A, CsrView<T const>
const& B)
117 cusparseHandle_t handle;
121 cudaDataType data_type;
122 if constexpr (std::is_same_v<T,float>) {
123 data_type = CUDA_R_32F;
124 }
else if constexpr (std::is_same_v<T,double>) {
125 data_type = CUDA_R_64F;
126 }
else if constexpr (std::is_same_v<T,GpuComplex<float>>) {
127 data_type = CUDA_C_32F;
128 }
else if constexpr (std::is_same_v<T,GpuComplex<double>>) {
129 data_type = CUDA_C_64F;
135 C.row_offset.resize(nrows+1);
137#if (CUDART_VERSION >= 13000)
139 constexpr cusparseIndexType_t index_type = CUSPARSE_INDEX_64I;
140 void* rowA = (
void*)A.row_offset;
141 void* colA = (
void*)A.col_index;
142 void* rowB = (
void*)B.row_offset;
143 void* colB = (
void*)B.col_index;
144 void* rowC = (
void*)
C.row_offset.data();
147 ncols <
Long(std::numeric_limits<int>::max()));
148 constexpr cusparseIndexType_t index_type = CUSPARSE_INDEX_32I;
149 CsrIndex<int,V> ciA, ciB, ciC;
152 ciC.row_offset.resize(nrows+1);
153 void* rowA = (
void*)ciA.row_offset.data();
154 void* colA = (
void*)ciA.col_index.data();
155 void* rowB = (
void*)ciB.row_offset.data();
156 void* colB = (
void*)ciB.col_index.data();
157 void* rowC = (
void*)ciC.row_offset.data();
160 cusparseSpMatDescr_t mat_A, mat_B, mat_C;
162 (cusparseCreateCsr(&mat_A, nrows, B.nrows, A.nnz, rowA, colA, (
void*)A.mat,
163 index_type, index_type, CUSPARSE_INDEX_BASE_ZERO, data_type));
165 (cusparseCreateCsr(&mat_B, B.nrows, ncols, B.nnz, rowB, colB, (
void*)B.mat,
166 index_type, index_type, CUSPARSE_INDEX_BASE_ZERO, data_type));
168 (cusparseCreateCsr(&mat_C, nrows, ncols, 0, rowC,
nullptr,
nullptr,
169 index_type, index_type, CUSPARSE_INDEX_BASE_ZERO, data_type));
171 cusparseSpGEMMDescr_t spgemm_descr;
176 cusparseOperation_t op = CUSPARSE_OPERATION_NON_TRANSPOSE;
178 std::size_t buffer_size1 = 0;
180 (cusparseSpGEMM_workEstimation(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_C,
181 data_type, CUSPARSE_SPGEMM_DEFAULT, spgemm_descr,
182 &buffer_size1,
nullptr));
185 (cusparseSpGEMM_workEstimation(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_C,
186 data_type, CUSPARSE_SPGEMM_DEFAULT, spgemm_descr,
187 &buffer_size1, buffer1));
189 std::size_t buffer_size2 = 0;
191 (cusparseSpGEMM_compute(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_C,
192 data_type, CUSPARSE_SPGEMM_DEFAULT, spgemm_descr,
193 &buffer_size2,
nullptr));
196 (cusparseSpGEMM_compute(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_C,
197 data_type, CUSPARSE_SPGEMM_DEFAULT, spgemm_descr,
198 &buffer_size2, buffer2));
200 std::int64_t c_nrows, c_ncols, c_nnz;
203#if (CUDART_VERSION < 13000)
208 C.col_index.resize(c_nnz);
210#if (CUDART_VERSION >= 13000)
211 void* colC = (
void*)
C.col_index.data();
213 ciC.col_index.resize(c_nnz);
214 void* colC = (
void*)ciC.col_index.data();
220 (cusparseSpGEMM_copy(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_C,
221 data_type, CUSPARSE_SPGEMM_DEFAULT, spgemm_descr));
223#if (CUDART_VERSION < 13000)
239#elif defined(AMREX_USE_HIP)
241template <
typename T,
template<
typename>
class V>
242CSR<T,V> spgemm_local_rocsparse (
Long nrows,
Long ncols,
243 CsrView<T const>
const& A, CsrView<T const>
const& B)
245 static_assert(
sizeof(
Long) == 8);
247 rocsparse_handle handle;
248 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_create_handle(&handle));
249 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_set_stream(handle,
Gpu::gpuStream()));
251 rocsparse_datatype data_type;
252 if constexpr (std::is_same_v<T,float>) {
253 data_type = rocsparse_datatype_f32_r;
254 }
else if constexpr (std::is_same_v<T,double>) {
255 data_type = rocsparse_datatype_f64_r;
256 }
else if constexpr (std::is_same_v<T,GpuComplex<float>>) {
257 data_type = rocsparse_datatype_f32_c;
258 }
else if constexpr (std::is_same_v<T,GpuComplex<double>>) {
259 data_type = rocsparse_datatype_f64_c;
264 constexpr rocsparse_indextype index_type = rocsparse_indextype_i64;
265 constexpr rocsparse_index_base index_base = rocsparse_index_base_zero;
268 C.row_offset.resize(nrows+1);
270 rocsparse_spmat_descr mat_A, mat_B, mat_C, mat_D;
271 AMREX_ROCSPARSE_SAFE_CALL
272 (rocsparse_create_csr_descr(&mat_A, nrows, B.nrows, A.nnz,
273 (
void*)A.row_offset, (
void*)A.col_index, (
void*)A.mat,
274 index_type, index_type, index_base, data_type));
275 AMREX_ROCSPARSE_SAFE_CALL
276 (rocsparse_create_csr_descr(&mat_B, B.nrows, ncols, B.nnz,
277 (
void*)B.row_offset, (
void*)B.col_index, (
void*)B.mat,
278 index_type, index_type, index_base, data_type));
279 AMREX_ROCSPARSE_SAFE_CALL
280 (rocsparse_create_csr_descr(&mat_C, nrows, ncols, 0,
281 (
void*)
C.row_offset.data(),
nullptr,
nullptr,
282 index_type, index_type, index_base, data_type));
284 AMREX_ROCSPARSE_SAFE_CALL
285 (rocsparse_create_csr_descr(&mat_D, 0, 0, 0,
nullptr,
nullptr,
nullptr,
286 index_type, index_type, index_base, data_type));
290 auto const op = rocsparse_operation_none;
291 auto const alg = rocsparse_spgemm_alg_default;
293 std::size_t buffer_size = 0;
294 AMREX_ROCSPARSE_SAFE_CALL
295 (rocsparse_spgemm(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_D, mat_C,
296 data_type, alg, rocsparse_spgemm_stage_buffer_size,
297 &buffer_size,
nullptr));
301 AMREX_ROCSPARSE_SAFE_CALL
302 (rocsparse_spgemm(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_D, mat_C,
303 data_type, alg, rocsparse_spgemm_stage_nnz,
304 &buffer_size, buffer));
306 std::int64_t c_nrows, c_ncols, c_nnz;
307 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_spmat_get_size(mat_C, &c_nrows, &c_ncols, &c_nnz));
311 C.col_index.resize(c_nnz);
313 AMREX_ROCSPARSE_SAFE_CALL
314 (rocsparse_csr_set_pointers(mat_C, (
void*)
C.row_offset.data(),
315 (
void*)
C.col_index.data(), (
void*)
C.mat.data()));
317 AMREX_ROCSPARSE_SAFE_CALL
318 (rocsparse_spgemm(handle, op, op, &alpha, mat_A, mat_B, &
beta, mat_D, mat_C,
319 data_type, alg, rocsparse_spgemm_stage_compute,
320 &buffer_size, buffer));
323 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_destroy_spmat_descr(mat_A));
324 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_destroy_spmat_descr(mat_B));
325 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_destroy_spmat_descr(mat_C));
326 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_destroy_spmat_descr(mat_D));
327 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_destroy_handle(handle));
335#elif defined(AMREX_USE_SYCL)
337template <
typename T,
template<
typename>
class V>
338CSR<T,V> spgemm_local_onemkl (
Long nrows,
Long ncols,
339 CsrView<T const>
const& A, CsrView<T const>
const& B)
341 auto& q = Gpu::Device::streamQueue();
344 C.row_offset.resize(nrows+1);
346 V<Long> dummy_col(1);
349 mkl::sparse::matrix_handle_t hA{}, hB{}, hC{};
350 mkl::sparse::init_matrix_handle(&hA);
351 mkl::sparse::init_matrix_handle(&hB);
352 mkl::sparse::init_matrix_handle(&hC);
354#if defined(INTEL_MKL_VERSION) && (INTEL_MKL_VERSION < 20250300)
355 mkl::sparse::set_csr_data(q, hA, nrows, B.nrows, mkl::index_base::zero,
356 (
Long*)A.row_offset, (
Long*)A.col_index, (T*)A.mat);
357 mkl::sparse::set_csr_data(q, hB, B.nrows, ncols, mkl::index_base::zero,
358 (
Long*)B.row_offset, (
Long*)B.col_index, (T*)B.mat);
359 mkl::sparse::set_csr_data(q, hC, nrows, ncols, mkl::index_base::zero,
360 C.row_offset.data(), dummy_col.data(), dummy_mat.data());
362 mkl::sparse::set_csr_data(q, hA, nrows, B.nrows, A.nnz, mkl::index_base::zero,
363 (
Long*)A.row_offset, (
Long*)A.col_index, (T*)A.mat);
364 mkl::sparse::set_csr_data(q, hB, B.nrows, ncols, B.nnz, mkl::index_base::zero,
365 (
Long*)B.row_offset, (
Long*)B.col_index, (T*)B.mat);
366 mkl::sparse::set_csr_data(q, hC, nrows, ncols,
Long(0), mkl::index_base::zero,
367 C.row_offset.data(), dummy_col.data(), dummy_mat.data());
370 mkl::sparse::matmat_descr_t descr =
nullptr;
371 mkl::sparse::init_matmat_descr(&descr);
372 mkl::sparse::set_matmat_data(descr,
373 mkl::sparse::matrix_view_descr::general,
374 mkl::transpose::nontrans,
375 mkl::sparse::matrix_view_descr::general,
376 mkl::transpose::nontrans,
377 mkl::sparse::matrix_view_descr::general);
379 using req = mkl::sparse::matmat_request;
382 mkl::sparse::matmat(q, hA, hB, hC, req::get_work_estimation_buf_size, descr,
383 size_buf,
nullptr, {}).wait();
384 auto* buffer1 = (
void*)
The_Arena()->
alloc(std::size_t(*size_buf));
385 mkl::sparse::matmat(q, hA, hB, hC, req::work_estimation, descr,
386 size_buf, buffer1, {}).wait();
388 mkl::sparse::matmat(q, hA, hB, hC, req::get_compute_buf_size, descr,
389 size_buf,
nullptr, {}).wait();
390 auto* buffer2 = (
void*)
The_Arena()->
alloc(std::size_t(*size_buf));
391 mkl::sparse::matmat(q, hA, hB, hC, req::compute, descr,
392 size_buf, buffer2, {}).wait();
394 mkl::sparse::matmat(q, hA, hB, hC, req::get_nnz, descr,
395 size_buf,
nullptr, {}).wait();
396 Long const c_nnz = *size_buf;
399 C.col_index.resize(c_nnz);
401#if defined(INTEL_MKL_VERSION) && (INTEL_MKL_VERSION < 20250300)
402 mkl::sparse::set_csr_data(q, hC, nrows, ncols, mkl::index_base::zero,
403 C.row_offset.data(),
C.col_index.data(),
C.mat.data());
405 mkl::sparse::set_csr_data(q, hC, nrows, ncols, c_nnz, mkl::index_base::zero,
406 C.row_offset.data(),
C.col_index.data(),
C.mat.data());
409 mkl::sparse::matmat(q, hA, hB, hC, req::finalize, descr,
410 size_buf,
nullptr, {}).wait();
412 mkl::sparse::release_matmat_descr(&descr);
413 mkl::sparse::release_matrix_handle(q, &hA);
414 mkl::sparse::release_matrix_handle(q, &hB);
415 auto ev = mkl::sparse::release_matrix_handle(q, &hC);
435template <
typename T,
template<
typename>
class V>
436CSR<T,V> spgemm_local (
Long nrows,
Long ncols,
437 CsrView<T const>
const& A, CsrView<T const>
const& B)
441 if (nrows <= 0 || ncols <= 0 || A.nnz <= 0 || B.nnz <= 0 || B.nrows <= 0) {
442 return spgemm_empty<T,V>(nrows);
445#if !defined(AMREX_USE_GPU)
446 return spgemm_local_cpu<T,V>(nrows, ncols, A, B);
447#elif defined(AMREX_USE_CUDA)
448 return spgemm_local_cusparse<T,V>(nrows, ncols, A, B);
449#elif defined(AMREX_USE_HIP)
450 return spgemm_local_rocsparse<T,V>(nrows, ncols, A, B);
451#elif defined(AMREX_USE_SYCL)
452 return spgemm_local_onemkl<T,V>(nrows, ncols, A, B);
460template <
typename T,
template<
typename>
class V,
typename F0,
typename F1>
461CSR<T,V> concat_csr_cols (
Long nrows, CsrView<T const>
const& X0, CsrView<T const>
const& X1,
462 F0
const& map0, F1
const& map1)
465 C.resize(nrows, X0.nnz + X1.nnz);
471 crow[i] = X0.row_offset[i] + X1.row_offset[i];
474 for (
Long q = X0.row_offset[i]; q < X0.row_offset[i+1]; ++q) {
475 ccol[p] = map0(X0.col_index[q]);
479 for (
Long q = X1.row_offset[i]; q < X1.row_offset[i+1]; ++q) {
480 ccol[p] = map1(X1.col_index[q]);
493template <
typename T,
template<
typename>
class V>
494void append_ext_rows (CSR<T,V>& Bh,
Long const* ext_row_offset,
Long const* ext_col,
495 T
const* ext_mat,
Long n_ext,
Long nnz_ext,
498 Long const nb = Bh.nrows();
499 Long const nnz_b = Bh.nnz;
500 Bh.mat.resize(nnz_b + nnz_ext);
501 Bh.col_index.resize(nnz_b + nnz_ext);
502 Bh.row_offset.resize(nb + n_ext + 1);
503 Bh.nnz = nnz_b + nnz_ext;
507 Long const nlocal = c1 - c0;
510 Long const b = ext_row_offset[r];
511 Long const e = ext_row_offset[r+1];
512 brow[nb+r+1] = nnz_b + e;
516 for (
Long q = p0; q < p1; ++q) {
517 bcol[p] = ext_col[q] - c0;
518 bmat[p] = ext_mat[q];
521 for (
Long q = b; q < e; ++q) {
522 if (q < p0 || q >= p1) {
524 bmat[p] = ext_mat[q];
552template <
typename T,
template <
typename>
class Allocator>
558 using csr_type =
typename SpMat::csr_type;
560 auto& Am =
const_cast<SpMat&
>(A);
561 auto& Bm =
const_cast<SpMat&
>(B);
565 using LongVec =
typename SpMat::template container_type<Long>;
567 Am.setColumnPartition(Bm.partition());
568 Bm.setColumnPartition(col_partition);
570 Long const nb = Bm.numLocalRows();
573 Long const nlocal = c1 - c0;
576 typename SpMat::RemoteRowsMM ext;
577 if (! detail::spmat_comm_is_local(Am.partition(), Bm.partition())) {
578 ext = Am.fetch_remote_rows_mm(Bm);
584 for (
Long i = 0; i < ext.nnz; ++i) {
585 auto g = ext.col_index[i];
586 if (g < c0 || g >= c1) { ru_h.push_back(g); }
592 Long const* ru = ru_d.data();
593 Long const ncols_hat = nlocal + nru;
595 csr_type Ah = detail::concat_csr_cols<T,SpMat::template container_type>
596 (nrows, Am.m_csr.const_view(), Am.remote_full_const_view(),
601 Long const* b_rcols = Bm.m_remote_cols_dv.data();
603 Long const* b_rcols = Bm.m_remote_cols_v.data();
605 csr_type Bh = detail::concat_csr_cols<T,SpMat::template container_type>
606 (nb, Bm.m_csr.const_view(), Bm.remote_full_const_view(),
612 LongVec ext_col_d(ext.nnz);
615 detail::append_ext_rows(Bh, ext.row_offset.data(), ext_col_d.data(), ext.mat,
616 ext.nrows, ext.nnz, c0, c1, ru, nru);
621 csr_type Ch = detail::spgemm_local<T,SpMat::template container_type>
622 (nrows, ncols_hat, Ah.const_view(), Bh.const_view());
629 auto* pc = Ch.col_index.data();
632 pc[i] = (c < nlocal) ? c + c0 : ru[c - nlocal];
637 SpMat
C(Am.partition(), std::move(Ch));
638 C.setColumnPartition(col_partition);
643 Am.setColumnPartition(Bm.partition());
644 Bm.setColumnPartition(col_partition);
647 csr_type Ch = detail::spgemm_local<T,SpMat::template container_type>
648 (nrows, ncols, Am.m_csr.const_view(), Bm.m_csr.const_view());
649 SpMat
C(Am.partition(), std::move(Ch));
650 C.setColumnPartition(col_partition);
General-purpose algorithm utilities available on both host and device.
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
#define AMREX_ASSUME(ASSUMPTION)
Definition AMReX_Extension.H:287
#define AMREX_RESTRICT
Definition AMReX_Extension.H:37
#define AMREX_CUSPARSE_SAFE_CALL(call)
Definition AMReX_GpuError.H:101
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
GpuArray< Real, 3 > beta
Definition AMReX_MLEBNodeFDLaplacian.cpp:1099
Definition AMReX_AlgPartition.H:21
Long numGlobalRows() const
Total number of rows covered by the partition.
Definition AMReX_AlgPartition.H:50
Long globalRowEnd() const
Exclusive global index end on this process.
Definition AMReX_AlgPartition.H:62
Long globalRowBegin() const
Inclusive global index begin on this process.
Definition AMReX_AlgPartition.H:57
virtual void free(void *pt)=0
Free a previously allocated block pointed to by pt.
virtual void * alloc(std::size_t sz)=0
Allocate sz bytes from this arena.
Distributed CSR matrix that manages storage and GPU-friendly partitions.
Definition AMReX_SpMatrix.H:63
Long numLocalRows() const
Number of rows owned by this rank.
Definition AMReX_SpMatrix.H:191
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
amrex_long Long
Definition AMReX_INT.H:30
void ParallelForOMP(T n, L const &f) noexcept
Performance-portable kernel launch function with optional OpenMP threading.
Definition AMReX_GpuLaunch.H:328
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:855
Arena * The_Arena()
Definition AMReX_Arena.cpp:815
__host__ __device__ ItType lower_bound(ItType first, ItType last, const ValType &val)
Return an iterator to the first element not less than a given value.
Definition AMReX_Algorithm.H:298
void copyAsync(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
A host-to-device copy routine. Note this is just a wrapper around memcpy, so it assumes contiguous st...
Definition AMReX_GpuContainers.H:228
static constexpr HostToDevice hostToDevice
Definition AMReX_GpuContainers.H:105
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:310
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:291
Definition AMReX_Amr.cpp:50
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:242
void RemoveDuplicates(Vector< T > &vec)
Definition AMReX_Vector.H:210
SpMatrix< T, Allocator > SpGEMM(SpMatrix< T, Allocator > const &A, SpMatrix< T, Allocator > const &B, AlgPartition const &col_partition)
Sparse matrix-matrix multiplication, C = A * B.
Definition AMReX_SpGEMM.H:554