diff --git a/clients/common/lapack/testing_sygvx_hegvx.hpp b/clients/common/lapack/testing_sygvx_hegvx.hpp index 031fe1f12..239640755 100644 --- a/clients/common/lapack/testing_sygvx_hegvx.hpp +++ b/clients/common/lapack/testing_sygvx_hegvx.hpp @@ -263,6 +263,8 @@ void sygvx_hegvx_initData(const rocblas_handle handle, // for testing purposes, we start with a reduced matrix M for the standard equivalent problem // with spectrum in a desired range (-20, 20). Then we construct the generalized pair // (A, B) from there. + + [[maybe_unused]] volatile auto mptr = memset(hB[b], 0, n * ldb * sizeof(T)); for(rocblas_int i = 0; i < n; i++) { // scale matrices and set hA = M (symmetric/hermitian), hB = U (upper triangular) @@ -424,6 +426,16 @@ void sygvx_hegvx_getError(const rocblas_handle handle, sygvx_hegvx_initData(handle, itype, evect, n, dA, lda, stA, dB, ldb, stB, bc, hA, hB, A, B, true, singular); + // + // Compute input data hash (combine matrices A and B) + // + std::size_t input_hash = 0; + for(rocblas_int b = 0; b < bc; ++b) + { + input_hash = hash_combine(input_hash, hA[0], lda * n); + input_hash = hash_combine(input_hash, hB[0], ldb * n); + } + // execute computations // GPU lapack CHECK_ROCBLAS_ERROR(rocsolver_sygvx_hegvx(STRIDED, handle, itype, evect, erange, uplo, n, @@ -472,6 +484,41 @@ void sygvx_hegvx_getError(const rocblas_handle handle, *max_err += 1; } + // + // Compute output hashes + // + std::size_t rocsolver_eigenvalues_hash = 0; + std::size_t rocsolver_eigenvectors_hash = 0; + + for(rocblas_int b = 0; b < bc; ++b) + { + if(hInfo[b][0] == 0) + { + rocsolver_eigenvalues_hash + = hash_combine(rocsolver_eigenvalues_hash, hWRes[b], hNevRes[b][0]); + + if(evect == rocblas_evect_original) + { + rocsolver_eigenvectors_hash + = hash_combine(rocsolver_eigenvectors_hash, hZRes[b], hNevRes[b][0] * ldz); + } + } + } + + // + // Print hashes + // + ROCSOLVER_GTEST_MSG_PRINTER << "Input matrix hash: " << input_hash << std::endl << std::flush; + ROCSOLVER_GTEST_MSG_PRINTER << "Rocsolver eigenvalues hash: " << rocsolver_eigenvalues_hash + << std::endl + << std::flush; + if(evect == rocblas_evect_original) + { + ROCSOLVER_GTEST_MSG_PRINTER + << "Rocsolver eigenvectors hash: " << rocsolver_eigenvectors_hash << std::endl + << std::flush; + } + double err; for(rocblas_int b = 0; b < bc; ++b) diff --git a/clients/common/misc/rocsolver_test.hpp b/clients/common/misc/rocsolver_test.hpp index e97ff794d..4e8cac82d 100644 --- a/clients/common/misc/rocsolver_test.hpp +++ b/clients/common/misc/rocsolver_test.hpp @@ -51,8 +51,49 @@ namespace fs = std::experimental::filesystem; #define USE_ROCBLAS_REALLOC_ON_DEMAND true #ifdef ROCSOLVER_CLIENTS_TEST -#define ROCSOLVER_TEST_CHECK(T, max_error, tol) ASSERT_LE((max_error), (tol)*get_epsilon()) + +#ifdef ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES +// Format output similarly as GTEST messages +#define ANSI_CODE_GTEST_GREEN "\033[0;32m" +#define ANSI_CODE_NORMAL_TERM "\033[0;0m" +// Macro ROCSOLVER_GTEST_MSG_PRINTER is also used to print hashes in tests +#define ROCSOLVER_GTEST_MSG_PRINTER \ + std::cout << ANSI_CODE_GTEST_GREEN << "[ ] " << ANSI_CODE_NORMAL_TERM +// Print computed errors for all tests, making sure that there are sufficient digits to uniquely +// represent all distinct `double` values. +#define ROCSOLVER_STRNGFY(s) #s +#define ROCSOLVER_PRINT_TEST_ERROR(T, max_error, tol) ROCSOLVER_PRINT_TEST_ERROR2(T, max_error, tol) +#define ROCSOLVER_PRINT_TEST_ERROR2(T, max_error, tol) \ + do \ + { \ + const auto default_precision{std::cout.precision()}; \ + constexpr auto max_precision{std::numeric_limits::max_digits10 + 1}; \ + double tol_ = static_cast(tol) * static_cast(get_epsilon()); \ + double max_error_ = static_cast(max_error); \ + ROCSOLVER_GTEST_MSG_PRINTER \ + << "Computed error: " << ROCSOLVER_STRNGFY(max_error) << " / " \ + << ROCSOLVER_STRNGFY(((tol)*get_epsilon())) << " = " \ + << std::setprecision(max_precision) \ + << ((max_error_ >= 0.) && (tol_ > 0.) ? max_error_ / tol_ : -1.) \ + << std::setprecision(default_precision) << std::endl \ + << std::flush; \ + } while(0) +#else // #ifdef ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES +static std::stringstream rocsolver_discard_tests_extra_messages; +#define ROCSOLVER_GTEST_MSG_PRINTER rocsolver_discard_tests_extra_messages +#define ROCSOLVER_PRINT_TEST_ERROR(T, max_error, tol) +#endif // #ifdef ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES + +#define ROCSOLVER_TEST_CHECK(T, max_error, tol) \ + do \ + { \ + ASSERT_LE((max_error), (tol)*get_epsilon()); \ + ROCSOLVER_PRINT_TEST_ERROR(T, max_error, tol); \ + } while(0) + #else // ROCSOLVER_CLIENTS_BENCH +static std::stringstream rocsolver_discard_tests_extra_messages; +#define ROCSOLVER_GTEST_MSG_PRINTER rocsolver_discard_tests_extra_messages #define ROCSOLVER_TEST_CHECK(T, max_error, tol) #endif @@ -158,3 +199,57 @@ inline std::ostream& operator<<(std::ostream& os, printable_char x) // location of the sparse data directory for the re-factorization tests fs::path get_sparse_data_dir(); + +/// Combines `seed` with the hash of `value`, following the spirit of +/// `boost::hash_combine`. +/// +/// Extends `std::hash` to combine the hashes of multiple values (e.g., +/// from an array). +/// +/// Attention: hash_combine(0, T(0)) != 0 +template +std::size_t hash_combine(std::size_t seed, T value) +{ + using S = decltype(std::real(T{})); + auto hasher = std::hash(); + + if constexpr(rocblas_is_complex) + { + seed ^= hasher(std::real(value)) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= hasher(std::imag(value)) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + else + { + seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + + return seed; +} + +/// Hash contents of the given array. +/// +/// If seed == 0 and array_size == 0, then hash_combine(seed, _, array_size) == 0 +template +std::size_t hash_combine(std::size_t seed, T const* array, std::size_t array_size) +{ + std::size_t hash = 0; + if(array_size > 0) + { + hash = hash_combine(seed, array_size); + for(std::size_t i = 0; i < array_size; ++i) + { + hash = hash_combine(hash, array[i]); + } + } + + return hash; +} + +/// Hash contents of the given array. +/// +/// If seed == 0 and array.size() == 0, then hash_combine(seed, array) == 0 +template +std::size_t hash_combine(std::size_t seed, const std::vector& array) +{ + return hash_combine(seed, array.data(), array.size()); +} diff --git a/clients/gtest/CMakeLists.txt b/clients/gtest/CMakeLists.txt index 6a5ed42b0..38eeda2c9 100755 --- a/clients/gtest/CMakeLists.txt +++ b/clients/gtest/CMakeLists.txt @@ -191,6 +191,7 @@ target_compile_options(rocsolver-test PRIVATE -mf16c) target_compile_definitions(rocsolver-test PRIVATE ROCM_USE_FLOAT16 ROCSOLVER_CLIENTS_TEST + ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES ) add_test(