From f23b298fcacaec5571ab1571cfdde49e0a1370d2 Mon Sep 17 00:00:00 2001 From: Bernhard Manfred Gruber Date: Mon, 18 Sep 2023 14:37:14 +0200 Subject: [PATCH] Simplify namespace of mathTest --- test/unit/math/src/Buffer.hpp | 178 ++++--- test/unit/math/src/DataGen.hpp | 362 +++++++------ test/unit/math/src/Defines.hpp | 186 ++++--- test/unit/math/src/FloatEqualExactTest.cpp | 3 +- test/unit/math/src/Functor.hpp | 560 +++++++++------------ test/unit/math/src/TestTemplate.hpp | 297 +++++------ test/unit/math/src/mathComplexDouble.cpp | 5 +- test/unit/math/src/mathComplexFloat.cpp | 5 +- test/unit/math/src/mathDouble.cpp | 8 +- test/unit/math/src/mathFloat.cpp | 8 +- test/unit/math/src/mathLambda.cpp | 31 +- test/unit/math/src/powMixedTypes.cpp | 6 +- test/unit/math/src/sincos.cpp | 7 +- 13 files changed, 767 insertions(+), 889 deletions(-) diff --git a/test/unit/math/src/Buffer.hpp b/test/unit/math/src/Buffer.hpp index ac0f002c29c0..fbffd348ae57 100644 --- a/test/unit/math/src/Buffer.hpp +++ b/test/unit/math/src/Buffer.hpp @@ -10,101 +10,91 @@ #include -namespace alpaka +namespace mathtest { - namespace test + //! Provides alpaka-style buffer with arguments' data. + //! TData can be a plain value or a complex data-structure. + //! The operator() is overloaded and returns the value from the correct Buffer, + //! either from the host (index) or device buffer (index, acc). + //! Index out of range errors are not checked. + //! @brief Encapsulates buffer initialisation and communication with Device. + //! @tparam TAcc Used accelerator, not interchangeable + //! @tparam TData The Data-type, only restricted by the alpaka-interface. + //! @tparam Tcapacity The size of the buffer. + template + struct Buffer { - namespace unit + using value_type = TData; + static constexpr size_t capacity = Tcapacity; + using Dim = typename alpaka::trait::DimType::type; + using Idx = typename alpaka::trait::IdxType::type; + + // Defines using's for alpaka-buffer. + using DevHost = alpaka::DevCpu; + using PlatformHost = alpaka::Platform; + using BufHost = alpaka::Buf; + + using DevAcc = alpaka::Dev; + using PlatformAcc = alpaka::Platform; + using BufAcc = alpaka::Buf; + + PlatformHost platformHost; + DevHost devHost; + + BufHost hostBuffer; + BufAcc devBuffer; + PlatformAcc platformAcc; + + // Native pointer to access buffer. + TData* const pHostBuffer; + TData* const pDevBuffer; + + // This constructor cant be used, + // because BufHost and BufAcc need to be initialised. + Buffer() = delete; + + // Constructor needs to initialize all Buffer. + Buffer(DevAcc const& devAcc) + : devHost{alpaka::getDevByIdx(platformHost, 0)} + , hostBuffer{alpaka::allocMappedBufIfSupported(devHost, platformAcc, Tcapacity)} + , devBuffer{alpaka::allocBuf(devAcc, Tcapacity)} + , pHostBuffer{alpaka::getPtrNative(hostBuffer)} + , pDevBuffer{alpaka::getPtrNative(devBuffer)} { - namespace math + } + + // Copy Host -> Acc. + template + auto copyToDevice(Queue queue) -> void + { + alpaka::memcpy(queue, devBuffer, hostBuffer); + } + + // Copy Acc -> Host. + template + auto copyFromDevice(Queue queue) -> void + { + alpaka::memcpy(queue, hostBuffer, devBuffer); + } + + ALPAKA_FN_ACC auto operator()(size_t idx, TAcc const& /* acc */) const -> TData& + { + return pDevBuffer[idx]; + } + + ALPAKA_FN_HOST auto operator()(size_t idx) const -> TData& + { + return pHostBuffer[idx]; + } + + ALPAKA_FN_HOST friend auto operator<<(std::ostream& os, Buffer const& buffer) -> std::ostream& + { + os << "capacity: " << capacity << "\n"; + for(size_t i = 0; i < capacity; ++i) { - //! Provides alpaka-style buffer with arguments' data. - //! TData can be a plain value or a complex data-structure. - //! The operator() is overloaded and returns the value from the correct Buffer, - //! either from the host (index) or device buffer (index, acc). - //! Index out of range errors are not checked. - //! @brief Encapsulates buffer initialisation and communication with Device. - //! @tparam TAcc Used accelerator, not interchangeable - //! @tparam TData The Data-type, only restricted by the alpaka-interface. - //! @tparam Tcapacity The size of the buffer. - template - struct Buffer - { - using value_type = TData; - static constexpr size_t capacity = Tcapacity; - using Dim = typename alpaka::trait::DimType::type; - using Idx = typename alpaka::trait::IdxType::type; - - // Defines using's for alpaka-buffer. - using DevHost = alpaka::DevCpu; - using PlatformHost = alpaka::Platform; - using BufHost = alpaka::Buf; - - using DevAcc = alpaka::Dev; - using PlatformAcc = alpaka::Platform; - using BufAcc = alpaka::Buf; - - PlatformHost platformHost; - DevHost devHost; - - BufHost hostBuffer; - BufAcc devBuffer; - PlatformAcc platformAcc; - - // Native pointer to access buffer. - TData* const pHostBuffer; - TData* const pDevBuffer; - - // This constructor cant be used, - // because BufHost and BufAcc need to be initialised. - Buffer() = delete; - - // Constructor needs to initialize all Buffer. - Buffer(DevAcc const& devAcc) - : devHost{alpaka::getDevByIdx(platformHost, 0)} - , hostBuffer{alpaka::allocMappedBufIfSupported(devHost, platformAcc, Tcapacity)} - , devBuffer{alpaka::allocBuf(devAcc, Tcapacity)} - , pHostBuffer{alpaka::getPtrNative(hostBuffer)} - , pDevBuffer{alpaka::getPtrNative(devBuffer)} - { - } - - // Copy Host -> Acc. - template - auto copyToDevice(Queue queue) -> void - { - alpaka::memcpy(queue, devBuffer, hostBuffer); - } - - // Copy Acc -> Host. - template - auto copyFromDevice(Queue queue) -> void - { - alpaka::memcpy(queue, hostBuffer, devBuffer); - } - - ALPAKA_FN_ACC auto operator()(size_t idx, TAcc const& /* acc */) const -> TData& - { - return pDevBuffer[idx]; - } - - ALPAKA_FN_HOST auto operator()(size_t idx) const -> TData& - { - return pHostBuffer[idx]; - } - - ALPAKA_FN_HOST friend auto operator<<(std::ostream& os, Buffer const& buffer) -> std::ostream& - { - os << "capacity: " << capacity << "\n"; - for(size_t i = 0; i < capacity; ++i) - { - os << i << ": " << buffer.pHostBuffer[i] << "\n"; - } - return os; - } - }; - - } // namespace math - } // namespace unit - } // namespace test -} // namespace alpaka + os << i << ": " << buffer.pHostBuffer[i] << "\n"; + } + return os; + } + }; +} // namespace mathtest diff --git a/test/unit/math/src/DataGen.hpp b/test/unit/math/src/DataGen.hpp index 8c6b119b9f60..78a5d0543a58 100644 --- a/test/unit/math/src/DataGen.hpp +++ b/test/unit/math/src/DataGen.hpp @@ -11,203 +11,191 @@ #include #include -namespace alpaka +namespace mathtest { - namespace test + //! Helper to generate random numbers of the given type for testing + //! + //! The general implementation supports float and double types + //! + //! @tparam TData generated type + template + struct RngWrapper { - namespace unit + auto getMax() { - namespace math - { - //! Helper to generate random numbers of the given type for testing - //! - //! The general implementation supports float and double types - //! - //! @tparam TData generated type - template - struct RngWrapper - { - auto getMax() - { - return std::numeric_limits::max(); - } + return std::numeric_limits::max(); + } - auto getLowest() - { - return std::numeric_limits::lowest(); - } + auto getLowest() + { + return std::numeric_limits::lowest(); + } - auto getDistribution() - { - return std::uniform_real_distribution{0, 1000}; - } + auto getDistribution() + { + return std::uniform_real_distribution{0, 1000}; + } - template - auto getNumber(TDistribution& distribution, TEngine& engine) - { - return distribution(engine); - } - }; - - //! Specialization for generating alpaka::Complex - //! - //! It has a much reduced range of numbers. - //! The reason is, the results of operations much easier go to infinity area. - //! Also, alpaka may emulate complex number math via calling other functions. - //! As a result, it may produce some infinities and NaNs when the std:: implementation would not. - //! So this range at least makes sure the "simple" cases work and therefore the implementation is - //! logically correct. - template - struct RngWrapper> + template + auto getNumber(TDistribution& distribution, TEngine& engine) + { + return distribution(engine); + } + }; + + //! Specialization for generating alpaka::Complex + //! + //! It has a much reduced range of numbers. + //! The reason is, the results of operations much easier go to infinity area. + //! Also, alpaka may emulate complex number math via calling other functions. + //! As a result, it may produce some infinities and NaNs when the std:: implementation would not. + //! So this range at least makes sure the "simple" cases work and therefore the implementation is + //! logically correct. + template + struct RngWrapper> + { + auto getMax() + { + return alpaka::Complex{TData{10}, TData{10}}; + } + + auto getLowest() + { + return -getMax(); + } + + auto getDistribution() + { + return std::uniform_real_distribution{0, 5}; + } + + template + auto getNumber(TDistribution& distribution, TEngine& engine) + { + return alpaka::Complex{distribution(engine), distribution(engine)}; + } + }; + + /** + * Fills buffer with random numbers (host-only). + * + * @tparam TData The used data-type (float, double, Complex or Complex). + * @tparam TArgs The args-buffer to be filled. + * @tparam TFunctor The used Functor-type. + * @param args The buffer that should be filled. + * @param functor The Functor, needed for ranges. + * @param seed The used seed. + */ + template + auto fillWithRndArgs(TArgs& args, TFunctor functor, unsigned int const& seed) -> void + { + /* + * Each "sub-buffer" is filled with zero and/or max and/or lowest, + * depending on the specified range (at [0] - [2]). + * + * Every switch case needs to return! + * If no switch case was matched an assert(false) will be triggered. + * + * This function is easily extendable. It is only necessary to add extra + * definitions in the switch case, for more Range-types. + */ + static_assert(TArgs::value_type::arity == TFunctor::arity, "Buffer properties must match TFunctor::arity"); + static_assert(TArgs::capacity > 6, "Set of args must provide > 6 entries."); + auto rngWrapper = RngWrapper{}; + auto const max = rngWrapper.getMax(); + auto const low = rngWrapper.getLowest(); + std::default_random_engine eng{static_cast(seed)}; + + // These pseudo-random numbers are implementation/platform specific! + auto dist = rngWrapper.getDistribution(); + decltype(dist) distOne(-1, 1); + for(size_t k = 0; k < TFunctor::arity_nr; ++k) + { + [[maybe_unused]] bool matchedSwitch = false; + switch(functor.ranges[k]) + { + case Range::OneNeighbourhood: + matchedSwitch = true; + for(size_t i = 0; i < TArgs::capacity; ++i) { - auto getMax() - { - return Complex{TData{10}, TData{10}}; - } + args(i).arg[k] = rngWrapper.getNumber(distOne, eng); + } + break; - auto getLowest() - { - return -getMax(); - } + case Range::PositiveOnly: + matchedSwitch = true; + args(0).arg[k] = max; + for(size_t i = 1; i < TArgs::capacity; ++i) + { + args(i).arg[k] = rngWrapper.getNumber(dist, eng) + TData{1}; + } + break; - auto getDistribution() - { - return std::uniform_real_distribution{0, 5}; - } + case Range::PositiveAndZero: + matchedSwitch = true; + args(0).arg[k] = TData{0}; + args(1).arg[k] = max; + for(size_t i = 2; i < TArgs::capacity; ++i) + { + args(i).arg[k] = rngWrapper.getNumber(dist, eng); + } + break; - template - auto getNumber(TDistribution& distribution, TEngine& engine) - { - return Complex{distribution(engine), distribution(engine)}; - } - }; - - /** - * Fills buffer with random numbers (host-only). - * - * @tparam TData The used data-type (float, double, Complex or Complex). - * @tparam TArgs The args-buffer to be filled. - * @tparam TFunctor The used Functor-type. - * @param args The buffer that should be filled. - * @param functor The Functor, needed for ranges. - * @param seed The used seed. - */ - template - auto fillWithRndArgs(TArgs& args, TFunctor functor, unsigned int const& seed) -> void + case Range::NotZero: + matchedSwitch = true; + args(0).arg[k] = max; + args(1).arg[k] = low; + for(size_t i = 2; i < TArgs::capacity; ++i) { - /* - * Each "sub-buffer" is filled with zero and/or max and/or lowest, - * depending on the specified range (at [0] - [2]). - * - * Every switch case needs to return! - * If no switch case was matched an assert(false) will be triggered. - * - * This function is easily extendable. It is only necessary to add extra - * definitions in the switch case, for more Range-types. - */ - static_assert( - TArgs::value_type::arity == TFunctor::arity, - "Buffer properties must match TFunctor::arity"); - static_assert(TArgs::capacity > 6, "Set of args must provide > 6 entries."); - auto rngWrapper = RngWrapper{}; - auto const max = rngWrapper.getMax(); - auto const low = rngWrapper.getLowest(); - std::default_random_engine eng{static_cast(seed)}; - - // These pseudo-random numbers are implementation/platform specific! - auto dist = rngWrapper.getDistribution(); - decltype(dist) distOne(-1, 1); - for(size_t k = 0; k < TFunctor::arity_nr; ++k) + TData arg; + do { - [[maybe_unused]] bool matchedSwitch = false; - switch(functor.ranges[k]) - { - case Range::OneNeighbourhood: - matchedSwitch = true; - for(size_t i = 0; i < TArgs::capacity; ++i) - { - args(i).arg[k] = rngWrapper.getNumber(distOne, eng); - } - break; - - case Range::PositiveOnly: - matchedSwitch = true; - args(0).arg[k] = max; - for(size_t i = 1; i < TArgs::capacity; ++i) - { - args(i).arg[k] = rngWrapper.getNumber(dist, eng) + TData{1}; - } - break; - - case Range::PositiveAndZero: - matchedSwitch = true; - args(0).arg[k] = TData{0}; - args(1).arg[k] = max; - for(size_t i = 2; i < TArgs::capacity; ++i) - { - args(i).arg[k] = rngWrapper.getNumber(dist, eng); - } - break; - - case Range::NotZero: - matchedSwitch = true; - args(0).arg[k] = max; - args(1).arg[k] = low; - for(size_t i = 2; i < TArgs::capacity; ++i) - { - TData arg; - do - { - arg = rngWrapper.getNumber(dist, eng); - } while(std::equal_to()(arg, 1)); - if(i % 2 == 0) - args(i).arg[k] = arg; - else - args(i).arg[k] = -arg; - } - break; - - case Range::Unrestricted: - matchedSwitch = true; - args(0).arg[k] = TData{0}; - args(1).arg[k] = max; - args(2).arg[k] = low; - for(size_t i = 3; i < TArgs::capacity; ++i) - { - if(i % 2 == 0) - args(i).arg[k] = rngWrapper.getNumber(dist, eng); - else - args(i).arg[k] = -rngWrapper.getNumber(dist, eng); - } - break; - - case Range::Anything: - matchedSwitch = true; - args(0).arg[k] = TData{0}; - args(1).arg[k] = std::numeric_limits::quiet_NaN(); - args(2).arg[k] = std::numeric_limits::signaling_NaN(); - args(3).arg[k] = std::numeric_limits::infinity(); - args(4).arg[k] = -std::numeric_limits::infinity(); - constexpr size_t nFixed = 5; - size_t i = nFixed; - // no need to test for denormal for now: not supported by CUDA - // for(; i < nFixed + (TArgs::capacity - nFixed) / 2; ++i) - // { - // const TData v = rngWrapper.getNumber(dist, eng) * - // std::numeric_limits::denorm_min(); args(i).arg[k] = (i % 2 == 0) ? v : -v; - // } - for(; i < TArgs::capacity; ++i) - { - const TData v = rngWrapper.getNumber(dist, eng); - args(i).arg[k] = (i % 2 == 0) ? v : -v; - } - break; - } - assert(matchedSwitch); - } + arg = rngWrapper.getNumber(dist, eng); + } while(std::equal_to()(arg, 1)); + if(i % 2 == 0) + args(i).arg[k] = arg; + else + args(i).arg[k] = -arg; } - - } // namespace math - } // namespace unit - } // namespace test -} // namespace alpaka + break; + + case Range::Unrestricted: + matchedSwitch = true; + args(0).arg[k] = TData{0}; + args(1).arg[k] = max; + args(2).arg[k] = low; + for(size_t i = 3; i < TArgs::capacity; ++i) + { + if(i % 2 == 0) + args(i).arg[k] = rngWrapper.getNumber(dist, eng); + else + args(i).arg[k] = -rngWrapper.getNumber(dist, eng); + } + break; + + case Range::Anything: + matchedSwitch = true; + args(0).arg[k] = TData{0}; + args(1).arg[k] = std::numeric_limits::quiet_NaN(); + args(2).arg[k] = std::numeric_limits::signaling_NaN(); + args(3).arg[k] = std::numeric_limits::infinity(); + args(4).arg[k] = -std::numeric_limits::infinity(); + constexpr size_t nFixed = 5; + size_t i = nFixed; + // no need to test for denormal for now: not supported by CUDA + // for(; i < nFixed + (TArgs::capacity - nFixed) / 2; ++i) + // { + // const TData v = rngWrapper.getNumber(dist, eng) * + // std::numeric_limits::denorm_min(); args(i).arg[k] = (i % 2 == 0) ? v : -v; + // } + for(; i < TArgs::capacity; ++i) + { + TData const v = rngWrapper.getNumber(dist, eng); + args(i).arg[k] = (i % 2 == 0) ? v : -v; + } + break; + } + assert(matchedSwitch); + } + } +} // namespace mathtest diff --git a/test/unit/math/src/Defines.hpp b/test/unit/math/src/Defines.hpp index 1b6cf418e716..e9b4c68d01e1 100644 --- a/test/unit/math/src/Defines.hpp +++ b/test/unit/math/src/Defines.hpp @@ -11,110 +11,100 @@ #include #include -namespace alpaka +namespace mathtest { - namespace test + // New types need to be added to the switch-case in DataGen.hpp + enum class Range { - namespace unit - { - namespace math - { - // New types need to be added to the switch-case in DataGen.hpp - enum class Range - { - OneNeighbourhood, - PositiveOnly, - PositiveAndZero, - NotZero, - Unrestricted, - Anything - }; - - // New types need to be added to the operator() function in Functor.hpp - enum class Arity - { - Unary = 1, - Binary = 2, - Ternary = 3 - }; - - template - struct ArgsItem - { - static constexpr Arity arity = Tarity; - static constexpr size_t arity_nr = static_cast(Tarity); - - T arg[arity_nr]; // represents arg0, arg1, ... - - friend auto operator<<(std::ostream& os, ArgsItem const& argsItem) -> std::ostream& - { - os.precision(17); - for(size_t i = 0; i < argsItem.arity_nr; ++i) - os << (i == 0 ? "[ " : ", ") << std::setprecision(std::numeric_limits::digits10 + 1) - << argsItem.arg[i]; - os << " ]"; - return os; - } - }; - - //! Reference implementation of rsqrt, since there is no std::rsqrt - template - auto rsqrt(T const& arg) - { - // Need ADL for complex numbers - using std::sqrt; - return static_cast(1) / sqrt(arg); - } + OneNeighbourhood, + PositiveOnly, + PositiveAndZero, + NotZero, + Unrestricted, + Anything + }; + + // New types need to be added to the operator() function in Functor.hpp + enum class Arity + { + Unary = 1, + Binary = 2, + Ternary = 3 + }; - //! Stub for division expressed same way as alpaka math traits - template - ALPAKA_FN_HOST_ACC auto divides(TAcc&, T const& arg1, T const& arg2) - { - return arg1 / arg2; - } + template + struct ArgsItem + { + static constexpr Arity arity = Tarity; + static constexpr size_t arity_nr = static_cast(Tarity); - //! Stub for subtraction expressed same way as alpaka math traits - template - ALPAKA_FN_HOST_ACC auto minus(TAcc&, T const& arg1, T const& arg2) - { - return arg1 - arg2; - } + T arg[arity_nr]; // represents arg0, arg1, ... - //! Stub for multiplication expressed same way as alpaka math traits - template - ALPAKA_FN_HOST_ACC auto multiplies(TAcc&, T const& arg1, T const& arg2) - { - return arg1 * arg2; - } + friend auto operator<<(std::ostream& os, ArgsItem const& argsItem) -> std::ostream& + { + os.precision(17); + for(size_t i = 0; i < argsItem.arity_nr; ++i) + os << (i == 0 ? "[ " : ", ") << std::setprecision(std::numeric_limits::digits10 + 1) + << argsItem.arg[i]; + os << " ]"; + return os; + } + }; + + //! Reference implementation of rsqrt, since there is no std::rsqrt + template + auto rsqrt(T const& arg) + { + // Need ADL for complex numbers + using std::sqrt; + return static_cast(1) / sqrt(arg); + } + + //! Stub for division expressed same way as alpaka math traits + template + ALPAKA_FN_HOST_ACC auto divides(TAcc&, T const& arg1, T const& arg2) + { + return arg1 / arg2; + } - //! Stub for addition expressed same way as alpaka math traits - template - ALPAKA_FN_HOST_ACC auto plus(TAcc&, T const& arg1, T const& arg2) - { - return arg1 + arg2; - } + //! Stub for subtraction expressed same way as alpaka math traits + template + ALPAKA_FN_HOST_ACC auto minus(TAcc&, T const& arg1, T const& arg2) + { + return arg1 - arg2; + } - // https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon - template - ALPAKA_FN_ACC auto almost_equal(TAcc const& acc, FP x, FP y, int ulp) - -> std::enable_if_t::is_integer, bool> - { - // the machine epsilon has to be scaled to the magnitude of the values used - // and multiplied by the desired precision in ULPs (units in the last place) - return alpaka::math::abs(acc, x - y) <= std::numeric_limits::epsilon() - * alpaka::math::abs(acc, x + y) * static_cast(ulp) - // unless the result is subnormal - || alpaka::math::abs(acc, x - y) < std::numeric_limits::min(); - } + //! Stub for multiplication expressed same way as alpaka math traits + template + ALPAKA_FN_HOST_ACC auto multiplies(TAcc&, T const& arg1, T const& arg2) + { + return arg1 * arg2; + } - //! Version for alpaka::Complex - template - ALPAKA_FN_ACC bool almost_equal(TAcc const& acc, alpaka::Complex x, alpaka::Complex y, int ulp) - { - return almost_equal(acc, x.real(), y.real(), ulp) && almost_equal(acc, x.imag(), y.imag(), ulp); - } + //! Stub for addition expressed same way as alpaka math traits + template + ALPAKA_FN_HOST_ACC auto plus(TAcc&, T const& arg1, T const& arg2) + { + return arg1 + arg2; + } - } // namespace math - } // namespace unit - } // namespace test -} // namespace alpaka + // https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon + template + ALPAKA_FN_ACC auto almost_equal(TAcc const& acc, FP x, FP y, int ulp) + -> std::enable_if_t::is_integer, bool> + { + // the machine epsilon has to be scaled to the magnitude of the values used + // and multiplied by the desired precision in ULPs (units in the last place) + return alpaka::math::abs(acc, x - y) + <= std::numeric_limits::epsilon() * alpaka::math::abs(acc, x + y) * static_cast(ulp) + // unless the result is subnormal + || alpaka::math::abs(acc, x - y) < std::numeric_limits::min(); + } + + //! Version for alpaka::Complex + template + ALPAKA_FN_ACC bool almost_equal(TAcc const& acc, alpaka::Complex x, alpaka::Complex y, int ulp) + { + return almost_equal(acc, x.real(), y.real(), ulp) && almost_equal(acc, x.imag(), y.imag(), ulp); + } +} // namespace mathtest diff --git a/test/unit/math/src/FloatEqualExactTest.cpp b/test/unit/math/src/FloatEqualExactTest.cpp index 22d31ce088b0..d0a8a8cead7f 100644 --- a/test/unit/math/src/FloatEqualExactTest.cpp +++ b/test/unit/math/src/FloatEqualExactTest.cpp @@ -9,9 +9,8 @@ #include #include -class FloatEqualExactTestKernel +struct FloatEqualExactTestKernel { -public: ALPAKA_NO_HOST_ACC_WARNING template ALPAKA_FN_ACC auto operator()(TAcc const& /* acc */, bool* success) const -> void diff --git a/test/unit/math/src/Functor.hpp b/test/unit/math/src/Functor.hpp index dc7d0a746768..aeebcf7f14ef 100644 --- a/test/unit/math/src/Functor.hpp +++ b/test/unit/math/src/Functor.hpp @@ -11,31 +11,25 @@ #include #include -namespace alpaka +namespace mathtest { - namespace test + //! Helper trait to define a type conversion before passing T to a std:: math function + //! + //! The default implementation does no conversion + //! + //! @tparam T input type + template + struct StdLibType { - namespace unit - { - namespace math - { - //! Helper trait to define a type conversion before passing T to a std:: math function - //! - //! The default implementation does no conversion - //! - //! @tparam T input type - template - struct StdLibType - { - using type = T; - }; - - //! Specialization converting alpaka::Complex to std::complex - template - struct StdLibType> - { - using type = std::complex; - }; + using type = T; + }; + + //! Specialization converting alpaka::Complex to std::complex + template + struct StdLibType> + { + using type = std::complex; + }; // Can be used with operator() that will use either the std. function or the // equivalent alpaka function (if an accelerator is passed additionally). @@ -131,303 +125,233 @@ namespace alpaka }; - ALPAKA_TEST_MATH_OP_FUNCTOR(OpAbs, Arity::Unary, std::abs, alpaka::math::abs, Range::Unrestricted) + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAbs, Arity::Unary, std::abs, alpaka::math::abs, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAcos, Arity::Unary, std::acos, alpaka::math::acos, Range::OneNeighbourhood) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAcosh, Arity::Unary, std::acosh, alpaka::math::acosh, Range::PositiveOnly) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpArg, Arity::Unary, std::arg, alpaka::math::arg, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAsin, Arity::Unary, std::asin, alpaka::math::asin, Range::OneNeighbourhood) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAsinh, Arity::Unary, std::asinh, alpaka::math::asinh, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAtan, Arity::Unary, std::atan, alpaka::math::atan, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpAtanh, Arity::Unary, std::atanh, alpaka::math::atanh, Range::OneNeighbourhood) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpCbrt, Arity::Unary, std::cbrt, alpaka::math::cbrt, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpCeil, Arity::Unary, std::ceil, alpaka::math::ceil, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpCos, Arity::Unary, std::cos, alpaka::math::cos, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpCosh, Arity::Unary, std::cosh, alpaka::math::cosh, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpErf, Arity::Unary, std::erf, alpaka::math::erf, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpExp, Arity::Unary, std::exp, alpaka::math::exp, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpFloor, Arity::Unary, std::floor, alpaka::math::floor, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpLog, Arity::Unary, std::log, alpaka::math::log, Range::PositiveOnly) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpLog2, Arity::Unary, std::log2, alpaka::math::log2, Range::PositiveOnly) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpLog10, Arity::Unary, std::log10, alpaka::math::log10, Range::PositiveOnly) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpRound, Arity::Unary, std::round, alpaka::math::round, Range::Unrestricted) + + // There is no std implementation, look in Defines.hpp. + ALPAKA_TEST_MATH_OP_FUNCTOR(OpRsqrt, Arity::Unary, rsqrt, alpaka::math::rsqrt, Range::PositiveOnly) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpSin, Arity::Unary, std::sin, alpaka::math::sin, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpSinh, Arity::Unary, std::sinh, alpaka::math::sinh, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpSqrt, Arity::Unary, std::sqrt, alpaka::math::sqrt, Range::PositiveAndZero) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpTan, Arity::Unary, std::tan, alpaka::math::tan, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpTanh, Arity::Unary, std::tanh, alpaka::math::tanh, Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpTrunc, Arity::Unary, std::trunc, alpaka::math::trunc, Range::Unrestricted) - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpAcos, - Arity::Unary, - std::acos, - alpaka::math::acos, - Range::OneNeighbourhood) + ALPAKA_TEST_MATH_OP_FUNCTOR(OpIsnan, Arity::Unary, std::isnan, alpaka::math::isnan, Range::Anything) - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpAcosh, - Arity::Unary, - std::acosh, - alpaka::math::acosh, - Range::PositiveOnly) + ALPAKA_TEST_MATH_OP_FUNCTOR(OpIsinf, Arity::Unary, std::isinf, alpaka::math::isinf, Range::Anything) - ALPAKA_TEST_MATH_OP_FUNCTOR(OpArg, Arity::Unary, std::arg, alpaka::math::arg, Range::Unrestricted) + ALPAKA_TEST_MATH_OP_FUNCTOR(OpIsfinite, Arity::Unary, std::isfinite, alpaka::math::isfinite, Range::Anything) - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpAsin, - Arity::Unary, - std::asin, - alpaka::math::asin, - Range::OneNeighbourhood) + // All binary operators. + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpAtan2, + Arity::Binary, + std::atan2, + alpaka::math::atan2, + Range::NotZero, + Range::NotZero) - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpAsinh, - Arity::Unary, - std::asinh, - alpaka::math::asinh, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpAtan, Arity::Unary, std::atan, alpaka::math::atan, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpAtanh, - Arity::Unary, - std::atanh, - alpaka::math::atanh, - Range::OneNeighbourhood) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpCbrt, Arity::Unary, std::cbrt, alpaka::math::cbrt, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpCeil, Arity::Unary, std::ceil, alpaka::math::ceil, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpCos, Arity::Unary, std::cos, alpaka::math::cos, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpCosh, Arity::Unary, std::cosh, alpaka::math::cosh, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpErf, Arity::Unary, std::erf, alpaka::math::erf, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpExp, Arity::Unary, std::exp, alpaka::math::exp, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpFloor, - Arity::Unary, - std::floor, - alpaka::math::floor, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpLog, Arity::Unary, std::log, alpaka::math::log, Range::PositiveOnly) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpLog2, Arity::Unary, std::log2, alpaka::math::log2, Range::PositiveOnly) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpLog10, - Arity::Unary, - std::log10, - alpaka::math::log10, - Range::PositiveOnly) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpRound, - Arity::Unary, - std::round, - alpaka::math::round, - Range::Unrestricted) - - // There is no std implementation, look in Defines.hpp. - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpRsqrt, - Arity::Unary, - alpaka::test::unit::math::rsqrt, - alpaka::math::rsqrt, - Range::PositiveOnly) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpSin, Arity::Unary, std::sin, alpaka::math::sin, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpSinh, Arity::Unary, std::sinh, alpaka::math::sinh, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpSqrt, - Arity::Unary, - std::sqrt, - alpaka::math::sqrt, - Range::PositiveAndZero) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpTan, Arity::Unary, std::tan, alpaka::math::tan, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpTanh, Arity::Unary, std::tanh, alpaka::math::tanh, Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpTrunc, - Arity::Unary, - std::trunc, - alpaka::math::trunc, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpIsnan, Arity::Unary, std::isnan, alpaka::math::isnan, Range::Anything) - - ALPAKA_TEST_MATH_OP_FUNCTOR(OpIsinf, Arity::Unary, std::isinf, alpaka::math::isinf, Range::Anything) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpIsfinite, - Arity::Unary, - std::isfinite, - alpaka::math::isfinite, - Range::Anything) - - // All binary operators. - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpAtan2, - Arity::Binary, - std::atan2, - alpaka::math::atan2, - Range::NotZero, - Range::NotZero) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpCopysign, - Arity::Binary, - std::copysign, - alpaka::math::copysign, - Range::Unrestricted, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpFmod, - Arity::Binary, - std::fmod, - alpaka::math::fmod, - Range::Unrestricted, - Range::NotZero) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpMax, - Arity::Binary, - std::max, - alpaka::math::max, - Range::Unrestricted, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpMin, - Arity::Binary, - std::min, - alpaka::math::min, - Range::Unrestricted, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpPow, - Arity::Binary, - std::pow, - alpaka::math::pow, - Range::PositiveAndZero, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpRemainder, - Arity::Binary, - std::remainder, - alpaka::math::remainder, - Range::Unrestricted, - Range::NotZero) - - // All ternary operators. - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpFma, - Arity::Ternary, - std::fma, - alpaka::math::fma, - Range::Unrestricted, - Range::Unrestricted, - Range::Unrestricted) - - // Unary functors to be used only for real types - using UnaryFunctorsReal = std::tuple< - OpAbs, - OpAcos, - OpAcosh, - OpArg, - OpAsin, - OpAsinh, - OpAtan, - OpAtanh, - OpCbrt, - OpCeil, - OpCos, - OpCosh, - OpErf, - OpExp, - OpFloor, - OpLog, - OpLog2, - OpLog10, - OpRound, - OpRsqrt, - OpSin, - OpSinh, - OpSqrt, - OpTan, - OpTanh, - OpTrunc, - OpIsnan, - OpIsinf, - OpIsfinite>; - - // Binary functors to be used only for real types - using BinaryFunctorsReal = std::tuple; - - // Ternary functors to be used only for real types - using TernaryFunctorsReal = std::tuple; - - // For complex numbers also test arithmetic operations - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpDivides, - Arity::Binary, - std::divides<>{}, - alpaka::test::unit::math::divides, - Range::Unrestricted, - Range::NotZero) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpMinus, - Arity::Binary, - std::minus<>{}, - alpaka::test::unit::math::minus, - Range::Unrestricted, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpMultiplies, - Arity::Binary, - std::multiplies<>{}, - alpaka::test::unit::math::multiplies, - Range::Unrestricted, - Range::Unrestricted) - - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpPlus, - Arity::Binary, - std::plus<>{}, - alpaka::test::unit::math::plus, - Range::Unrestricted, - Range::Unrestricted) - - // conj() is only tested for complex as it returns a complex type for real arguments - // and it doesn't fit the existing tests' infrastructure - ALPAKA_TEST_MATH_OP_FUNCTOR(OpConj, Arity::Unary, std::conj, alpaka::math::conj, Range::Unrestricted) - - // As a workaround for complex 0^0 unit tests issues, test complex pow for positive range only - ALPAKA_TEST_MATH_OP_FUNCTOR( - OpPowComplex, - Arity::Binary, - std::pow, - alpaka::math::pow, - Range::PositiveOnly, - Range::Unrestricted) - - // Unary functors to be used for both real and complex types - using UnaryFunctorsComplex = std::tuple< - OpAbs, - OpAcos, - OpAcosh, - OpArg, - OpAsin, - OpAsinh, - OpAtan, - OpAtanh, - OpConj, - OpCos, - OpCosh, - OpExp, - OpLog, - OpLog10, - OpRsqrt, - OpSin, - OpSinh, - OpSqrt, - OpTan, - OpTanh>; - - // Binary functors to be used for complex types - using BinaryFunctorsComplex = std::tuple; - - } // namespace math - } // namespace unit - } // namespace test -} // namespace alpaka + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpCopysign, + Arity::Binary, + std::copysign, + alpaka::math::copysign, + Range::Unrestricted, + Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpFmod, + Arity::Binary, + std::fmod, + alpaka::math::fmod, + Range::Unrestricted, + Range::NotZero) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpMax, + Arity::Binary, + std::max, + alpaka::math::max, + Range::Unrestricted, + Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpMin, + Arity::Binary, + std::min, + alpaka::math::min, + Range::Unrestricted, + Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpPow, + Arity::Binary, + std::pow, + alpaka::math::pow, + Range::PositiveAndZero, + Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpRemainder, + Arity::Binary, + std::remainder, + alpaka::math::remainder, + Range::Unrestricted, + Range::NotZero) + + // All ternary operators. + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpFma, + Arity::Ternary, + std::fma, + alpaka::math::fma, + Range::Unrestricted, + Range::Unrestricted, + Range::Unrestricted) + + // Unary functors to be used only for real types + using UnaryFunctorsReal = std::tuple< + OpAbs, + OpAcos, + OpAcosh, + OpArg, + OpAsin, + OpAsinh, + OpAtan, + OpAtanh, + OpCbrt, + OpCeil, + OpCos, + OpCosh, + OpErf, + OpExp, + OpFloor, + OpLog, + OpLog2, + OpLog10, + OpRound, + OpRsqrt, + OpSin, + OpSinh, + OpSqrt, + OpTan, + OpTanh, + OpTrunc, + OpIsnan, + OpIsinf, + OpIsfinite>; + + // Binary functors to be used only for real types + using BinaryFunctorsReal = std::tuple; + + // Ternary functors to be used only for real types + using TernaryFunctorsReal = std::tuple; + + // For complex numbers also test arithmetic operations + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpDivides, + Arity::Binary, + std::divides<>{}, + divides, + Range::Unrestricted, + Range::NotZero) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpMinus, + Arity::Binary, + std::minus<>{}, + minus, + Range::Unrestricted, + Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpMultiplies, + Arity::Binary, + std::multiplies<>{}, + multiplies, + Range::Unrestricted, + Range::Unrestricted) + + ALPAKA_TEST_MATH_OP_FUNCTOR(OpPlus, Arity::Binary, std::plus<>{}, plus, Range::Unrestricted, Range::Unrestricted) + + // conj() is only tested for complex as it returns a complex type for real arguments + // and it doesn't fit the existing tests' infrastructure + ALPAKA_TEST_MATH_OP_FUNCTOR(OpConj, Arity::Unary, std::conj, alpaka::math::conj, Range::Unrestricted) + + // As a workaround for complex 0^0 unit tests issues, test complex pow for positive range only + ALPAKA_TEST_MATH_OP_FUNCTOR( + OpPowComplex, + Arity::Binary, + std::pow, + alpaka::math::pow, + Range::PositiveOnly, + Range::Unrestricted) + + // Unary functors to be used for both real and complex types + using UnaryFunctorsComplex = std::tuple< + OpAbs, + OpAcos, + OpAcosh, + OpArg, + OpAsin, + OpAsinh, + OpAtan, + OpAtanh, + OpConj, + OpCos, + OpCosh, + OpExp, + OpLog, + OpLog10, + OpRsqrt, + OpSin, + OpSinh, + OpSqrt, + OpTan, + OpTanh>; + + // Binary functors to be used for complex types + using BinaryFunctorsComplex = std::tuple; +} // namespace mathtest diff --git a/test/unit/math/src/TestTemplate.hpp b/test/unit/math/src/TestTemplate.hpp index aa62afab8d2e..b417aeb4b346 100644 --- a/test/unit/math/src/TestTemplate.hpp +++ b/test/unit/math/src/TestTemplate.hpp @@ -18,163 +18,166 @@ #include -template -struct TestKernel +namespace mathtest { - //! @tparam TAcc Accelerator. - //! @tparam TFunctor Functor defined in Functor.hpp. - //! @param acc Accelerator given from alpaka. - //! @param functor Accessible with operator(). - ALPAKA_NO_HOST_ACC_WARNING - template - ALPAKA_FN_ACC auto operator()(TAcc const& acc, TResults* results, TFunctor const& functor, TArgs const* args) - const noexcept -> void + template + struct TestKernel { - for(size_t i = 0; i < TCapacity; ++i) + //! @tparam TAcc Accelerator. + //! @tparam TFunctor Functor defined in Functor.hpp. + //! @param acc Accelerator given from alpaka. + //! @param functor Accessible with operator(). + ALPAKA_NO_HOST_ACC_WARNING + template + ALPAKA_FN_ACC auto operator()(TAcc const& acc, TResults* results, TFunctor const& functor, TArgs const* args) + const noexcept -> void { - results[i] = functor(args[i], acc); + for(size_t i = 0; i < TCapacity; ++i) + { + results[i] = functor(args[i], acc); + } } - } -}; + }; -//! Helper trait to determine underlying type of real and complex numbers -template -struct UnderlyingType -{ - using type = T; -}; + //! Helper trait to determine underlying type of real and complex numbers + template + struct UnderlyingType + { + using type = T; + }; -//! Specialization for complex -template -struct UnderlyingType> -{ - using type = T; -}; - -//! Base test template for math unit tests -//! @tparam TAcc Accelerator. -//! @tparam TFunctor Functor defined in Functor.hpp. -template -struct TestTemplate -{ - //! wrappedFunctor is either a TFunctor{} or TFunctor{} wrapped into a host-device lambda - template - auto operator()(TWrappedFunctor const& wrappedFunctor = TWrappedFunctor{}) -> void + //! Specialization for complex + template + struct UnderlyingType> + { + using type = T; + }; + + //! Base test template for math unit tests + //! @tparam TAcc Accelerator. + //! @tparam TFunctor Functor defined in Functor.hpp. + template + struct TestTemplate { - std::random_device rd{}; - auto const seed = rd(); - INFO( - "testing" - << " acc:" << alpaka::core::demangled << " data type:" << alpaka::core::demangled - << " functor:" << alpaka::core::demangled << " seed:" << seed); - - // SETUP (defines and initialising) - // DevAcc is defined in Buffer.hpp too. - using DevAcc = alpaka::Dev; - - using Dim = alpaka::DimInt<1u>; - using Idx = std::size_t; - using WorkDiv = alpaka::WorkDivMembers; - using QueueAcc = alpaka::test::DefaultQueue; - using TArgsItem = alpaka::test::unit::math::ArgsItem; - - static constexpr auto capacity = 1000; - - using Args = alpaka::test::unit::math::Buffer; - using Results = alpaka::test::unit::math::Buffer; - - // Every functor is executed individual on one kernel. - static constexpr size_t elementsPerThread = 1u; - static constexpr size_t sizeExtent = 1u; - - auto const platformAcc = alpaka::Platform{}; - auto const devAcc = alpaka::getDevByIdx(platformAcc, 0); - auto const platformHost = alpaka::PlatformCpu{}; - auto const devHost = alpaka::getDevByIdx(platformHost, 0); - - QueueAcc queue{devAcc}; - - TestKernel kernel; - TFunctor functor; - Args args{devAcc}; - Results results{devAcc}; - - WorkDiv const workDiv = alpaka::getValidWorkDiv( - devAcc, - sizeExtent, - elementsPerThread, - false, - alpaka::GridBlockExtentSubDivRestrictions::Unrestricted); - // SETUP COMPLETED. - - // Fill the buffer with random test-numbers. - alpaka::test::unit::math::fillWithRndArgs(args, functor, seed); - using Underlying = typename UnderlyingType::type; - for(size_t i = 0; i < Results::capacity; ++i) - results(i) = static_cast(std::nan("")); - - // Copy both buffer to the device - args.copyToDevice(queue); - results.copyToDevice(queue); - - // Enqueue the kernel execution task. - auto const taskKernel - = alpaka::createTaskKernel(workDiv, kernel, results.pDevBuffer, wrappedFunctor, args.pDevBuffer); - alpaka::enqueue(queue, taskKernel); - - // Copy back the results (encapsulated in the buffer class). - results.copyFromDevice(queue); - alpaka::wait(queue); - std::cout.precision(std::numeric_limits::digits10 + 1); - - INFO("Operator: " << functor); - INFO("Type: " << alpaka::core::demangled); // Compiler specific. + //! wrappedFunctor is either a TFunctor{} or TFunctor{} wrapped into a host-device lambda + template + auto operator()(TWrappedFunctor const& wrappedFunctor = TWrappedFunctor{}) -> void + { + std::random_device rd{}; + auto const seed = rd(); + INFO( + "testing" + << " acc:" << alpaka::core::demangled << " data type:" << alpaka::core::demangled + << " functor:" << alpaka::core::demangled << " seed:" << seed); + + // SETUP (defines and initialising) + // DevAcc is defined in Buffer.hpp too. + using DevAcc = alpaka::Dev; + + using Dim = alpaka::DimInt<1u>; + using Idx = std::size_t; + using WorkDiv = alpaka::WorkDivMembers; + using QueueAcc = alpaka::test::DefaultQueue; + using TArgsItem = ArgsItem; + + static constexpr auto capacity = 1000; + + using Args = Buffer; + using Results = Buffer; + + // Every functor is executed individual on one kernel. + static constexpr size_t elementsPerThread = 1u; + static constexpr size_t sizeExtent = 1u; + + auto const platformAcc = alpaka::Platform{}; + auto const devAcc = alpaka::getDevByIdx(platformAcc, 0); + auto const platformHost = alpaka::PlatformCpu{}; + auto const devHost = alpaka::getDevByIdx(platformHost, 0); + + QueueAcc queue{devAcc}; + + TestKernel kernel; + TFunctor functor; + Args args{devAcc}; + Results results{devAcc}; + + WorkDiv const workDiv = alpaka::getValidWorkDiv( + devAcc, + sizeExtent, + elementsPerThread, + false, + alpaka::GridBlockExtentSubDivRestrictions::Unrestricted); + // SETUP COMPLETED. + + // Fill the buffer with random test-numbers. + fillWithRndArgs(args, functor, seed); + using Underlying = typename UnderlyingType::type; + for(size_t i = 0; i < Results::capacity; ++i) + results(i) = static_cast(std::nan("")); + + // Copy both buffer to the device + args.copyToDevice(queue); + results.copyToDevice(queue); + + // Enqueue the kernel execution task. + auto const taskKernel + = alpaka::createTaskKernel(workDiv, kernel, results.pDevBuffer, wrappedFunctor, args.pDevBuffer); + alpaka::enqueue(queue, taskKernel); + + // Copy back the results (encapsulated in the buffer class). + results.copyFromDevice(queue); + alpaka::wait(queue); + std::cout.precision(std::numeric_limits::digits10 + 1); + + INFO("Operator: " << functor); + INFO("Type: " << alpaka::core::demangled); // Compiler specific. #if ALPAKA_DEBUG_FULL - INFO( - "The args buffer: \n" - << std::setprecision(std::numeric_limits::digits10 + 1) << args << "\n"); + INFO( + "The args buffer: \n" + << std::setprecision(std::numeric_limits::digits10 + 1) << args << "\n"); #endif - for(size_t i = 0; i < Args::capacity; ++i) + for(size_t i = 0; i < Args::capacity; ++i) + { + TData std_result = functor(args(i)); + INFO("Idx i: " << i << " computed : " << results(i) << " vs expected: " << std_result); + REQUIRE(isApproxEqual(results(i), std_result)); + } + } + + //! Approximate comparison of real numbers + template + static bool isApproxEqual(T const& a, T const& b) { - TData std_result = functor(args(i)); - INFO("Idx i: " << i << " computed : " << results(i) << " vs expected: " << std_result); - REQUIRE(isApproxEqual(results(i), std_result)); + return a == Catch::Approx(b).margin(std::numeric_limits::epsilon()); } - } - //! Approximate comparison of real numbers - template - static bool isApproxEqual(T const& a, T const& b) - { - return a == Catch::Approx(b).margin(std::numeric_limits::epsilon()); - } - - //! Is complex number considered finite for math testing. - //! Complex numbers with absolute value close to max() of underlying type are considered infinite. - //! The reason is, CUDA/HIP implementation cannot guarantee correct treatment of such values due to implementing - //! some math functions via calls to others. For extreme values of arguments, it could cause intermediate results - //! to become infinite or NaN. So in this function we consider all large enough values to be effectively infinite - //! and equivalent to one another. Thus, the tests do not concern accuracy for extreme values. However, they still - //! check the implementation for "reasonable" values. - template - static bool isFinite(alpaka::Complex const& z) - { - auto const absValue = abs(z); - auto const maxAbs = static_cast(0.1) * std::numeric_limits::max(); - return std::isfinite(absValue) && (absValue < maxAbs); - } + //! Is complex number considered finite for math testing. + //! Complex numbers with absolute value close to max() of underlying type are considered infinite. + //! The reason is, CUDA/HIP implementation cannot guarantee correct treatment of such values due to + //! implementing some math functions via calls to others. For extreme values of arguments, it could cause + //! intermediate results to become infinite or NaN. So in this function we consider all large enough values to + //! be effectively infinite and equivalent to one another. Thus, the tests do not concern accuracy for extreme + //! values. However, they still check the implementation for "reasonable" values. + template + static bool isFinite(alpaka::Complex const& z) + { + auto const absValue = abs(z); + auto const maxAbs = static_cast(0.1) * std::numeric_limits::max(); + return std::isfinite(absValue) && (absValue < maxAbs); + } - //! Approximate comparison of complex numbers - template - static bool isApproxEqual(alpaka::Complex const& a, alpaka::Complex const& b) - { - // Consider all infinite values equal, @see comment at isFinite() - if(!isFinite(a) && !isFinite(b)) - return true; - // For the same reason use relative difference comparison with a large margin - auto const scalingFactor = static_cast(std::is_same_v ? 1.1e4 : 1.1e6); - auto const marginValue = scalingFactor * std::numeric_limits::epsilon(); - return (a.real() == Catch::Approx(b.real()).margin(marginValue).epsilon(marginValue)) - && (a.imag() == Catch::Approx(b.imag()).margin(marginValue).epsilon(marginValue)); - } -}; + //! Approximate comparison of complex numbers + template + static bool isApproxEqual(alpaka::Complex const& a, alpaka::Complex const& b) + { + // Consider all infinite values equal, @see comment at isFinite() + if(!isFinite(a) && !isFinite(b)) + return true; + // For the same reason use relative difference comparison with a large margin + auto const scalingFactor = static_cast(std::is_same_v ? 1.1e4 : 1.1e6); + auto const marginValue = scalingFactor * std::numeric_limits::epsilon(); + return (a.real() == Catch::Approx(b.real()).margin(marginValue).epsilon(marginValue)) + && (a.imag() == Catch::Approx(b.imag()).margin(marginValue).epsilon(marginValue)); + } + }; +} // namespace mathtest diff --git a/test/unit/math/src/mathComplexDouble.cpp b/test/unit/math/src/mathComplexDouble.cpp index 5e95586d5dab..d4478cba7ec5 100644 --- a/test/unit/math/src/mathComplexDouble.cpp +++ b/test/unit/math/src/mathComplexDouble.cpp @@ -20,8 +20,7 @@ using TestAccs = alpaka::test::EnabledAccs, std::size_t>; // This file only has unit tests for complex numbers in order to split the tests between object files and save compiler // memory. For the same reason single- and double-precision are done separately and not wrapped into a common template. -using FunctorsComplex = alpaka::meta:: - Concatenate; +using FunctorsComplex = alpaka::meta::Concatenate; using TestAccFunctorTuplesComplex = alpaka::meta::CartesianProduct; TEMPLATE_LIST_TEST_CASE("mathOpsComplexDouble", "[math] [operator]", TestAccFunctorTuplesComplex) @@ -29,7 +28,7 @@ TEMPLATE_LIST_TEST_CASE("mathOpsComplexDouble", "[math] [operator]", TestAccFunc // Same as "mathOpsFloat" template test, but for complex double. See detailed explanation there. using Acc = std::tuple_element_t<0u, TestType>; using Functor = std::tuple_element_t<1u, TestType>; - auto testTemplate = TestTemplate{}; + auto testTemplate = mathtest::TestTemplate{}; testTemplate.template operator()>(); } diff --git a/test/unit/math/src/mathComplexFloat.cpp b/test/unit/math/src/mathComplexFloat.cpp index ca52337bfe36..3ba8e7cb993e 100644 --- a/test/unit/math/src/mathComplexFloat.cpp +++ b/test/unit/math/src/mathComplexFloat.cpp @@ -20,8 +20,7 @@ using TestAccs = alpaka::test::EnabledAccs, std::size_t>; // This file only has unit tests for complex numbers in order to split the tests between object files and save compiler // memory. For the same reason single- and double-precision are done separately and not wrapped into a common template. -using FunctorsComplex = alpaka::meta:: - Concatenate; +using FunctorsComplex = alpaka::meta::Concatenate; using TestAccFunctorTuplesComplex = alpaka::meta::CartesianProduct; TEMPLATE_LIST_TEST_CASE("mathOpsComplexFloat", "[math] [operator]", TestAccFunctorTuplesComplex) @@ -29,7 +28,7 @@ TEMPLATE_LIST_TEST_CASE("mathOpsComplexFloat", "[math] [operator]", TestAccFunct // Same as "mathOpsFloat" template test, but for complex float. See detailed explanation there. using Acc = std::tuple_element_t<0u, TestType>; using Functor = std::tuple_element_t<1u, TestType>; - auto testTemplate = TestTemplate{}; + auto testTemplate = mathtest::TestTemplate{}; testTemplate.template operator()>(); } diff --git a/test/unit/math/src/mathDouble.cpp b/test/unit/math/src/mathDouble.cpp index c32d01de60d5..b9879f6310ac 100644 --- a/test/unit/math/src/mathDouble.cpp +++ b/test/unit/math/src/mathDouble.cpp @@ -15,10 +15,8 @@ using TestAccs = alpaka::test::EnabledAccs, std::size_t>; // This file only has unit tests for real numbers in order to split the tests between object files -using FunctorsReal = alpaka::meta::Concatenate< - alpaka::test::unit::math::UnaryFunctorsReal, - alpaka::test::unit::math::BinaryFunctorsReal, - alpaka::test::unit::math::TernaryFunctorsReal>; +using FunctorsReal = alpaka::meta:: + Concatenate; using TestAccFunctorTuplesReal = alpaka::meta::CartesianProduct; TEMPLATE_LIST_TEST_CASE("mathOpsDouble", "[math] [operator]", TestAccFunctorTuplesReal) @@ -26,6 +24,6 @@ TEMPLATE_LIST_TEST_CASE("mathOpsDouble", "[math] [operator]", TestAccFunctorTupl // Same as "mathOpsFloat" template test, but for double. See detailed explanation there. using Acc = std::tuple_element_t<0u, TestType>; using Functor = std::tuple_element_t<1u, TestType>; - auto testTemplate = TestTemplate{}; + auto testTemplate = mathtest::TestTemplate{}; testTemplate.template operator()(); } diff --git a/test/unit/math/src/mathFloat.cpp b/test/unit/math/src/mathFloat.cpp index 7f719d48b0d4..aaaf5478ba31 100644 --- a/test/unit/math/src/mathFloat.cpp +++ b/test/unit/math/src/mathFloat.cpp @@ -15,10 +15,8 @@ using TestAccs = alpaka::test::EnabledAccs, std::size_t>; // This file only has unit tests for real numbers in order to split the tests between object files -using FunctorsReal = alpaka::meta::Concatenate< - alpaka::test::unit::math::UnaryFunctorsReal, - alpaka::test::unit::math::BinaryFunctorsReal, - alpaka::test::unit::math::TernaryFunctorsReal>; +using FunctorsReal = alpaka::meta:: + Concatenate; using TestAccFunctorTuplesReal = alpaka::meta::CartesianProduct; TEMPLATE_LIST_TEST_CASE("mathOpsFloat", "[math] [operator]", TestAccFunctorTuplesReal) @@ -72,6 +70,6 @@ TEMPLATE_LIST_TEST_CASE("mathOpsFloat", "[math] [operator]", TestAccFunctorTuple using Acc = std::tuple_element_t<0u, TestType>; using Functor = std::tuple_element_t<1u, TestType>; - auto testTemplate = TestTemplate{}; + auto testTemplate = mathtest::TestTemplate{}; testTemplate.template operator()(); } diff --git a/test/unit/math/src/mathLambda.cpp b/test/unit/math/src/mathLambda.cpp index e18bf9009e0d..16dc6a75ab6d 100644 --- a/test/unit/math/src/mathLambda.cpp +++ b/test/unit/math/src/mathLambda.cpp @@ -24,13 +24,6 @@ using TestAccs = alpaka::test::EnabledAccs, std::size_t>; -// This file only has unit tests for real numbers in order to split the tests between object files -using UnaryFunctorsReal = alpaka::test::unit::math::UnaryFunctorsReal; -using BinaryFunctorsReal = alpaka::test::unit::math::BinaryFunctorsReal; -using TernaryFunctorsReal = alpaka::test::unit::math::TernaryFunctorsReal; -using UnaryFunctorsComplex = alpaka::test::unit::math::UnaryFunctorsComplex; -using BinaryFunctorsComplex = alpaka::test::unit::math::BinaryFunctorsComplex; - //! Caller of test template with additional lambda wrapping the functor //! @tparam TAcc Accelerator. //! @tparam TData Input data type.. @@ -47,10 +40,10 @@ struct LambdaMathTestTemplate // To avoid always using the first functor from the list, calculate index based on input data type sizes. constexpr uint32_t index = (sizeof(TAcc) + sizeof(TData) + sizeof(TFunctors)) % std::tuple_size_v; using Functor = std::tuple_element_t; - using ArgsItem = alpaka::test::unit::math::ArgsItem; + using ArgsItem = mathtest::ArgsItem; auto wrappedFunctor = [] ALPAKA_FN_HOST_ACC(ArgsItem const& arguments, TAcc const& acc) { return Functor{}(arguments, acc); }; - auto testTemplate = TestTemplate{}; + auto testTemplate = mathtest::TestTemplate{}; testTemplate.template operator()(wrappedFunctor); } }; @@ -59,33 +52,33 @@ TEMPLATE_LIST_TEST_CASE("mathOpsLambdaFloat", "[math] [operator]", TestAccs) { using Acc = TestType; auto testTemplate = LambdaMathTestTemplate{}; - testTemplate.template operator()(); - testTemplate.template operator()(); - testTemplate.template operator()(); + testTemplate.template operator()(); + testTemplate.template operator()(); + testTemplate.template operator()(); } TEMPLATE_LIST_TEST_CASE("mathOpsLambdaDouble", "[math] [operator]", TestAccs) { using Acc = TestType; auto testTemplate = LambdaMathTestTemplate{}; - testTemplate.template operator()(); - testTemplate.template operator()(); - testTemplate.template operator()(); + testTemplate.template operator()(); + testTemplate.template operator()(); + testTemplate.template operator()(); } TEMPLATE_LIST_TEST_CASE("mathOpsLambdaComplexFloat", "[math] [operator]", TestAccs) { using Acc = TestType; auto testTemplate = LambdaMathTestTemplate>{}; - testTemplate.template operator()(); - testTemplate.template operator()(); + testTemplate.template operator()(); + testTemplate.template operator()(); } TEMPLATE_LIST_TEST_CASE("mathOpsLambdaComplexDouble", "[math] [operator]", TestAccs) { using Acc = TestType; auto testTemplate = LambdaMathTestTemplate>{}; - testTemplate.template operator()(); - testTemplate.template operator()(); + testTemplate.template operator()(); + testTemplate.template operator()(); } #endif diff --git a/test/unit/math/src/powMixedTypes.cpp b/test/unit/math/src/powMixedTypes.cpp index ef02fe03a4c1..86785d8156f9 100644 --- a/test/unit/math/src/powMixedTypes.cpp +++ b/test/unit/math/src/powMixedTypes.cpp @@ -49,17 +49,15 @@ struct Convert, std::enable_if_t -class PowMixedTypesTestKernel +struct PowMixedTypesTestKernel { -public: ALPAKA_NO_HOST_ACC_WARNING template ALPAKA_FN_ACC auto operator()(TAcc const& acc, bool* success, TArg1 const arg1, TArg2 const arg2) const -> void { auto expected = alpaka::math::pow(acc, Convert{}(arg1), Convert{}(arg2)); auto actual = alpaka::math::pow(acc, arg1, arg2); - using alpaka::test::unit::math::almost_equal; - ALPAKA_CHECK(*success, almost_equal(acc, expected, actual, 1)); + ALPAKA_CHECK(*success, mathtest::almost_equal(acc, expected, actual, 1)); } }; diff --git a/test/unit/math/src/sincos.cpp b/test/unit/math/src/sincos.cpp index 55edd467a94c..9d49eafb16b6 100644 --- a/test/unit/math/src/sincos.cpp +++ b/test/unit/math/src/sincos.cpp @@ -15,9 +15,8 @@ #include -class SinCosTestKernel +struct SinCosTestKernel { -public: ALPAKA_NO_HOST_ACC_WARNING template ALPAKA_FN_ACC auto operator()(TAcc const& acc, bool* success, FP const arg) const -> void @@ -29,10 +28,10 @@ class SinCosTestKernel auto result_sin = FP{0}; auto result_cos = FP{0}; alpaka::math::sincos(acc, arg, result_sin, result_cos); - using alpaka::test::unit::math::almost_equal; ALPAKA_CHECK( *success, - almost_equal(acc, result_sin, check_sin, 1) && almost_equal(acc, result_cos, check_cos, 1)); + mathtest::almost_equal(acc, result_sin, check_sin, 1) + && mathtest::almost_equal(acc, result_cos, check_cos, 1)); } };