mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
GPU: Add sparse solvers, FFT, and SpMV (cuDSS, cuFFT, cuSPARSE)
Add GPU sparse direct solvers (Cholesky, LDL^T, LU) via cuDSS, 1D/2D FFT via cuFFT with plan caching, and sparse matrix-vector/matrix multiply (SpMV/SpMM) via cuSPARSE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -547,11 +547,86 @@ if(CUDA_FOUND AND EIGEN_TEST_CUDA)
|
||||
set(EIGEN_ADD_TEST_FILENAME_EXTENSION "cu")
|
||||
endif()
|
||||
|
||||
# cuFFT test (cuFFT is part of the CUDA toolkit — no separate option needed).
|
||||
if(TARGET CUDA::cufft)
|
||||
unset(EIGEN_ADD_TEST_FILENAME_EXTENSION)
|
||||
add_executable(gpu_cufft gpu_cufft.cpp)
|
||||
target_include_directories(gpu_cufft PRIVATE
|
||||
"${CUDA_TOOLKIT_ROOT_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_link_libraries(gpu_cufft
|
||||
Eigen3::Eigen CUDA::cudart CUDA::cufft CUDA::cublas)
|
||||
target_compile_definitions(gpu_cufft PRIVATE
|
||||
EIGEN_TEST_MAX_SIZE=${EIGEN_TEST_MAX_SIZE}
|
||||
EIGEN_TEST_PART_ALL=1)
|
||||
add_test(NAME gpu_cufft COMMAND gpu_cufft)
|
||||
add_dependencies(buildtests gpu_cufft)
|
||||
add_dependencies(buildtests_gpu gpu_cufft)
|
||||
set_property(TEST gpu_cufft APPEND PROPERTY LABELS "Official;gpu")
|
||||
set_property(TEST gpu_cufft PROPERTY SKIP_RETURN_CODE 77)
|
||||
set(EIGEN_ADD_TEST_FILENAME_EXTENSION "cu")
|
||||
endif()
|
||||
|
||||
# cuSPARSE SpMV test (cuSPARSE is part of the CUDA toolkit).
|
||||
if(TARGET CUDA::cusparse)
|
||||
unset(EIGEN_ADD_TEST_FILENAME_EXTENSION)
|
||||
add_executable(gpu_cusparse_spmv gpu_cusparse_spmv.cpp)
|
||||
target_include_directories(gpu_cusparse_spmv PRIVATE
|
||||
"${CUDA_TOOLKIT_ROOT_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_link_libraries(gpu_cusparse_spmv
|
||||
Eigen3::Eigen CUDA::cudart CUDA::cusparse)
|
||||
target_compile_definitions(gpu_cusparse_spmv PRIVATE
|
||||
EIGEN_TEST_MAX_SIZE=${EIGEN_TEST_MAX_SIZE}
|
||||
EIGEN_TEST_PART_ALL=1)
|
||||
add_test(NAME gpu_cusparse_spmv COMMAND gpu_cusparse_spmv)
|
||||
add_dependencies(buildtests gpu_cusparse_spmv)
|
||||
add_dependencies(buildtests_gpu gpu_cusparse_spmv)
|
||||
set_property(TEST gpu_cusparse_spmv APPEND PROPERTY LABELS "Official;gpu")
|
||||
set_property(TEST gpu_cusparse_spmv PROPERTY SKIP_RETURN_CODE 77)
|
||||
set(EIGEN_ADD_TEST_FILENAME_EXTENSION "cu")
|
||||
endif()
|
||||
|
||||
option(EIGEN_TEST_CUSPARSE "Test cuSPARSE integration" OFF)
|
||||
if(EIGEN_TEST_CUSPARSE AND TARGET CUDA::cusparse)
|
||||
ei_add_test(gpu_cusparse "" "CUDA::cusparse")
|
||||
endif()
|
||||
|
||||
# cuDSS sparse direct solver tests.
|
||||
# cuDSS is distributed separately from the CUDA Toolkit.
|
||||
option(EIGEN_TEST_CUDSS "Test cuDSS sparse solver integration" OFF)
|
||||
if(EIGEN_TEST_CUDSS)
|
||||
find_path(CUDSS_INCLUDE_DIR cudss.h
|
||||
HINTS ${CUDSS_DIR}/include ${CUDA_TOOLKIT_ROOT_DIR}/include /usr/include)
|
||||
find_library(CUDSS_LIBRARY cudss
|
||||
HINTS ${CUDSS_DIR}/lib ${CUDSS_DIR}/lib64 ${CUDA_TOOLKIT_ROOT_DIR}/lib64 /usr/lib/x86_64-linux-gnu)
|
||||
if(CUDSS_INCLUDE_DIR AND CUDSS_LIBRARY)
|
||||
message(STATUS "cuDSS found: ${CUDSS_LIBRARY}")
|
||||
unset(EIGEN_ADD_TEST_FILENAME_EXTENSION)
|
||||
foreach(_cudss_test IN ITEMS gpu_cudss_llt gpu_cudss_ldlt gpu_cudss_lu)
|
||||
add_executable(${_cudss_test} ${_cudss_test}.cpp)
|
||||
target_include_directories(${_cudss_test} PRIVATE
|
||||
"${CUDA_TOOLKIT_ROOT_DIR}/include"
|
||||
"${CUDSS_INCLUDE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_link_libraries(${_cudss_test}
|
||||
Eigen3::Eigen CUDA::cudart CUDA::cusolver CUDA::cublas ${CUDSS_LIBRARY})
|
||||
target_compile_definitions(${_cudss_test} PRIVATE
|
||||
EIGEN_TEST_MAX_SIZE=${EIGEN_TEST_MAX_SIZE}
|
||||
EIGEN_TEST_PART_ALL=1
|
||||
EIGEN_CUDSS=1)
|
||||
add_test(NAME ${_cudss_test} COMMAND "${_cudss_test}")
|
||||
add_dependencies(buildtests ${_cudss_test})
|
||||
add_dependencies(buildtests_gpu ${_cudss_test})
|
||||
set_property(TEST ${_cudss_test} APPEND PROPERTY LABELS "Official;gpu")
|
||||
set_property(TEST ${_cudss_test} PROPERTY SKIP_RETURN_CODE 77)
|
||||
endforeach()
|
||||
set(EIGEN_ADD_TEST_FILENAME_EXTENSION "cu")
|
||||
else()
|
||||
message(WARNING "EIGEN_TEST_CUDSS=ON but cuDSS not found. Set CUDSS_DIR.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
unset(EIGEN_ADD_TEST_FILENAME_EXTENSION)
|
||||
|
||||
endif()
|
||||
|
||||
154
test/gpu_cudss_ldlt.cpp
Normal file
154
test/gpu_cudss_ldlt.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Tests for GpuSparseLDLT: GPU sparse LDL^T via cuDSS.
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
#include "main.h"
|
||||
#include <Eigen/Sparse>
|
||||
#include <Eigen/GPU>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
// ---- Helper: build a random sparse symmetric indefinite matrix ---------------
|
||||
|
||||
template <typename Scalar>
|
||||
SparseMatrix<Scalar, ColMajor, int> make_symmetric_indefinite(Index n, double density = 0.1) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
|
||||
// Build a random sparse matrix and symmetrize it.
|
||||
// The diagonal has mixed signs to ensure indefiniteness.
|
||||
SpMat R(n, n);
|
||||
R.reserve(VectorXi::Constant(n, static_cast<int>(n * density) + 1));
|
||||
for (Index j = 0; j < n; ++j) {
|
||||
for (Index i = 0; i < n; ++i) {
|
||||
if (i == j || (std::rand() / double(RAND_MAX)) < density) {
|
||||
R.insert(i, j) = Scalar(std::rand() / double(RAND_MAX) - 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
R.makeCompressed();
|
||||
|
||||
// A = R + R^H (symmetric), then add diagonal with alternating signs for indefiniteness.
|
||||
SpMat A = R + SparseMatrix<Scalar, ColMajor, int>(R.adjoint());
|
||||
for (Index i = 0; i < n; ++i) {
|
||||
Scalar diag_val = Scalar((i % 2 == 0) ? n : -n);
|
||||
A.coeffRef(i, i) += diag_val;
|
||||
}
|
||||
A.makeCompressed();
|
||||
return A;
|
||||
}
|
||||
|
||||
// ---- Solve and check residual -----------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_solve(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_symmetric_indefinite<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLDLT<Scalar> ldlt(A);
|
||||
VERIFY_IS_EQUAL(ldlt.info(), Success);
|
||||
|
||||
Vec x = ldlt.solve(b);
|
||||
VERIFY_IS_EQUAL(x.rows(), n);
|
||||
|
||||
Vec r = A * x - b;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(r.norm() / b.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Multiple RHS -----------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_multiple_rhs(Index n, Index nrhs) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Mat = Matrix<Scalar, Dynamic, Dynamic>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_symmetric_indefinite<Scalar>(n);
|
||||
Mat B = Mat::Random(n, nrhs);
|
||||
|
||||
GpuSparseLDLT<Scalar> ldlt(A);
|
||||
VERIFY_IS_EQUAL(ldlt.info(), Success);
|
||||
|
||||
Mat X = ldlt.solve(B);
|
||||
VERIFY_IS_EQUAL(X.rows(), n);
|
||||
VERIFY_IS_EQUAL(X.cols(), nrhs);
|
||||
|
||||
Mat R = A * X - B;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(R.norm() / B.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Refactorize ------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_refactorize(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_symmetric_indefinite<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLDLT<Scalar> ldlt;
|
||||
ldlt.analyzePattern(A);
|
||||
VERIFY_IS_EQUAL(ldlt.info(), Success);
|
||||
|
||||
ldlt.factorize(A);
|
||||
VERIFY_IS_EQUAL(ldlt.info(), Success);
|
||||
Vec x1 = ldlt.solve(b);
|
||||
|
||||
// Modify values, keep pattern.
|
||||
SpMat A2 = A;
|
||||
for (Index i = 0; i < n; ++i) A2.coeffRef(i, i) *= Scalar(RealScalar(2));
|
||||
|
||||
ldlt.factorize(A2);
|
||||
VERIFY_IS_EQUAL(ldlt.info(), Success);
|
||||
Vec x2 = ldlt.solve(b);
|
||||
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((A * x1 - b).norm() / b.norm() < tol);
|
||||
VERIFY((A2 * x2 - b).norm() / b.norm() < tol);
|
||||
VERIFY((x1 - x2).norm() > NumTraits<Scalar>::epsilon());
|
||||
}
|
||||
|
||||
// ---- Empty ------------------------------------------------------------------
|
||||
|
||||
void test_empty() {
|
||||
using SpMat = SparseMatrix<double, ColMajor, int>;
|
||||
SpMat A(0, 0);
|
||||
A.makeCompressed();
|
||||
GpuSparseLDLT<double> ldlt(A);
|
||||
VERIFY_IS_EQUAL(ldlt.info(), Success);
|
||||
VERIFY_IS_EQUAL(ldlt.rows(), 0);
|
||||
VERIFY_IS_EQUAL(ldlt.cols(), 0);
|
||||
}
|
||||
|
||||
// ---- Per-scalar driver ------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_scalar() {
|
||||
CALL_SUBTEST(test_solve<Scalar>(64));
|
||||
CALL_SUBTEST(test_solve<Scalar>(256));
|
||||
CALL_SUBTEST(test_multiple_rhs<Scalar>(64, 4));
|
||||
CALL_SUBTEST(test_refactorize<Scalar>(64));
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(gpu_cudss_ldlt) {
|
||||
CALL_SUBTEST(test_scalar<float>());
|
||||
CALL_SUBTEST(test_scalar<double>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<float>>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<double>>());
|
||||
CALL_SUBTEST(test_empty());
|
||||
}
|
||||
202
test/gpu_cudss_llt.cpp
Normal file
202
test/gpu_cudss_llt.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Tests for GpuSparseLLT: GPU sparse Cholesky via cuDSS.
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
#include "main.h"
|
||||
#include <Eigen/Sparse>
|
||||
#include <Eigen/GPU>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
// ---- Helper: build a random sparse SPD matrix -------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
SparseMatrix<Scalar, ColMajor, int> make_spd(Index n, double density = 0.1) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
// Uses the global std::rand state seeded by the test framework (g_seed).
|
||||
SpMat R(n, n);
|
||||
R.reserve(VectorXi::Constant(n, static_cast<int>(n * density) + 1));
|
||||
for (Index j = 0; j < n; ++j) {
|
||||
for (Index i = 0; i < n; ++i) {
|
||||
if (i == j || (std::rand() / double(RAND_MAX)) < density) {
|
||||
R.insert(i, j) = Scalar(std::rand() / double(RAND_MAX) - 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
R.makeCompressed();
|
||||
|
||||
// A = R^H * R + n * I (guaranteed SPD).
|
||||
SpMat A = R.adjoint() * R;
|
||||
for (Index i = 0; i < n; ++i) A.coeffRef(i, i) += Scalar(RealScalar(n));
|
||||
A.makeCompressed();
|
||||
return A;
|
||||
}
|
||||
|
||||
// ---- Solve and check residual -----------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_solve(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_spd<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLLT<Scalar> llt(A);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
|
||||
Vec x = llt.solve(b);
|
||||
VERIFY_IS_EQUAL(x.rows(), n);
|
||||
|
||||
// Check residual: ||Ax - b|| / ||b||.
|
||||
Vec r = A * x - b;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(r.norm() / b.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Compare with CPU SimplicialLLT -----------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_vs_cpu(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_spd<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLLT<Scalar> gpu_llt(A);
|
||||
VERIFY_IS_EQUAL(gpu_llt.info(), Success);
|
||||
Vec x_gpu = gpu_llt.solve(b);
|
||||
|
||||
SimplicialLLT<SpMat> cpu_llt(A);
|
||||
VERIFY_IS_EQUAL(cpu_llt.info(), Success);
|
||||
Vec x_cpu = cpu_llt.solve(b);
|
||||
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((x_gpu - x_cpu).norm() / x_cpu.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Multiple RHS -----------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_multiple_rhs(Index n, Index nrhs) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Mat = Matrix<Scalar, Dynamic, Dynamic>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_spd<Scalar>(n);
|
||||
Mat B = Mat::Random(n, nrhs);
|
||||
|
||||
GpuSparseLLT<Scalar> llt(A);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
|
||||
Mat X = llt.solve(B);
|
||||
VERIFY_IS_EQUAL(X.rows(), n);
|
||||
VERIFY_IS_EQUAL(X.cols(), nrhs);
|
||||
|
||||
Mat R = A * X - B;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(R.norm() / B.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Separate analyze + factorize (refactorization) -------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_refactorize(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_spd<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLLT<Scalar> llt;
|
||||
llt.analyzePattern(A);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
|
||||
// First factorize + solve.
|
||||
llt.factorize(A);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
Vec x1 = llt.solve(b);
|
||||
|
||||
// Modify values (keep same pattern): scale diagonal.
|
||||
SpMat A2 = A;
|
||||
for (Index i = 0; i < n; ++i) A2.coeffRef(i, i) *= Scalar(RealScalar(2));
|
||||
|
||||
// Refactorize with same pattern.
|
||||
llt.factorize(A2);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
Vec x2 = llt.solve(b);
|
||||
|
||||
// Both solutions should satisfy their respective systems.
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((A * x1 - b).norm() / b.norm() < tol);
|
||||
VERIFY((A2 * x2 - b).norm() / b.norm() < tol);
|
||||
|
||||
// Solutions should differ (A2 != A).
|
||||
VERIFY((x1 - x2).norm() > NumTraits<Scalar>::epsilon());
|
||||
}
|
||||
|
||||
// ---- Empty matrix -----------------------------------------------------------
|
||||
|
||||
void test_empty() {
|
||||
using SpMat = SparseMatrix<double, ColMajor, int>;
|
||||
SpMat A(0, 0);
|
||||
A.makeCompressed();
|
||||
GpuSparseLLT<double> llt(A);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
VERIFY_IS_EQUAL(llt.rows(), 0);
|
||||
VERIFY_IS_EQUAL(llt.cols(), 0);
|
||||
}
|
||||
|
||||
// ---- Upper triangle ---------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_upper(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_spd<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLLT<Scalar, Upper> llt(A);
|
||||
VERIFY_IS_EQUAL(llt.info(), Success);
|
||||
|
||||
Vec x = llt.solve(b);
|
||||
Vec r = A * x - b;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(r.norm() / b.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Per-scalar driver ------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_scalar() {
|
||||
CALL_SUBTEST(test_solve<Scalar>(64));
|
||||
CALL_SUBTEST(test_solve<Scalar>(256));
|
||||
CALL_SUBTEST(test_vs_cpu<Scalar>(64));
|
||||
CALL_SUBTEST(test_multiple_rhs<Scalar>(64, 4));
|
||||
CALL_SUBTEST(test_refactorize<Scalar>(64));
|
||||
CALL_SUBTEST(test_upper<Scalar>(64));
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(gpu_cudss_llt) {
|
||||
CALL_SUBTEST(test_scalar<float>());
|
||||
CALL_SUBTEST(test_scalar<double>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<float>>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<double>>());
|
||||
CALL_SUBTEST(test_empty());
|
||||
}
|
||||
147
test/gpu_cudss_lu.cpp
Normal file
147
test/gpu_cudss_lu.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Tests for GpuSparseLU: GPU sparse LU via cuDSS.
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
#include "main.h"
|
||||
#include <Eigen/Sparse>
|
||||
#include <Eigen/GPU>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
// ---- Helper: build a random sparse non-singular general matrix ---------------
|
||||
|
||||
template <typename Scalar>
|
||||
SparseMatrix<Scalar, ColMajor, int> make_general(Index n, double density = 0.1) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat R(n, n);
|
||||
R.reserve(VectorXi::Constant(n, static_cast<int>(n * density) + 1));
|
||||
for (Index j = 0; j < n; ++j) {
|
||||
for (Index i = 0; i < n; ++i) {
|
||||
if (i == j || (std::rand() / double(RAND_MAX)) < density) {
|
||||
R.insert(i, j) = Scalar(std::rand() / double(RAND_MAX) - 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add strong diagonal for non-singularity.
|
||||
for (Index i = 0; i < n; ++i) R.coeffRef(i, i) += Scalar(RealScalar(n));
|
||||
R.makeCompressed();
|
||||
return R;
|
||||
}
|
||||
|
||||
// ---- Solve and check residual -----------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_solve(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_general<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLU<Scalar> lu(A);
|
||||
VERIFY_IS_EQUAL(lu.info(), Success);
|
||||
|
||||
Vec x = lu.solve(b);
|
||||
VERIFY_IS_EQUAL(x.rows(), n);
|
||||
|
||||
Vec r = A * x - b;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(r.norm() / b.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Multiple RHS -----------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_multiple_rhs(Index n, Index nrhs) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Mat = Matrix<Scalar, Dynamic, Dynamic>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_general<Scalar>(n);
|
||||
Mat B = Mat::Random(n, nrhs);
|
||||
|
||||
GpuSparseLU<Scalar> lu(A);
|
||||
VERIFY_IS_EQUAL(lu.info(), Success);
|
||||
|
||||
Mat X = lu.solve(B);
|
||||
VERIFY_IS_EQUAL(X.rows(), n);
|
||||
VERIFY_IS_EQUAL(X.cols(), nrhs);
|
||||
|
||||
Mat R = A * X - B;
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY(R.norm() / B.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Refactorize ------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_refactorize(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_general<Scalar>(n);
|
||||
Vec b = Vec::Random(n);
|
||||
|
||||
GpuSparseLU<Scalar> lu;
|
||||
lu.analyzePattern(A);
|
||||
VERIFY_IS_EQUAL(lu.info(), Success);
|
||||
|
||||
lu.factorize(A);
|
||||
VERIFY_IS_EQUAL(lu.info(), Success);
|
||||
Vec x1 = lu.solve(b);
|
||||
|
||||
// Modify values, keep pattern.
|
||||
SpMat A2 = A;
|
||||
for (Index i = 0; i < n; ++i) A2.coeffRef(i, i) *= Scalar(RealScalar(2));
|
||||
|
||||
lu.factorize(A2);
|
||||
VERIFY_IS_EQUAL(lu.info(), Success);
|
||||
Vec x2 = lu.solve(b);
|
||||
|
||||
RealScalar tol = RealScalar(100) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((A * x1 - b).norm() / b.norm() < tol);
|
||||
VERIFY((A2 * x2 - b).norm() / b.norm() < tol);
|
||||
VERIFY((x1 - x2).norm() > NumTraits<Scalar>::epsilon());
|
||||
}
|
||||
|
||||
// ---- Empty ------------------------------------------------------------------
|
||||
|
||||
void test_empty() {
|
||||
using SpMat = SparseMatrix<double, ColMajor, int>;
|
||||
SpMat A(0, 0);
|
||||
A.makeCompressed();
|
||||
GpuSparseLU<double> lu(A);
|
||||
VERIFY_IS_EQUAL(lu.info(), Success);
|
||||
VERIFY_IS_EQUAL(lu.rows(), 0);
|
||||
VERIFY_IS_EQUAL(lu.cols(), 0);
|
||||
}
|
||||
|
||||
// ---- Per-scalar driver ------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_scalar() {
|
||||
CALL_SUBTEST(test_solve<Scalar>(64));
|
||||
CALL_SUBTEST(test_solve<Scalar>(256));
|
||||
CALL_SUBTEST(test_multiple_rhs<Scalar>(64, 4));
|
||||
CALL_SUBTEST(test_refactorize<Scalar>(64));
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(gpu_cudss_lu) {
|
||||
CALL_SUBTEST(test_scalar<float>());
|
||||
CALL_SUBTEST(test_scalar<double>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<float>>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<double>>());
|
||||
CALL_SUBTEST(test_empty());
|
||||
}
|
||||
186
test/gpu_cufft.cpp
Normal file
186
test/gpu_cufft.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Tests for GpuFFT: GPU FFT via cuFFT.
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
#include "main.h"
|
||||
#include <Eigen/GPU>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
// ---- 1D C2C roundtrip: inv(fwd(x)) ≈ x -------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_c2c_roundtrip(Index n) {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using Vec = Matrix<Complex, Dynamic, 1>;
|
||||
using RealScalar = Scalar;
|
||||
|
||||
Vec x = Vec::Random(n);
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
Vec X = fft.fwd(x);
|
||||
VERIFY_IS_EQUAL(X.size(), n);
|
||||
|
||||
Vec y = fft.inv(X);
|
||||
VERIFY_IS_EQUAL(y.size(), n);
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((y - x).norm() / x.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- 1D C2C known signal: FFT of constant = delta --------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_c2c_constant() {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using Vec = Matrix<Complex, Dynamic, 1>;
|
||||
using RealScalar = Scalar;
|
||||
|
||||
const int n = 64;
|
||||
Vec x = Vec::Constant(n, Complex(3.0, 0.0));
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
Vec X = fft.fwd(x);
|
||||
|
||||
// FFT of constant c: X[0] = c*n, X[k] = 0 for k > 0.
|
||||
RealScalar tol = RealScalar(10) * NumTraits<Scalar>::epsilon() * RealScalar(n);
|
||||
VERIFY(std::abs(X(0) - Complex(3.0 * n, 0.0)) < tol);
|
||||
for (int k = 1; k < n; ++k) {
|
||||
VERIFY(std::abs(X(k)) < tol);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 1D R2C/C2R roundtrip: invReal(fwd(r), n) ≈ r --------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_r2c_roundtrip(Index n) {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using CVec = Matrix<Complex, Dynamic, 1>;
|
||||
using RVec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = Scalar;
|
||||
|
||||
RVec r = RVec::Random(n);
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
CVec R = fft.fwd(r);
|
||||
|
||||
// R2C returns n/2+1 complex values.
|
||||
VERIFY_IS_EQUAL(R.size(), n / 2 + 1);
|
||||
|
||||
RVec s = fft.invReal(R, n);
|
||||
VERIFY_IS_EQUAL(s.size(), n);
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((s - r).norm() / r.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- 2D C2C roundtrip: inv2d(fwd2d(A)) ≈ A ---------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_2d_roundtrip(Index rows, Index cols) {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using Mat = Matrix<Complex, Dynamic, Dynamic>;
|
||||
using RealScalar = Scalar;
|
||||
|
||||
Mat A = Mat::Random(rows, cols);
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
Mat B = fft.fwd2d(A);
|
||||
VERIFY_IS_EQUAL(B.rows(), rows);
|
||||
VERIFY_IS_EQUAL(B.cols(), cols);
|
||||
|
||||
Mat C = fft.inv2d(B);
|
||||
VERIFY_IS_EQUAL(C.rows(), rows);
|
||||
VERIFY_IS_EQUAL(C.cols(), cols);
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar(rows * cols) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((C - A).norm() / A.norm() < tol);
|
||||
}
|
||||
|
||||
// ---- 2D C2C known signal: constant matrix -----------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_2d_constant() {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using Mat = Matrix<Complex, Dynamic, Dynamic>;
|
||||
using RealScalar = Scalar;
|
||||
|
||||
const int rows = 16, cols = 32;
|
||||
Mat A = Mat::Constant(rows, cols, Complex(2.0, 0.0));
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
Mat B = fft.fwd2d(A);
|
||||
|
||||
// 2D FFT of constant c: B(0,0) = c*rows*cols, all others = 0.
|
||||
RealScalar tol = RealScalar(10) * NumTraits<Scalar>::epsilon() * RealScalar(rows * cols);
|
||||
VERIFY(std::abs(B(0, 0) - Complex(2.0 * rows * cols, 0.0)) < tol);
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
if (i == 0 && j == 0) continue;
|
||||
VERIFY(std::abs(B(i, j)) < tol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Plan reuse: repeated calls should work ---------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_plan_reuse() {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using Vec = Matrix<Complex, Dynamic, 1>;
|
||||
using RealScalar = Scalar;
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
for (int trial = 0; trial < 5; ++trial) {
|
||||
Vec x = Vec::Random(128);
|
||||
Vec X = fft.fwd(x);
|
||||
Vec y = fft.inv(X);
|
||||
RealScalar tol = RealScalar(10) * RealScalar(128) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((y - x).norm() / x.norm() < tol);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Empty ------------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_empty() {
|
||||
using Complex = std::complex<Scalar>;
|
||||
using Vec = Matrix<Complex, Dynamic, 1>;
|
||||
|
||||
GpuFFT<Scalar> fft;
|
||||
Vec x(0);
|
||||
Vec X = fft.fwd(x);
|
||||
VERIFY_IS_EQUAL(X.size(), 0);
|
||||
Vec y = fft.inv(X);
|
||||
VERIFY_IS_EQUAL(y.size(), 0);
|
||||
}
|
||||
|
||||
// ---- Per-scalar driver ------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_scalar() {
|
||||
CALL_SUBTEST(test_c2c_roundtrip<Scalar>(64));
|
||||
CALL_SUBTEST(test_c2c_roundtrip<Scalar>(256));
|
||||
CALL_SUBTEST(test_c2c_roundtrip<Scalar>(1000)); // non-power-of-2
|
||||
CALL_SUBTEST(test_c2c_constant<Scalar>());
|
||||
CALL_SUBTEST(test_r2c_roundtrip<Scalar>(64));
|
||||
CALL_SUBTEST(test_r2c_roundtrip<Scalar>(256));
|
||||
CALL_SUBTEST(test_2d_roundtrip<Scalar>(32, 32));
|
||||
CALL_SUBTEST(test_2d_roundtrip<Scalar>(16, 64)); // non-square
|
||||
CALL_SUBTEST(test_2d_constant<Scalar>());
|
||||
CALL_SUBTEST(test_plan_reuse<Scalar>());
|
||||
CALL_SUBTEST(test_empty<Scalar>());
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(gpu_cufft) {
|
||||
CALL_SUBTEST(test_scalar<float>());
|
||||
CALL_SUBTEST(test_scalar<double>());
|
||||
}
|
||||
203
test/gpu_cusparse_spmv.cpp
Normal file
203
test/gpu_cusparse_spmv.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Tests for GpuSparseContext: GPU SpMV/SpMM via cuSPARSE.
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
#include "main.h"
|
||||
#include <Eigen/Sparse>
|
||||
#include <Eigen/GPU>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
// ---- Helper: build a random sparse matrix -----------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
SparseMatrix<Scalar, ColMajor, int> make_sparse(Index rows, Index cols, double density = 0.1) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat R(rows, cols);
|
||||
R.reserve(VectorXi::Constant(cols, static_cast<int>(rows * density) + 1));
|
||||
for (Index j = 0; j < cols; ++j) {
|
||||
for (Index i = 0; i < rows; ++i) {
|
||||
if ((std::rand() / double(RAND_MAX)) < density) {
|
||||
R.insert(i, j) = Scalar(RealScalar(std::rand() / double(RAND_MAX) - 0.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
R.makeCompressed();
|
||||
return R;
|
||||
}
|
||||
|
||||
// ---- SpMV: y = A * x -------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_spmv(Index rows, Index cols) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_sparse<Scalar>(rows, cols);
|
||||
Vec x = Vec::Random(cols);
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
Vec y_gpu = ctx.multiply(A, x);
|
||||
Vec y_cpu = A * x;
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar((std::max)(rows, cols)) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY_IS_EQUAL(y_gpu.size(), rows);
|
||||
VERIFY((y_gpu - y_cpu).norm() / (y_cpu.norm() + RealScalar(1)) < tol);
|
||||
}
|
||||
|
||||
// ---- SpMV with alpha/beta: y = alpha*A*x + beta*y ---------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_spmv_alpha_beta(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_sparse<Scalar>(n, n);
|
||||
Vec x = Vec::Random(n);
|
||||
Vec y_init = Vec::Random(n);
|
||||
|
||||
Scalar alpha(2);
|
||||
Scalar beta(3);
|
||||
|
||||
Vec y_cpu = alpha * (A * x) + beta * y_init;
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
Vec y_gpu = y_init;
|
||||
ctx.multiply(A, x, y_gpu, alpha, beta);
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY((y_gpu - y_cpu).norm() / (y_cpu.norm() + RealScalar(1)) < tol);
|
||||
}
|
||||
|
||||
// ---- Transpose: y = A^T * x ------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_spmv_transpose(Index rows, Index cols) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_sparse<Scalar>(rows, cols);
|
||||
Vec x = Vec::Random(rows);
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
Vec y_gpu = ctx.multiplyT(A, x);
|
||||
Vec y_cpu = A.transpose() * x;
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar((std::max)(rows, cols)) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY_IS_EQUAL(y_gpu.size(), cols);
|
||||
VERIFY((y_gpu - y_cpu).norm() / (y_cpu.norm() + RealScalar(1)) < tol);
|
||||
}
|
||||
|
||||
// ---- SpMM: Y = A * X (multiple RHS) ----------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_spmm(Index rows, Index cols, Index nrhs) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Mat = Matrix<Scalar, Dynamic, Dynamic>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
SpMat A = make_sparse<Scalar>(rows, cols);
|
||||
Mat X = Mat::Random(cols, nrhs);
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
Mat Y_gpu = ctx.multiplyMat(A, X);
|
||||
Mat Y_cpu = A * X;
|
||||
|
||||
RealScalar tol = RealScalar(10) * RealScalar((std::max)(rows, cols)) * NumTraits<Scalar>::epsilon();
|
||||
VERIFY_IS_EQUAL(Y_gpu.rows(), rows);
|
||||
VERIFY_IS_EQUAL(Y_gpu.cols(), nrhs);
|
||||
VERIFY((Y_gpu - Y_cpu).norm() / (Y_cpu.norm() + RealScalar(1)) < tol);
|
||||
}
|
||||
|
||||
// ---- Identity matrix: I * x = x --------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_identity(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
// Build sparse identity.
|
||||
SpMat eye(n, n);
|
||||
eye.setIdentity();
|
||||
eye.makeCompressed();
|
||||
|
||||
Vec x = Vec::Random(n);
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
Vec y = ctx.multiply(eye, x);
|
||||
|
||||
RealScalar tol = NumTraits<Scalar>::epsilon();
|
||||
VERIFY((y - x).norm() < tol);
|
||||
}
|
||||
|
||||
// ---- Context reuse ----------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_reuse(Index n) {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
using RealScalar = typename NumTraits<Scalar>::Real;
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
RealScalar tol = RealScalar(10) * RealScalar(n) * NumTraits<Scalar>::epsilon();
|
||||
|
||||
for (int trial = 0; trial < 3; ++trial) {
|
||||
SpMat A = make_sparse<Scalar>(n, n);
|
||||
Vec x = Vec::Random(n);
|
||||
Vec y_gpu = ctx.multiply(A, x);
|
||||
Vec y_cpu = A * x;
|
||||
VERIFY((y_gpu - y_cpu).norm() / (y_cpu.norm() + RealScalar(1)) < tol);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Empty ------------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_empty() {
|
||||
using SpMat = SparseMatrix<Scalar, ColMajor, int>;
|
||||
using Vec = Matrix<Scalar, Dynamic, 1>;
|
||||
|
||||
SpMat A(0, 0);
|
||||
A.makeCompressed();
|
||||
Vec x(0);
|
||||
|
||||
GpuSparseContext<Scalar> ctx;
|
||||
Vec y = ctx.multiply(A, x);
|
||||
VERIFY_IS_EQUAL(y.size(), 0);
|
||||
}
|
||||
|
||||
// ---- Per-scalar driver ------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void test_scalar() {
|
||||
CALL_SUBTEST(test_spmv<Scalar>(64, 64));
|
||||
CALL_SUBTEST(test_spmv<Scalar>(128, 64)); // non-square
|
||||
CALL_SUBTEST(test_spmv<Scalar>(64, 128)); // wide
|
||||
CALL_SUBTEST(test_spmv_alpha_beta<Scalar>(64));
|
||||
CALL_SUBTEST(test_spmv_transpose<Scalar>(128, 64));
|
||||
CALL_SUBTEST(test_spmm<Scalar>(64, 64, 4));
|
||||
CALL_SUBTEST(test_identity<Scalar>(64));
|
||||
CALL_SUBTEST(test_reuse<Scalar>(64));
|
||||
CALL_SUBTEST(test_empty<Scalar>());
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(gpu_cusparse_spmv) {
|
||||
CALL_SUBTEST(test_scalar<float>());
|
||||
CALL_SUBTEST(test_scalar<double>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<float>>());
|
||||
CALL_SUBTEST(test_scalar<std::complex<double>>());
|
||||
}
|
||||
Reference in New Issue
Block a user