/******************************************************************************* * * MIT License * * Copyright (c) 2022-2025 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * *******************************************************************************/ #pragma once #include "hipsparselt_math.hpp" #include #include #include #include #include /* ============================================================================================ */ // Random number generator using hipsparselt_rng_t = std::mt19937; extern hipsparselt_rng_t g_hipsparselt_seed; extern std::thread::id g_main_thread_id; extern thread_local hipsparselt_rng_t t_hipsparselt_rng; extern thread_local int t_hipsparselt_rand_idx; // optimized helper float hipsparselt_uniform_int_1_10(); void hipsparselt_uniform_int_1_10_run_float(float* ptr, size_t num); void hipsparselt_uniform_int_1_10_run_double(double* ptr, size_t num); // For the main thread, we use g_hipsparselt_seed; for other threads, we start with a different seed but // deterministically based on the thread id's hash function. inline hipsparselt_rng_t get_seed() { auto tid = std::this_thread::get_id(); return tid == g_main_thread_id ? g_hipsparselt_seed : hipsparselt_rng_t(std::hash{}(tid)); } // Reset the seed (mainly to ensure repeatability of failures in a given suite) inline void hipsparselt_seedrand() { t_hipsparselt_rng = get_seed(); t_hipsparselt_rand_idx = 0; } /* ============================================================================================ */ /*! \brief Random number generator which generates NaN values */ class hipsparselt_nan_rng { // Generate random NaN values template static T random_nan_data() { static_assert(sizeof(UINT_T) == sizeof(T), "Type sizes do not match"); union { UINT_T u; T fp; } x; do x.u = std::uniform_int_distribution{}(t_hipsparselt_rng); while(!(x.u & (((UINT_T)1 << SIG) - 1))); // Reject Inf (mantissa == 0) x.u |= (((UINT_T)1 << EXP) - 1) << SIG; // Exponent = all 1's return x.fp; // NaN with random bits } public: // Random integer template {}, int> = 0> explicit operator T() { return std::uniform_int_distribution{}(t_hipsparselt_rng); } // Random signed char explicit operator signed char() { return static_cast(std::uniform_int_distribution{}(t_hipsparselt_rng)); } // Random NaN double explicit operator double() { return random_nan_data(); } // Random NaN float explicit operator float() { return random_nan_data(); } // Random NaN half explicit operator __half() { return random_nan_data<__half, uint16_t, 10, 5>(); } // Random NaN bfloat16 explicit operator hip_bfloat16() { return random_nan_data(); } #ifdef HIPSPARSELT_CLIENT_ENABLE_FP8_OCP // Random NaN bfloat16 explicit operator hipsparselt_fp8_e4m3() { return hipsparselt_fp8_e4m3(random_nan_data()); } // Random NaN bfloat16 explicit operator hipsparselt_fp8_e5m2() { return hipsparselt_fp8_e5m2(random_nan_data()); } #endif #ifdef HIPSPARSELT_CLIENT_ENABLE_FP8_FNUZ explicit operator hipsparselt_fp8_e4m3_fnuz() { return hipsparselt_fp8_e4m3_fnuz(random_nan_data()); } explicit operator hipsparselt_fp8_e5m2_fnuz() { return hipsparselt_fp8_e5m2_fnuz(random_nan_data()); } #endif }; /* ============================================================================================ */ /*! \brief Random number generator which generates Inf values */ class hipsparselt_inf_rng { // Generate random Inf values static unsigned rand2() { return std::uniform_int_distribution(0, 1)(t_hipsparselt_rng); } public: // Random integer template {}, int> = 0> explicit operator T() { return rand2() ? std::numeric_limits::min : std::numeric_limits::max; } // Random float template {}, int> = 0> explicit operator T() { return T(rand2() ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); } }; /* ============================================================================================ */ /*! \brief Random number generator which generates zero values */ class hipsparselt_zero_rng { // Generate random zero values static unsigned rand2() { return std::uniform_int_distribution(0, 1)(t_hipsparselt_rng); } public: // Random integer template {}, int> = 0> explicit operator T() { return 0; } // Random float template {}, int> = 0> explicit operator T() { return T(rand2() ? -0.0 : 0.0); } }; /* ============================================================================================ */ /* generate random number :*/ /*! \brief generate a random NaN number */ template inline T random_nan_generator() { return T(hipsparselt_nan_rng{}); } /*! \brief generate a random Inf number */ template inline T random_inf_generator() { return T(hipsparselt_inf_rng{}); } /*! \brief generate a random Inf number */ template inline T random_zero_generator() { return T(hipsparselt_zero_rng{}); } /*! \brief generate a random number in range [1,2,3,4,5,6,7,8,9,10] */ template inline T random_generator() { return T(hipsparselt_uniform_int_1_10()); } template <> inline float random_generator() { return hipsparselt_uniform_int_1_10(); } // for __half, generate float, and convert to __half /*! \brief generate a random number in range [-2,-1,0,1,2] */ template <> inline __half random_generator<__half>() { #if defined(__HIP_PLATFORM_AMD__) #define CAST #else #define CAST static_cast #endif return __half(CAST(std::uniform_int_distribution(-2, 2)(t_hipsparselt_rng))); }; // for hip_bfloat16, generate float, and convert to hip_bfloat16 /*! \brief generate a random number in range [-2,-1,0,1,2] */ template <> inline hip_bfloat16 random_generator() { #if defined(__HIP_PLATFORM_AMD__) #define CAST #else #define CAST static_cast #endif return hip_bfloat16(CAST(std::uniform_int_distribution(-2, 2)(t_hipsparselt_rng))); }; /*! \brief generate a random number in range [1,2,3] */ template <> inline int8_t random_generator() { return static_cast( std::uniform_int_distribution(1, 3)(t_hipsparselt_rng)); }; /*! \brief generate a sequence of random number in range [1,2,3,4,5,6,7,8,9,10] */ template inline void random_run_generator(T* ptr, size_t num) { for(size_t i = 0; i < num; i++) { ptr[i] = random_generator(); } } template <> inline void random_run_generator(float* ptr, size_t num) { hipsparselt_uniform_int_1_10_run_float(ptr, num); }; template <> inline void random_run_generator(double* ptr, size_t num) { hipsparselt_uniform_int_1_10_run_double(ptr, num); }; // HPL /*! \brief generate a random number in HPL-like [-0.5,0.5] doubles */ template inline T random_hpl_generator() { return std::uniform_real_distribution(-0.5, 0.5)(t_hipsparselt_rng); } #ifdef HIPSPARSELT_CLIENT_ENABLE_FP8_OCP /*! \brief generate a random number in HPL-like [-0.5,0.5] doubles */ template <> inline hipsparselt_fp8_e4m3 random_hpl_generator() { return hipsparselt_fp8_e4m3(std::uniform_real_distribution(-0.5, 0.5)(t_hipsparselt_rng)); } /*! \brief generate a random number in HPL-like [-0.5,0.5] doubles */ template <> inline hipsparselt_fp8_e5m2 random_hpl_generator() { return hipsparselt_fp8_e5m2(std::uniform_real_distribution(-0.5, 0.5)(t_hipsparselt_rng)); } #endif #ifdef HIPSPARSELT_CLIENT_ENABLE_FP8_FNUZ template <> inline hipsparselt_fp8_e4m3_fnuz random_hpl_generator() { return hipsparselt_fp8_e4m3_fnuz(std::uniform_real_distribution(-0.5, 0.5)(t_hipsparselt_rng)); } template <> inline hipsparselt_fp8_e5m2_fnuz random_hpl_generator() { return hipsparselt_fp8_e5m2_fnuz(std::uniform_real_distribution(-0.5, 0.5)(t_hipsparselt_rng)); } #endif /*! \brief generate a random number in [-1.0,1.0] doubles */ template <> inline int8_t random_hpl_generator() { auto saturate = [](double val) { auto _val = std::nearbyint(val); _val = _val > 127.f ? 127.f : _val < -128.f ? -128.f : _val; return _val; }; return static_cast( saturate(std::uniform_real_distribution(-1.0, 1.0)(t_hipsparselt_rng))); } // for hip_bfloat16, generate float, and convert to hip_bfloat16 /*! \brief generate a random number in HPL-like [-0.5,0.5] doubles */ template <> inline hip_bfloat16 random_hpl_generator() { return hip_bfloat16(std::uniform_real_distribution(-0.5, 0.5)(t_hipsparselt_rng)); } /*! \brief generate a random ASCII string of up to length n */ inline std::string random_string(size_t n) { std::string str; if(n) { size_t len = std::uniform_int_distribution(1, n)(t_hipsparselt_rng); str.reserve(len); for(size_t i = 0; i < len; ++i) str.push_back(static_cast( std::uniform_int_distribution(0x20, 0x7E)(t_hipsparselt_rng))); } return str; }