/******************************************************************************* * * MIT License * * Copyright (c) 2024 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 #include #include #include #include #include #include "../tensor_holder.hpp" namespace miopen { namespace tests { namespace activ { template && std::is_unsigned_v), bool> = false> auto Ceil(T1 val, T2 div) { assert(div != 0); return (val + div - 1) / div; } namespace activ_func { /// \note static_cast in below functions is needed to speed up execution time for CPU non-native /// data types class ActivationPASTHRU { public: template static T Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)alpha; (void)beta; (void)gamma; return x; } template static T Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)alpha; (void)beta; (void)gamma; (void)x; (void)y; return dy; } }; class ActivationLOGISTIC { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)alpha; (void)beta; (void)gamma; return 1 / (1 + std::exp(-x)); } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)alpha; (void)beta; (void)gamma; (void)x; return static_cast(dy) * y * (static_cast(1) - y); } }; class ActivationTANH { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)gamma; return beta * std::tanh(alpha * x); } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)gamma; (void)x; return dy * alpha * (beta - static_cast(y) * y / beta); } }; class ActivationRELU { public: template static T Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)alpha; (void)beta; (void)gamma; return (x > static_cast(0)) ? x : static_cast(0); } template static T Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)alpha; (void)beta; (void)gamma; (void)y; return (x > static_cast(0)) ? dy : static_cast(0); } }; class ActivationSOFTRELU { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)alpha; (void)beta; (void)gamma; return std::log1p(std::exp(x)); } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)alpha; (void)beta; (void)gamma; (void)y; const Tparam threshold = 50.0; const Tparam expval = std::exp(std::min(static_cast(x), threshold)); return dy * expval / (expval + 1); } }; class ActivationABS { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)alpha; (void)beta; (void)gamma; return std::abs(x); } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)alpha; (void)beta; (void)gamma; (void)y; return dy * static_cast((x > static_cast(0)) ? 1 : -1); } }; class ActivationPOWER { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { const auto v = alpha + beta * x; return v <= std::numeric_limits::epsilon() ? 0 : std::pow(v, gamma); } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)gamma; (void)dy; const auto v = alpha + beta * x; return v <= std::numeric_limits::epsilon() ? 0 : gamma * beta * y / v; } }; class ActivationCLIPPEDRELU { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)beta; (void)gamma; return std::clamp(static_cast(x), static_cast(0), alpha); } template static T Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)beta; (void)gamma; (void)y; Tparam x_native = x; return (x_native > 0 && x_native <= alpha) ? dy : static_cast(0); } }; class ActivationLEAKYRELU { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)beta; (void)gamma; Tparam x_native = x; return (x_native > 0) ? x_native : x_native * alpha; } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)beta; (void)gamma; (void)y; return dy * ((x > static_cast(0)) ? 1 : alpha); } }; class ActivationELU { public: template static Tparam Forward(Tparam alpha, Tparam beta, Tparam gamma, T x) { (void)beta; (void)gamma; Tparam x_native = x; return (x_native > 0) ? x_native : alpha * std::expm1(x_native); } template static Tparam Backward(Tparam alpha, Tparam beta, Tparam gamma, T dy, T x, T y) { (void)beta; (void)gamma; return dy * ((x > static_cast(0)) ? 1 : y + alpha); } }; } // namespace activ_func enum class Direction { Forward, Backward }; template class ActivationParamDataType; template <> class ActivationParamDataType { public: using Type = double; // "Type" must be a native CPU type! }; template <> class ActivationParamDataType { public: using Type = float; // "Type" must be a native CPU type! }; template <> class ActivationParamDataType { public: using Type = float; // "Type" must be a native CPU type! }; template <> class ActivationParamDataType { public: using Type = float; // "Type" must be a native CPU type! }; std::size_t CpuActivationGetNumThreads(std::size_t num_jobs) { const unsigned max_num_hw_threads = std::thread::hardware_concurrency(); return std::min(num_jobs, static_cast(max_num_hw_threads)); } template void DoCpuActivationForwardPacked(std::size_t offset, std::size_t end, Tparam alpha, Tparam beta, Tparam gamma, const tensor& x, tensor& y) { for(std::size_t i = offset; i < end; i++) y.data[i] = A::Forward(alpha, beta, gamma, x.data[i]); } template void DoCpuActivationBackwardPacked(std::size_t offset, std::size_t end, Tparam alpha, Tparam beta, Tparam gamma, const tensor& y, const tensor& dy, const tensor& x, tensor& dx) { for(std::size_t i = offset; i < end; i++) dx.data[i] = A::Backward(alpha, beta, gamma, dy.data[i], x.data[i], y.data[i]); } template void DoCpuActivationPacked(std::size_t offset, std::size_t end, Ts&&... xs) { if constexpr(direction == Direction::Forward) DoCpuActivationForwardPacked(offset, end, xs...); else DoCpuActivationBackwardPacked(offset, end, xs...); } template void CpuActivationPackedSingleThread(std::size_t num_items, Ts&&... xs) { DoCpuActivationPacked(0, num_items, xs...); } template void CpuActivationPackedMultiThread(std::size_t num_items, Ts&&... xs) { const std::size_t max_num_items_per_job = 16 * 1024 * 1024; const std::size_t num_jobs = Ceil(num_items, max_num_items_per_job); const auto num_threads = CpuActivationGetNumThreads(num_jobs); if(num_threads == 1) { CpuActivationPackedSingleThread(num_items, xs...); return; } auto func = [&](std::size_t thread_num) { const auto offset = num_items * thread_num / num_threads; const auto end = num_items * (thread_num + 1) / num_threads; DoCpuActivationPacked(offset, end, xs...); }; std::vector> threads; threads.reserve(num_threads - 1); for(std::size_t i = 0; i < num_threads - 1; ++i) threads.push_back(std::async(std::launch::async, func, i)); func(num_threads - 1); for(auto& thread : threads) thread.wait(); } template void CpuActivationPacked(std::size_t num_items, Ts&&... xs) { CpuActivationPackedMultiThread(num_items, xs...); } template [[noreturn]] void CpuActivationNonPacked(std::size_t /*num_items*/, Ts&&... /*xs*/) { throw std::runtime_error("CpuActivationNonPacked is not implemented yet"); } template void CpuActivation(std::size_t num_items, Ts&&... xs) { if constexpr(is_packed) CpuActivationPacked(num_items, xs...); else CpuActivationNonPacked(num_items, xs...); } template void CpuActivation(miopenActivationMode_t m, std::size_t num_items, Ts&&... xs) { switch(m) { case miopenActivationPASTHRU: CpuActivation(num_items, xs...); break; case miopenActivationLOGISTIC: CpuActivation(num_items, xs...); break; case miopenActivationTANH: CpuActivation(num_items, xs...); break; case miopenActivationRELU: CpuActivation(num_items, xs...); break; case miopenActivationSOFTRELU: CpuActivation(num_items, xs...); break; case miopenActivationABS: CpuActivation(num_items, xs...); break; case miopenActivationPOWER: CpuActivation(num_items, xs...); break; case miopenActivationCLIPPEDRELU: CpuActivation(num_items, xs...); break; case miopenActivationLEAKYRELU: CpuActivation(num_items, xs...); break; case miopenActivationELU: CpuActivation(num_items, xs...); break; case miopenActivationCLAMP: throw std::runtime_error("Unknown activation mode"); } } template void CpuActivationForward(miopenActivationMode_t m, double alpha, double beta, double gamma, const tensor& x, tensor& y) { using Tparam = typename ActivationParamDataType::Type; if(x.desc.GetElementSize() != y.desc.GetElementSize()) throw std::runtime_error("x.desc.GetElementSize() != y.desc.GetElementSize()"); Tparam p_alpha = alpha; Tparam p_beta = beta; Tparam p_gamma = gamma; if(x.desc.IsPacked() && y.desc.IsPacked()) { const auto num_items = x.data.size(); CpuActivation(m, num_items, p_alpha, p_beta, p_gamma, x, y); } else { CpuActivation(m, 0, p_alpha, p_beta, p_gamma, x, y); } } template void CpuActivationBackward(miopenActivationMode_t m, double alpha, double beta, double gamma, const tensor& y, const tensor& dy, const tensor& x, tensor& dx) { using Tparam = typename ActivationParamDataType::Type; if(x.desc.GetElementSize() != y.desc.GetElementSize()) throw std::runtime_error("x.desc.GetElementSize() != y.desc.GetElementSize()"); if(dx.desc.GetElementSize() != dy.desc.GetElementSize()) throw std::runtime_error("dx.desc.GetElementSize() != dy.desc.GetElementSize()"); if(x.desc.GetElementSize() != dx.desc.GetElementSize()) throw std::runtime_error("x.desc.GetElementSize() != dx.desc.GetElementSize()"); Tparam p_alpha = alpha; Tparam p_beta = beta; Tparam p_gamma = gamma; if(y.desc.IsPacked() && dy.desc.IsPacked() && x.desc.IsPacked() && dx.desc.IsPacked()) { const auto num_items = dy.data.size(); CpuActivation( m, num_items, p_alpha, p_beta, p_gamma, y, dy, x, dx); } else { CpuActivation(m, 0, p_alpha, p_beta, p_gamma, y, dy, x, dx); } } } // namespace activ } // namespace tests } // namespace miopen