diff --git a/unit_test/CMakeLists.txt b/unit_test/CMakeLists.txt index 10cf434515..fdef92424f 100644 --- a/unit_test/CMakeLists.txt +++ b/unit_test/CMakeLists.txt @@ -231,13 +231,5 @@ IF (Kokkos_ENABLE_Pthread) ) ENDIF () -TRIBITS_ADD_EXECUTABLE_AND_TEST( - GitHubIssue101 - SOURCES - Test_Main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/sparse/Test_GitHubIssue101.cpp - NUM_MPI_PROCS 1 - TESTONLYLIBS kokkoskernels_gtest -) diff --git a/unit_test/sparse/Test_GitHubIssue101.cpp b/unit_test/sparse/Test_GitHubIssue101.cpp deleted file mode 100644 index d041ea8929..0000000000 --- a/unit_test/sparse/Test_GitHubIssue101.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include -#include "Kokkos_Core.hpp" -#include "KokkosSparse_spmv.hpp" -#include -#include - -namespace { // (anonymous) - -// See GitHub Issue #101 for details: -// -// https://github.com/kokkos/kokkos-kernels/issues/101 -// -// Create a 1x2 matrix A = [1, eps_f/2], where eps_f is machine -// epsilon for float, and multiply it by x = [1, 1]^T. If y = A*x and -// x and y are both in double precision, then KokkosSparse::spmv -// should compute the result in double precision ("double"), not -// single precision ("float"). If it computes in double precision, -// then the result should not equal 1 (or 0); if it computes in single -// precison, then the result should equal 1. -template -void -test_github_issue_101 () -{ - typedef KokkosSparse::CrsMatrix float_matrix_type; - typedef KokkosSparse::CrsMatrix double_matrix_type; - static_assert (std::is_same::value, - "Two KokkosSparse::CrsMatrix types that differ only in the type of " - "matrix values, appear to have two different StaticCrsGraphType " - "typedefs. This should never happen."); - typedef typename float_matrix_type::StaticCrsGraphType graph_type; - - constexpr int numRows = 1; - constexpr int numCols = 2; - constexpr double alpha_d = 1.0; - constexpr double beta_d = 0.0; - const float EPS_f = std::numeric_limits::epsilon (); - - graph_type G; - { - typename graph_type::entries_type colInds ("colInds", numCols); - auto colInds_h = Kokkos::create_mirror_view (colInds); - colInds_h[0] = 0; - colInds_h[1] = 1; - Kokkos::deep_copy (colInds, colInds_h); - - typedef typename graph_type::row_map_type::non_const_type row_offsets_type; - row_offsets_type rowOffsets ("rowOffsets", numRows+1); - auto rowOffsets_h = Kokkos::create_mirror_view (rowOffsets); - rowOffsets_h[0] = 0; // Entries start at offset 0 - rowOffsets_h[1] = 2; // 2 entries total in the "sparse" matrix - Kokkos::deep_copy (rowOffsets, rowOffsets_h); - - G = graph_type (colInds, rowOffsets); - } - - Kokkos::View x ("x", numCols); - Kokkos::deep_copy (x, static_cast (1.0)); - Kokkos::View y ("y", numRows); - auto y_h = Kokkos::create_mirror_view (y); // we'll want this later - - // Pick some number large enough to exercise all unrolling cases. - // Sparse mat-vec does or at least used to unroll for 1, 2, ..., 17 - // vectors. Include a little extra in case the implementers decide - // to strip-mine that. - constexpr int numVecs = 22; - Kokkos::View X ("X", numCols, numVecs); - Kokkos::deep_copy (X, static_cast (1.0)); - Kokkos::View Y ("Y", numRows, numVecs); - auto Y_h = Kokkos::create_mirror_view (Y); // we'll want this later - - // Start with the easy test case, where the matrix and the vectors - // are all double. - { - constexpr double ZERO_d = static_cast (0.0); - constexpr double ONE_d = static_cast (1.0); - constexpr double TWO_d = static_cast (2.0); - - double_matrix_type A_d ("A_d", G); - auto A_d_val_h = Kokkos::create_mirror_view (A_d.values); - A_d_val_h[0] = ONE_d; - // This cast is deliberate; we want to use float eps here, but as - // a double-precision number. This is just a sanity check for - // accuracy of the sparse mat-vec when not using mixed precision. - A_d_val_h[1] = static_cast (EPS_f) / TWO_d; - EXPECT_NE( A_d_val_h[1], ZERO_d ); // just making sure - Kokkos::deep_copy (A_d.values, A_d_val_h); - - // Just to make sure, we purge the previous contents of y, - // before doing the sparse mat-vec. - Kokkos::deep_copy (y, ZERO_d); - KokkosSparse::spmv ("N", alpha_d, A_d, x, beta_d, y); - - Kokkos::deep_copy (y_h, y); - const double expectedResult_allDouble = static_cast (1.0) + - static_cast (EPS_f) / static_cast (2.0); - EXPECT_NE( expectedResult_allDouble, ZERO_d ); - EXPECT_EQ( y_h[0], expectedResult_allDouble ); - - for (int curNumVecs = 1; curNumVecs <= numVecs; ++curNumVecs) { - const Kokkos::pair vecRng (0, curNumVecs); - auto X_sub = Kokkos::subview (X, Kokkos::ALL (), vecRng); - auto Y_sub = Kokkos::subview (Y, Kokkos::ALL (), vecRng); - - // Just to make sure, we purge the previous contents of Y, - // before doing the sparse mat-vec. - Kokkos::deep_copy (Y, ZERO_d); - KokkosSparse::spmv ("N", alpha_d, A_d, X, beta_d, Y); - - Kokkos::deep_copy (Y_h, Y); - for (int j = 0; j < curNumVecs; ++j) { - const double actualResult = Y_h(0,j); - EXPECT_EQ( actualResult, expectedResult_allDouble ); - } - } - } - - // Now exercise the case where the matrix is in float, but the - // vectors are in double. - { - constexpr float ZERO_f = static_cast (0.0); - constexpr float ONE_f = static_cast (1.0); - constexpr float TWO_f = static_cast (2.0); - constexpr double ZERO_d = static_cast (0.0); - - float_matrix_type A_f ("A_f", G); - auto A_f_val_h = Kokkos::create_mirror_view (A_f.values); - A_f_val_h[0] = ONE_f; - A_f_val_h[1] = EPS_f / TWO_f; - EXPECT_NE( A_f_val_h[1], ZERO_f ); // just making sure - Kokkos::deep_copy (A_f.values, A_f_val_h); - - // Just to make sure, we purge the previous contents of y, - // before doing the sparse mat-vec. - Kokkos::deep_copy (y, ZERO_d); - KokkosSparse::spmv ("N", alpha_d, A_f, x, beta_d, y); - - Kokkos::deep_copy (y_h, y); - const double expectedResult_mixed = static_cast (1.0) + - static_cast (EPS_f) / static_cast (2.0); - EXPECT_NE( expectedResult_mixed, ZERO_d ); - EXPECT_EQ( y_h[0], expectedResult_mixed ); - - for (int curNumVecs = 1; curNumVecs <= numVecs; ++curNumVecs) { - const Kokkos::pair vecRng (0, curNumVecs); - auto X_sub = Kokkos::subview (X, Kokkos::ALL (), vecRng); - auto Y_sub = Kokkos::subview (Y, Kokkos::ALL (), vecRng); - - // Just to make sure, we purge the previous contents of Y, - // before doing the sparse mat-vec. - Kokkos::deep_copy (Y, ZERO_d); - KokkosSparse::spmv ("N", alpha_d, A_f, X, beta_d, Y); - - Kokkos::deep_copy (Y_h, Y); - for (int j = 0; j < curNumVecs; ++j) { - const double actualResult = Y_h(0,j); - EXPECT_EQ( actualResult, expectedResult_mixed ); - } - } - } -} - -TEST( KokkosSparse, GitHubIssue101 ) { -#if defined(KOKKOS_ENABLE_CUDA) - typedef Kokkos::Device device_type; - typedef Kokkos::View::HostMirror::device_type::execution_space host_execution_space; - typedef Kokkos::Device host_device_type; - - test_github_issue_101 (); - test_github_issue_101 (); -#else - typedef Kokkos::View::device_type device_type; - test_github_issue_101 (); -#endif // defined(KOKKOS_ENABLE_CUDA) -} - -} // namespace (anonymous) - diff --git a/unit_test/sparse/Test_Sparse_spmv.hpp b/unit_test/sparse/Test_Sparse_spmv.hpp index 7cf3f90918..801380c842 100644 --- a/unit_test/sparse/Test_Sparse_spmv.hpp +++ b/unit_test/sparse/Test_Sparse_spmv.hpp @@ -182,6 +182,153 @@ void test_spmv_mv(lno_t numRows,size_type nnz, lno_t bandwidth, lno_t row_size_v } +//call it if ordinal int and, scalar float and double are instantiated. +template +void test_github_issue_101 () +{ + typedef KokkosSparse::CrsMatrix float_matrix_type; + typedef KokkosSparse::CrsMatrix double_matrix_type; + static_assert (std::is_same::value, + "Two KokkosSparse::CrsMatrix types that differ only in the type of " + "matrix values, appear to have two different StaticCrsGraphType " + "typedefs. This should never happen."); + typedef typename float_matrix_type::StaticCrsGraphType graph_type; + + constexpr int numRows = 1; + constexpr int numCols = 2; + constexpr double alpha_d = 1.0; + constexpr double beta_d = 0.0; + const float EPS_f = std::numeric_limits::epsilon (); + + graph_type G; + { + typename graph_type::entries_type colInds ("colInds", numCols); + auto colInds_h = Kokkos::create_mirror_view (colInds); + colInds_h[0] = 0; + colInds_h[1] = 1; + Kokkos::deep_copy (colInds, colInds_h); + + typedef typename graph_type::row_map_type::non_const_type row_offsets_type; + row_offsets_type rowOffsets ("rowOffsets", numRows+1); + auto rowOffsets_h = Kokkos::create_mirror_view (rowOffsets); + rowOffsets_h[0] = 0; // Entries start at offset 0 + rowOffsets_h[1] = 2; // 2 entries total in the "sparse" matrix + Kokkos::deep_copy (rowOffsets, rowOffsets_h); + + G = graph_type (colInds, rowOffsets); + } + + Kokkos::View x ("x", numCols); + Kokkos::deep_copy (x, static_cast (1.0)); + Kokkos::View y ("y", numRows); + auto y_h = Kokkos::create_mirror_view (y); // we'll want this later + + // Pick some number large enough to exercise all unrolling cases. + // Sparse mat-vec does or at least used to unroll for 1, 2, ..., 17 + // vectors. Include a little extra in case the implementers decide + // to strip-mine that. + constexpr int numVecs = 22; + Kokkos::View X ("X", numCols, numVecs); + Kokkos::deep_copy (X, static_cast (1.0)); + Kokkos::View Y ("Y", numRows, numVecs); + auto Y_h = Kokkos::create_mirror_view (Y); // we'll want this later + + // Start with the easy test case, where the matrix and the vectors + // are all double. + { + constexpr double ZERO_d = static_cast (0.0); + constexpr double ONE_d = static_cast (1.0); + constexpr double TWO_d = static_cast (2.0); + + double_matrix_type A_d ("A_d", G); + auto A_d_val_h = Kokkos::create_mirror_view (A_d.values); + A_d_val_h[0] = ONE_d; + // This cast is deliberate; we want to use float eps here, but as + // a double-precision number. This is just a sanity check for + // accuracy of the sparse mat-vec when not using mixed precision. + A_d_val_h[1] = static_cast (EPS_f) / TWO_d; + EXPECT_NE( A_d_val_h[1], ZERO_d ); // just making sure + Kokkos::deep_copy (A_d.values, A_d_val_h); + + // Just to make sure, we purge the previous contents of y, + // before doing the sparse mat-vec. + Kokkos::deep_copy (y, ZERO_d); + KokkosSparse::spmv ("N", alpha_d, A_d, x, beta_d, y); + + Kokkos::deep_copy (y_h, y); + const double expectedResult_allDouble = static_cast (1.0) + + static_cast (EPS_f) / static_cast (2.0); + EXPECT_NE( expectedResult_allDouble, ZERO_d ); + EXPECT_EQ( y_h[0], expectedResult_allDouble ); + + for (int curNumVecs = 1; curNumVecs <= numVecs; ++curNumVecs) { + const Kokkos::pair vecRng (0, curNumVecs); + auto X_sub = Kokkos::subview (X, Kokkos::ALL (), vecRng); + auto Y_sub = Kokkos::subview (Y, Kokkos::ALL (), vecRng); + + // Just to make sure, we purge the previous contents of Y, + // before doing the sparse mat-vec. + Kokkos::deep_copy (Y, ZERO_d); + KokkosSparse::spmv ("N", alpha_d, A_d, X, beta_d, Y); + + Kokkos::deep_copy (Y_h, Y); + for (int j = 0; j < curNumVecs; ++j) { + const double actualResult = Y_h(0,j); + EXPECT_EQ( actualResult, expectedResult_allDouble ); + } + } + } + + // Now exercise the case where the matrix is in float, but the + // vectors are in double. + { + constexpr float ZERO_f = static_cast (0.0); + constexpr float ONE_f = static_cast (1.0); + constexpr float TWO_f = static_cast (2.0); + constexpr double ZERO_d = static_cast (0.0); + + float_matrix_type A_f ("A_f", G); + auto A_f_val_h = Kokkos::create_mirror_view (A_f.values); + A_f_val_h[0] = ONE_f; + A_f_val_h[1] = EPS_f / TWO_f; + EXPECT_NE( A_f_val_h[1], ZERO_f ); // just making sure + Kokkos::deep_copy (A_f.values, A_f_val_h); + + // Just to make sure, we purge the previous contents of y, + // before doing the sparse mat-vec. + Kokkos::deep_copy (y, ZERO_d); + KokkosSparse::spmv ("N", alpha_d, A_f, x, beta_d, y); + + Kokkos::deep_copy (y_h, y); + const double expectedResult_mixed = static_cast (1.0) + + static_cast (EPS_f) / static_cast (2.0); + EXPECT_NE( expectedResult_mixed, ZERO_d ); + EXPECT_EQ( y_h[0], expectedResult_mixed ); + + for (int curNumVecs = 1; curNumVecs <= numVecs; ++curNumVecs) { + const Kokkos::pair vecRng (0, curNumVecs); + auto X_sub = Kokkos::subview (X, Kokkos::ALL (), vecRng); + auto Y_sub = Kokkos::subview (Y, Kokkos::ALL (), vecRng); + + // Just to make sure, we purge the previous contents of Y, + // before doing the sparse mat-vec. + Kokkos::deep_copy (Y, ZERO_d); + KokkosSparse::spmv ("N", alpha_d, A_f, X, beta_d, Y); + + Kokkos::deep_copy (Y_h, Y); + for (int j = 0; j < curNumVecs; ++j) { + const double actualResult = Y_h(0,j); + EXPECT_EQ( actualResult, expectedResult_mixed ); + } + } + } +} + +#define EXECUTE_TEST_ISSUE_101( DEVICE) \ +TEST_F( TestCategory,sparse ## _ ## spmv_issue_101 ## _ ## OFFSET ## _ ## DEVICE ) { \ + test_github_issue_101 (); \ +} #define EXECUTE_TEST(SCALAR, ORDINAL, OFFSET, DEVICE) \ @@ -197,7 +344,11 @@ TEST_F( TestCategory,sparse ## _ ## spmv_mv ## _ ## SCALAR ## _ ## ORDINAL ## _ test_spmv_mv (50000, 50000 * 30, 200, 10, 1); \ test_spmv_mv (10000, 10000 * 20, 100, 5, 10); \ } - // + +#if (!defined(KOKKOSKERNELS_ETI_ONLY) && !defined(KOKKOSKERNELS_IMPL_CHECK_ETI_CALLS)) + EXECUTE_TEST_ISSUE_101(TestExecSpace) +#endif + #if (defined (KOKKOSKERNELS_INST_DOUBLE) \