diff --git a/hic/CMakeLists.txt b/hic/CMakeLists.txt index 904f7a077..a2f54ebb9 100644 --- a/hic/CMakeLists.txt +++ b/hic/CMakeLists.txt @@ -35,6 +35,7 @@ if( HAVE_CUDA ) find_package(CUDAToolkit REQUIRED) elseif( HAVE_HIP ) find_package(hip CONFIG REQUIRED) + find_package(hipsparse CONFIG REQUIRED) endif() add_subdirectory( src ) diff --git a/hic/src/CMakeLists.txt b/hic/src/CMakeLists.txt index 5048de357..42803534b 100644 --- a/hic/src/CMakeLists.txt +++ b/hic/src/CMakeLists.txt @@ -30,10 +30,17 @@ ecbuild_add_library( TARGET hic install( FILES ${PROJECT_BINARY_DIR}/src/hic/hic_config.h DESTINATION include/hic ) install( FILES hic/hic.h DESTINATION include/hic ) install( FILES hic/hic_runtime.h DESTINATION include/hic ) +install( FILES hic/hic_namespace_macro.h DESTINATION include/hic ) +install( FILES hic/hic_library_types.h DESTINATION include/hic ) +install( FILES hic/hicsparse.h DESTINATION include/hic ) +install( FILES hic/hic_dummy/dummyShouldNotBeCalled.h DESTINATION include/hic/hic_dummy ) install( FILES hic/hic_dummy/hic_dummy_runtime.h DESTINATION include/hic/hic_dummy ) +install( FILES hic/hic_dummy/hicsparse_dummy.h DESTINATION include/hic/hic_dummy ) if( HAVE_CUDA ) target_link_libraries( hic INTERFACE CUDA::cudart ) + target_link_libraries( hic INTERFACE CUDA::cusparse ) elseif( HAVE_HIP ) target_link_libraries( hic INTERFACE hip::host ) + target_link_libraries( hic INTERFACE roc::hipsparse ) endif() diff --git a/hic/src/hic/hic_dummy/dummyShouldNotBeCalled.h b/hic/src/hic/hic_dummy/dummyShouldNotBeCalled.h new file mode 100644 index 000000000..53fe40ad1 --- /dev/null +++ b/hic/src/hic/hic_dummy/dummyShouldNotBeCalled.h @@ -0,0 +1,21 @@ +/* + * (C) Copyright 2024- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ +#pragma once + +#include +#include + +namespace { + +[[noreturn]] void dummyShouldNotBeCalled(const char* symbol) { + throw std::runtime_error(std::string(symbol)+" is using the dummy backend and should not be called"); +} + +} \ No newline at end of file diff --git a/hic/src/hic/hic_dummy/hic_dummy_runtime.h b/hic/src/hic/hic_dummy/hic_dummy_runtime.h index 1560b790c..910e0378c 100644 --- a/hic/src/hic/hic_dummy/hic_dummy_runtime.h +++ b/hic/src/hic/hic_dummy/hic_dummy_runtime.h @@ -8,8 +8,7 @@ * nor does it submit to any jurisdiction. */ -#include -#include +#include "hic/hic_dummy/dummyShouldNotBeCalled.h" #define DUMMY_SHOULD_NOT_BE_CALLED(SYMBOL) dummyShouldNotBeCalled( #SYMBOL ) #define DUMMY_FUNCTION(SYMBOL) \ @@ -23,10 +22,6 @@ namespace { -[[noreturn]] void dummyShouldNotBeCalled(const char* symbol) { - throw std::runtime_error(std::string(symbol)+" is using the dummy backend and should not be called"); -} - using dummyError_t = int; using dummyEvent_t = void*; using dummyStream_t = void*; diff --git a/hic/src/hic/hic_dummy/hicsparse_dummy.h b/hic/src/hic/hic_dummy/hicsparse_dummy.h new file mode 100644 index 000000000..c9256d888 --- /dev/null +++ b/hic/src/hic/hic_dummy/hicsparse_dummy.h @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2024- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "hic/hic_dummy/dummyShouldNotBeCalled.h" + +#define DUMMY_SHOULD_NOT_BE_CALLED(SYMBOL) dummyShouldNotBeCalled( #SYMBOL ) +#define DUMMY_FUNCTION(SYMBOL) \ + template inline \ + dummysparseStatus_t dummy##SYMBOL(Args&&... args) { \ + DUMMY_SHOULD_NOT_BE_CALLED( hic##SYMBOL ); \ + return dummysparseStatus_t{0}; \ + } +#define DUMMY_VALUE(SYMBOL) \ + constexpr int dummy##SYMBOL = 0; + +namespace { + +using dummysparseHandle_t = int; +using dummysparseStatus_t = int; +using dummysparseIndexType_t = int; +using dummysparseOrder_t = int; +using dummysparseConstDnVecDescr_t = void*; +using dummysparseDnVecDescr_t = void*; +using dummysparseConstDnMatDescr_t = void*; +using dummysparseDnMatDescr_t = void*; +using dummysparseConstSpVecDescr_t = void*; +using dummysparseSpVecDescr_t = void*; +using dummysparseConstSpMatDescr_t = void*; +using dummysparseSpMatDescr_t = void*; +using dummysparseSpMVAlg_t = int; +using dummysparseSpMMAlg_t = int; + +DUMMY_FUNCTION(sparseGetErrorString) +DUMMY_FUNCTION(sparseCreate) +DUMMY_FUNCTION(sparseDestroy) +DUMMY_FUNCTION(sparseCreateConstDnVec) +DUMMY_FUNCTION(sparseCreateDnVec) +DUMMY_FUNCTION(sparseDestroyDnVec) +DUMMY_FUNCTION(sparseCreateConstDnMat) +DUMMY_FUNCTION(sparseCreateDnMat) +DUMMY_FUNCTION(sparseDestroyDnMat) +DUMMY_FUNCTION(sparseCreateConstSpVec) +DUMMY_FUNCTION(sparseCreateSpVec) +DUMMY_FUNCTION(sparseDestroySpVec) +DUMMY_FUNCTION(sparseCreateConstCsr) +DUMMY_FUNCTION(sparseDestroySpMat) +DUMMY_FUNCTION(sparseSpMV_bufferSize) +DUMMY_FUNCTION(sparseSpMV_preprocess) +DUMMY_FUNCTION(sparseSpMV) +DUMMY_FUNCTION(sparseSpMM_bufferSize) +DUMMY_FUNCTION(sparseSpMM_preprocess) +DUMMY_FUNCTION(sparseSpMM) + +DUMMY_VALUE(SPARSE_STATUS_SUCCESS) +DUMMY_VALUE(SPARSE_ORDER_COL) +DUMMY_VALUE(SPARSE_ORDER_ROW) +DUMMY_VALUE(SPARSE_INDEX_32I) +DUMMY_VALUE(SPARSE_INDEX_64I) +DUMMY_VALUE(SPARSE_INDEX_BASE_ZERO) +DUMMY_VALUE(SPARSE_INDEX_BASE_ONE) +DUMMY_VALUE(SPARSE_SPMV_ALG_DEFAULT) +DUMMY_VALUE(SPARSE_SPMM_ALG_DEFAULT) +DUMMY_VALUE(SPARSE_OPERATION_NON_TRANSPOSE) +DUMMY_VALUE(SPARSE_OPERATION_TRANSPOSE) +DUMMY_VALUE(SPARSE_OPERATION_CONJUGATE_TRANSPOSE) + +} + +#undef DUMMY_FUNCTION +#undef DUMMY_VALUE +#undef DUMMY_SHOULD_NOT_BE_CALLED \ No newline at end of file diff --git a/hic/src/hic/hic_library_types.h b/hic/src/hic/hic_library_types.h new file mode 100644 index 000000000..c9c612b5a --- /dev/null +++ b/hic/src/hic/hic_library_types.h @@ -0,0 +1,43 @@ +/* + * (C) Copyright 2024- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ +#pragma once + +#include "hic/hic_namespace_macro.h" + +#if HIC_BACKEND_CUDA + #include +#elif HIC_BACKEND_HIP + #include +#elif HIC_BACKEND_DUMMY + // intentionally blank +#else + #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY +#endif + +//------------------------------------------------ +HIC_NAMESPACE_BEGIN +//------------------------------------------------ + +#if HIC_BACKEND_CUDA + constexpr decltype(CUDA_R_32F) HIC_R_32F = CUDA_R_32F; + constexpr decltype(CUDA_R_64F) HIC_R_64F = CUDA_R_64F; +#elif HIC_BACKEND_HIP + constexpr decltype(HIP_R_32F) HIC_R_32F = HIP_R_32F; + constexpr decltype(HIP_R_64F) HIC_R_64F = HIP_R_64F; +#elif HIC_BACKEND_DUMMY + constexpr int HIC_R_32F = 0; + constexpr int HIC_R_64F = 0; +#else + #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY +#endif + +//------------------------------------------------ +HIC_NAMESPACE_END +//------------------------------------------------ \ No newline at end of file diff --git a/hic/src/hic/hic_namespace_macro.h b/hic/src/hic/hic_namespace_macro.h new file mode 100644 index 000000000..af602b3a6 --- /dev/null +++ b/hic/src/hic/hic_namespace_macro.h @@ -0,0 +1,19 @@ +/* + * (C) Copyright 2024- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ +#pragma once + +#if !defined(HIC_NAMESPACE) + #define HIC_NAMESPACE + #define HIC_NAMESPACE_BEGIN + #define HIC_NAMESPACE_END +#else + #define HIC_NAMESPACE_BEGIN namespace HIC_NAMESPACE { + #define HIC_NAMESPACE_END } +#endif \ No newline at end of file diff --git a/hic/src/hic/hic_runtime.h b/hic/src/hic/hic_runtime.h index 25af23cb0..1e4553193 100644 --- a/hic/src/hic/hic_runtime.h +++ b/hic/src/hic/hic_runtime.h @@ -9,14 +9,7 @@ */ #pragma once -#if !defined(HIC_NAMESPACE) - #define HIC_NAMESPACE - #define HIC_NAMESPACE_BEGIN - #define HIC_NAMESPACE_END -#else - #define HIC_NAMESPACE_BEGIN namespace HIC_NAMESPACE { - #define HIC_NAMESPACE_END } -#endif +#include "hic/hic_namespace_macro.h" #if HIC_BACKEND_CUDA #define HIC_BACKEND cuda diff --git a/hic/src/hic/hicsparse.h b/hic/src/hic/hicsparse.h new file mode 100644 index 000000000..cde17727d --- /dev/null +++ b/hic/src/hic/hicsparse.h @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2024- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ +#pragma once + +#include "hic/hic_namespace_macro.h" +#include "hic/hic_library_types.h" + +#if HIC_BACKEND_CUDA + #define HICSPARSE_BACKEND_PREFIX cu + #define HICSPARSE_BACKEND_PREFIX_CAPS CU + #include +#elif HIC_BACKEND_HIP + #define HICSPARSE_BACKEND_PREFIX hip + #define HICSPARSE_BACKEND_PREFIX_CAPS HIP + #include +#elif HIC_BACKEND_DUMMY + #define HICSPARSE_BACKEND_PREFIX dummy + #define HICSPARSE_BACKEND_PREFIX_CAPS dummy + #include "hic/hic_dummy/hicsparse_dummy.h" +#else + #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY +#endif + +#define HIC_PREFIX hic +#define HIC_PREFIX_CAPS HIC +#define HIC_CONCAT_(A, B) A ## B +#define HIC_CONCAT(A, B) HIC_CONCAT_(A, B) +#define HIC_SYMBOL(API) HIC_CONCAT(HIC_PREFIX, API) +#define HIC_SYMBOL_CAPS(API) HIC_CONCAT(HIC_PREFIX_CAPS, API) + +#define HIC_TYPE(TYPE) \ + using HIC_SYMBOL(TYPE) = HIC_CONCAT(HICSPARSE_BACKEND_PREFIX, TYPE); + +#define HIC_FUNCTION(FUNCTION) \ + template inline \ + auto HIC_SYMBOL(FUNCTION)(Args&&... args) -> decltype(HIC_CONCAT(HICSPARSE_BACKEND_PREFIX, FUNCTION)(std::forward(args)...)) { \ + return HIC_CONCAT(HICSPARSE_BACKEND_PREFIX, FUNCTION)(std::forward(args)...); \ + } + +#define HIC_VALUE(VALUE) \ + constexpr decltype(HIC_CONCAT(HICSPARSE_BACKEND_PREFIX_CAPS, VALUE)) HIC_SYMBOL_CAPS(VALUE) = HIC_CONCAT(HICSPARSE_BACKEND_PREFIX_CAPS, VALUE); + +//------------------------------------------------ +HIC_NAMESPACE_BEGIN +//------------------------------------------------ + +HIC_TYPE(sparseHandle_t) +HIC_TYPE(sparseStatus_t) +HIC_TYPE(sparseIndexType_t) +HIC_TYPE(sparseOrder_t) +HIC_TYPE(sparseConstDnVecDescr_t) +HIC_TYPE(sparseDnVecDescr_t) +HIC_TYPE(sparseConstDnMatDescr_t) +HIC_TYPE(sparseDnMatDescr_t) +HIC_TYPE(sparseConstSpVecDescr_t) +HIC_TYPE(sparseSpVecDescr_t) +HIC_TYPE(sparseConstSpMatDescr_t) +HIC_TYPE(sparseSpMatDescr_t) +HIC_TYPE(sparseSpMVAlg_t) +HIC_TYPE(sparseSpMMAlg_t) + +HIC_FUNCTION(sparseGetErrorString) +HIC_FUNCTION(sparseCreate) +HIC_FUNCTION(sparseDestroy) +HIC_FUNCTION(sparseCreateConstDnVec) +HIC_FUNCTION(sparseCreateDnVec) +HIC_FUNCTION(sparseDestroyDnVec) +HIC_FUNCTION(sparseCreateConstDnMat) +HIC_FUNCTION(sparseCreateDnMat) +HIC_FUNCTION(sparseDestroyDnMat) +HIC_FUNCTION(sparseCreateConstSpVec) +HIC_FUNCTION(sparseCreateSpVec) +HIC_FUNCTION(sparseDestroySpVec) +HIC_FUNCTION(sparseCreateConstCsr) +HIC_FUNCTION(sparseDestroySpMat) +HIC_FUNCTION(sparseSpMV_bufferSize) +HIC_FUNCTION(sparseSpMV_preprocess) +HIC_FUNCTION(sparseSpMV) +HIC_FUNCTION(sparseSpMM_bufferSize) +HIC_FUNCTION(sparseSpMM_preprocess) +HIC_FUNCTION(sparseSpMM) + +HIC_VALUE(SPARSE_STATUS_SUCCESS) +HIC_VALUE(SPARSE_ORDER_COL) +HIC_VALUE(SPARSE_ORDER_ROW) +HIC_VALUE(SPARSE_INDEX_32I) +HIC_VALUE(SPARSE_INDEX_64I) +HIC_VALUE(SPARSE_INDEX_BASE_ZERO) +HIC_VALUE(SPARSE_INDEX_BASE_ONE) +HIC_VALUE(SPARSE_SPMV_ALG_DEFAULT) +HIC_VALUE(SPARSE_SPMM_ALG_DEFAULT) +HIC_VALUE(SPARSE_OPERATION_NON_TRANSPOSE) +HIC_VALUE(SPARSE_OPERATION_TRANSPOSE) +HIC_VALUE(SPARSE_OPERATION_CONJUGATE_TRANSPOSE) + +#if HIC_BACKEND_DUMMY +#define HICSPARSE_CALL(val) +#else +#define HICSPARSE_CALL(val) hicsparse_assert((val), #val, __FILE__, __LINE__) +#endif + +inline void hicsparse_assert(hicsparseStatus_t status, const char* const func, const char* const file, const int line) { + // Ignore errors when HIP/CUDA runtime is unloaded or deinitialized. + // This happens when calling HIP/CUDA after main has ended, e.g. in teardown of static variables calling `hicFree` + // --> ignore hicErrorDeinitialized (a.k.a. cudaErrorCudartUnloading / hipErrorDeinitialized) + if (status != HICSPARSE_STATUS_SUCCESS) { + std::ostringstream msg; + msg << "HIC Runtime Error [code="< +#include +#include +#include +#include + +#include "hic/hic.h" +#include "hic/hicsparse.h" + +// ----------------------------------------------------------------------------- + +int test_hicsparseCreate() { + std::cout << "--- " << __FUNCTION__ << std::endl; + hicsparseHandle_t handle; + HICSPARSE_CALL( hicsparseCreate(&handle) ); + HICSPARSE_CALL( hicsparseDestroy(handle) ); + std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; + return 0; // success +} + +// ----------------------------------------------------------------------------- + +int test_hicsparseSpMV() { + std::cout << "--- " << __FUNCTION__ << std::endl; + + // Create a sparse matrix + const int rows = 3; + const int cols = 3; + const int nnz = 3; + double values[nnz] = {1.0, 2.0, 3.0}; + int row_offsets[rows+1] = {0, 1, 2, 3}; + int column_indices[nnz] = {0, 1, 2}; + + // Put the sparse matrix onto the device + double* dvalues; + int* drow_offsets; + int* dcolumn_indices; + HIC_CALL( hicMalloc((void**)&dvalues, nnz * sizeof(double)) ); + HIC_CALL( hicMalloc((void**)&drow_offsets, (rows+1) * sizeof(int)) ); + HIC_CALL( hicMalloc((void**)&dcolumn_indices, nnz * sizeof(int)) ); + HIC_CALL( hicMemcpy(dvalues, values, nnz * sizeof(double), hicMemcpyHostToDevice) ); + HIC_CALL( hicMemcpy(drow_offsets, row_offsets, (rows+1) * sizeof(int), hicMemcpyHostToDevice) ); + HIC_CALL( hicMemcpy(dcolumn_indices, column_indices, nnz * sizeof(int), hicMemcpyHostToDevice) ); + + // Create a dense vector + const int N = 3; + double x[N] = {1.0, 2.0, 3.0}; + double y[N] = {0.0, 0.0, 0.0}; + + // Put the dense vector onto the device + double* dx; + double* dy; + HIC_CALL( hicMalloc((void**)&dx, N * sizeof(double)) ); + HIC_CALL( hicMalloc((void**)&dy, N * sizeof(double)) ); + HIC_CALL( hicMemcpy(dx, x, N * sizeof(double), hicMemcpyHostToDevice) ); + HIC_CALL( hicMemcpy(dy, y, N * sizeof(double), hicMemcpyHostToDevice) ); + + // Create sparse library handle + hicsparseHandle_t handle; + HICSPARSE_CALL( hicsparseCreate(&handle) ); + + // Create a sparse matrix descriptor + hicsparseConstSpMatDescr_t matA; + HICSPARSE_CALL( hicsparseCreateConstCsr( + &matA, + rows, cols, nnz, + drow_offsets, + dcolumn_indices, + dvalues, + HICSPARSE_INDEX_32I, + HICSPARSE_INDEX_32I, + HICSPARSE_INDEX_BASE_ZERO, + HIC_R_64F) ); + + // Create dense matrix descriptors + hicsparseConstDnVecDescr_t vecX; + HICSPARSE_CALL( hicsparseCreateConstDnVec( + &vecX, + N, + dx, + HIC_R_64F) ); + + hicsparseDnVecDescr_t vecY; + HICSPARSE_CALL( hicsparseCreateDnVec( + &vecY, + N, + dy, + HIC_R_64F) ); + + // Set parameters + const double alpha = 1.0; + const double beta = 0.0; + + // Determine buffer size + size_t bufferSize = 0; + HICSPARSE_CALL( hicsparseSpMV_bufferSize( + handle, + HICSPARSE_OPERATION_NON_TRANSPOSE, + &alpha, + matA, + vecX, + &beta, + vecY, + HIC_R_64F, + HICSPARSE_SPMV_ALG_DEFAULT, + &bufferSize) ); + + // Allocate buffer + char* buffer; + HIC_CALL( hicMalloc(&buffer, bufferSize) ); + + // Perform SpMV + HICSPARSE_CALL( hicsparseSpMV( + handle, + HICSPARSE_OPERATION_NON_TRANSPOSE, + &alpha, + matA, + vecX, + &beta, + vecY, + HIC_R_64F, + HICSPARSE_SPMV_ALG_DEFAULT, + buffer) ); + + // Copy result back to host + HIC_CALL( hicMemcpy(y, dy, N * sizeof(double), hicMemcpyDeviceToHost) ); + + // Check result + const double expected_y[N] = {1.0, 4.0, 9.0}; + for( int i = 0; i < N; ++i ) { + if( y[i] != expected_y[i] ) { + throw std::runtime_error("Error: y[" + std::to_string(i) + "] = " + std::to_string(y[i]) + " != " + std::to_string(expected_y[i])); + } + } + + // Clean up + HIC_CALL( hicFree(dy) ); + HIC_CALL( hicFree(dx) ); + HIC_CALL( hicFree(dcolumn_indices) ); + HIC_CALL( hicFree(drow_offsets) ); + HIC_CALL( hicFree(dvalues) ); + HICSPARSE_CALL( hicsparseDestroyDnVec(vecY) ); + HICSPARSE_CALL( hicsparseDestroyDnVec(vecX) ); + HICSPARSE_CALL( hicsparseDestroySpMat(matA) ); + HICSPARSE_CALL( hicsparseDestroy(handle) ); + + std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; + return 0; // success +} + +// ----------------------------------------------------------------------------- + +int test_hicsparseSpMM() { + std::cout << "--- " << __FUNCTION__ << std::endl; + + // Create a sparse matrix + const int rows = 3; + const int cols = 3; + const int nnz = 3; + double values[nnz] = {1.0, 2.0, 3.0}; + int row_offsets[rows + 1] = {0, 1, 2, 3}; + int column_indices[nnz] = {0, 1, 2}; + + // Put the sparse matrix onto the device + double* dvalues; + int* drow_offsets; + int* dcolumn_indices; + HIC_CALL(hicMalloc((void**)&dvalues, nnz * sizeof(double))); + HIC_CALL(hicMalloc((void**)&drow_offsets, (rows + 1) * sizeof(int))); + HIC_CALL(hicMalloc((void**)&dcolumn_indices, nnz * sizeof(int))); + HIC_CALL(hicMemcpy(dvalues, values, nnz * sizeof(double), hicMemcpyHostToDevice)); + HIC_CALL(hicMemcpy(drow_offsets, row_offsets, (rows + 1) * sizeof(int), hicMemcpyHostToDevice)); + HIC_CALL(hicMemcpy(dcolumn_indices, column_indices, nnz * sizeof(int), hicMemcpyHostToDevice)); + + // Create dense matrices + const int B_rows = 3; + const int B_cols = 3; + double B[B_rows * B_cols] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; + double C[rows * B_cols] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + // Put the dense matrices onto the device + double* dB; + double* dC; + HIC_CALL(hicMalloc((void**)&dB, B_rows * B_cols * sizeof(double))); + HIC_CALL(hicMalloc((void**)&dC, rows * B_cols * sizeof(double))); + HIC_CALL(hicMemcpy(dB, B, B_rows * B_cols * sizeof(double), hicMemcpyHostToDevice)); + HIC_CALL(hicMemcpy(dC, C, rows * B_cols * sizeof(double), hicMemcpyHostToDevice)); + + // Create sparse library handle + hicsparseHandle_t handle; + HICSPARSE_CALL(hicsparseCreate(&handle)); + + // Create a sparse matrix descriptor + hicsparseConstSpMatDescr_t matA; + HICSPARSE_CALL(hicsparseCreateConstCsr( + &matA, + rows, cols, nnz, + drow_offsets, + dcolumn_indices, + dvalues, + HICSPARSE_INDEX_32I, + HICSPARSE_INDEX_32I, + HICSPARSE_INDEX_BASE_ZERO, + HIC_R_64F)); + + // Create dense matrix descriptors + hicsparseConstDnMatDescr_t matB; + HICSPARSE_CALL(hicsparseCreateConstDnMat( + &matB, + B_rows, + B_cols, + B_cols, + dB, + HIC_R_64F, + HICSPARSE_ORDER_ROW)); + + hicsparseDnMatDescr_t matC; + HICSPARSE_CALL(hicsparseCreateDnMat( + &matC, + rows, + B_cols, + B_cols, + dC, + HIC_R_64F, + HICSPARSE_ORDER_ROW)); + + // Set parameters + const double alpha = 1.0; + const double beta = 0.0; + + // Determine buffer size + size_t bufferSize = 0; + HICSPARSE_CALL(hicsparseSpMM_bufferSize( + handle, + HICSPARSE_OPERATION_NON_TRANSPOSE, + HICSPARSE_OPERATION_NON_TRANSPOSE, + &alpha, + matA, + matB, + &beta, + matC, + HIC_R_64F, + HICSPARSE_SPMM_ALG_DEFAULT, + &bufferSize)); + + // Allocate buffer + char* buffer; + HIC_CALL(hicMalloc(&buffer, bufferSize)); + + // Perform SpMM + HICSPARSE_CALL(hicsparseSpMM( + handle, + HICSPARSE_OPERATION_NON_TRANSPOSE, + HICSPARSE_OPERATION_NON_TRANSPOSE, + &alpha, + matA, + matB, + &beta, + matC, + HIC_R_64F, + HICSPARSE_SPMM_ALG_DEFAULT, + buffer)); + + // Copy result back to host + HIC_CALL(hicMemcpy(C, dC, rows * B_cols * sizeof(double), hicMemcpyDeviceToHost)); + + // Check result + const double expected_C[rows * B_cols] = {1.0, 2.0, 3.0, 8.0, 10.0, 12.0, 21.0, 24.0, 27.0}; + for (int i = 0; i < rows * B_cols; ++i) { + if (C[i] != expected_C[i]) { + throw std::runtime_error("Error: C[" + std::to_string(i) + "] = " + std::to_string(C[i]) + " != " + std::to_string(expected_C[i])); + } + } + + // Clean up + HIC_CALL(hicFree(dC)); + HIC_CALL(hicFree(dB)); + HIC_CALL(hicFree(dcolumn_indices)); + HIC_CALL(hicFree(drow_offsets)); + HIC_CALL(hicFree(dvalues)); + HICSPARSE_CALL(hicsparseDestroyDnMat(matC)); + HICSPARSE_CALL(hicsparseDestroyDnMat(matB)); + HICSPARSE_CALL(hicsparseDestroySpMat(matA)); + HICSPARSE_CALL(hicsparseDestroy(handle)); + + std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; + return 0; // success +} + +// ----------------------------------------------------------------------------- + +std::vector> tests = { + test_hicsparseCreate, + test_hicsparseSpMV, + test_hicsparseSpMM, +}; + +int main(int argc, char* argv[]) { + int num_devices = 0; + hicGetDeviceCount(&num_devices); + if( num_devices == 0 ) { + std::ignore = hicGetLastError(); + std::cout << "TEST IGNORED, hicGetDeviceCount -> 0" << std::endl; + return 0; + } + std::cout << "hicGetDeviceCount -> " << num_devices << std::endl; + int error = 0; + for( auto& test: tests) { + try { + error += test(); + } + catch( std::exception& e ) { + error += 1; + std::cout << e.what() << std::endl; + } + } + return error; +}