From 3e5f225dfd58bc8055dd14c2dd5ced0d63ab43e7 Mon Sep 17 00:00:00 2001 From: Thomas Hahn Date: Thu, 3 Oct 2024 15:48:16 -0400 Subject: [PATCH] Make mapped functions work with nda::Array as well as nda::Scalar objects --- c++/nda/blas/tools.hpp | 2 +- c++/nda/map.hpp | 14 ++ c++/nda/mapped_functions.hpp | 133 +++++++------------ c++/nda/mapped_functions.hxx | 222 ++++++++++++++++---------------- doc/DoxygenLayout.xml | 1 - test/c++/nda_math_functions.cpp | 42 ++++++ 6 files changed, 216 insertions(+), 198 deletions(-) diff --git a/c++/nda/blas/tools.hpp b/c++/nda/blas/tools.hpp index 3d9a7079f..e95e49dcb 100644 --- a/c++/nda/blas/tools.hpp +++ b/c++/nda/blas/tools.hpp @@ -53,7 +53,7 @@ namespace nda::blas { /// Specialization of nda::blas::is_conj_array_expr for the conjugate lazy expressions. template - static constexpr bool is_conj_array_expr> = true; + static constexpr bool is_conj_array_expr> = true; // Specialization of nda::blas::is_conj_array_expr for cvref types. template diff --git a/c++/nda/map.hpp b/c++/nda/map.hpp index 1b930aabe..0f512cf16 100644 --- a/c++/nda/map.hpp +++ b/c++/nda/map.hpp @@ -184,6 +184,20 @@ namespace nda { EXPECTS(((as.shape() == a0.shape()) && ...)); // same shape return {f, {std::forward(a0), std::forward(as)...}}; } + + /** + * @brief Function call operator that returns the result of the callable object applied to the scalar arguments. + * + * @tparam T0 First nda::Scalar argument type. + * @tparam Ts Rest of the nda::Scalar argument types. + * @param t0 First nda::Scalar argument. + * @param ts Rest of the nda::Scalar arguments. + * @return Result of the functor applied to the scalar arguments. + */ + template + auto operator()(T0 a0, Ts... as) const { + return f(a0, as...); + } }; /** diff --git a/c++/nda/mapped_functions.hpp b/c++/nda/mapped_functions.hpp index 69741e284..4661b46d7 100644 --- a/c++/nda/mapped_functions.hpp +++ b/c++/nda/mapped_functions.hpp @@ -38,92 +38,57 @@ namespace nda { * @{ */ - /** - * @brief Get the real part of a scalar. - * - * @tparam T Scalar type. - * @param t Scalar value. - * @return Real part of the scalar. - */ - template - auto real(T t) - requires(nda::is_scalar_v) - { - if constexpr (is_complex_v) { - return std::real(t); - } else { - return t; + namespace detail { + + // Get the real part of a scalar. + template + auto real(T t) + requires(nda::is_scalar_v) + { + if constexpr (is_complex_v) { + return std::real(t); + } else { + return t; + } } - } - /** - * @brief Get the complex conjugate of a scalar. - * - * @tparam T Scalar type. - * @param t Scalar value. - * @return The given scalar if it is not complex, otherwise its complex conjugate. - */ - template - auto conj(T t) - requires(nda::is_scalar_v) - { - if constexpr (is_complex_v) { - return std::conj(t); - } else { - return t; + // Get the complex conjugate of a scalar. + template + auto conj(T t) + requires(nda::is_scalar_v) + { + if constexpr (is_complex_v) { + return std::conj(t); + } else { + return t; + } } - } - /** - * @brief Get the squared absolute value of a double. - * - * @param x Double value. - * @return Squared absolute value of the given double. - */ - inline double abs2(double x) { return x * x; } + // Get the squared absolute value of a double. + inline double abs2(double x) { return x * x; } - /** - * @brief Get the squared absolute value of a std::complex. - * - * @param z std::complex value. - * @return Squared absolute value of the given complex number. - */ - inline double abs2(std::complex z) { return (conj(z) * z).real(); } + // Get the squared absolute value of a std::complex. + inline double abs2(std::complex z) { return (conj(z) * z).real(); } - /** - * @brief Check if a std::complex is NaN. - * - * @param z std::complex value. - * @return True if either the real or imaginary part of the given complex number is `NaN`, false otherwise. - */ - inline bool isnan(std::complex const &z) { return std::isnan(z.real()) or std::isnan(z.imag()); } + // Check if a std::complex is NaN. + inline bool isnan(std::complex const &z) { return std::isnan(z.real()) or std::isnan(z.imag()); } - /** - * @brief Calculate the integer power of an integer. - * - * @tparam T Integer type. - * @param x Base value. - * @param n Exponent value. - * @return The result of the base raised to the power of the exponent. - */ - template - T pow(T x, int n) - requires(std::is_integral_v) - { - T r = 1; - for (int i = 0; i < n; ++i) r *= x; - return r; - } + // Functor for nda::detail::conj. + struct conj_f { + auto operator()(auto const &x) const { return conj(x); }; + }; + + } // namespace detail /** - * @brief Lazy, coefficient-wise power function for nda::Array types. + * @brief Function pow for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). * - * @tparam A nda::Array type. - * @param a nda::Array object. + * @tparam A nda::ArrayOrScalar type.. + * @param a nda::ArrayOrScalar object. * @param p Exponent value. - * @return A lazy nda::expr_call object. + * @return A lazy nda::expr_call object (nda::Array) or the result of `std::pow` applied to the object (nda::Scalar). */ - template + template auto pow(A &&a, double p) { return nda::map([p](auto const &x) { using std::pow; @@ -131,23 +96,19 @@ namespace nda { })(std::forward(a)); } - /// Wrapper for nda::conj. - struct conj_f { - /// Function call operator that forwards the call to nda::conj. - auto operator()(auto const &x) const { return conj(x); }; - }; - /** - * @brief Lazy, coefficient-wise complex conjugate function for nda::Array types. + * @brief Function conj for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types with a complex + * value type). * - * @tparam A nda::Array type. - * @param a nda::Array object. - * @return A lazy nda::expr_call object if the array is complex valued, otherwise the array itself. + * @tparam A nda::ArrayOrScalar type.. + * @param a nda::ArrayOrScalar object. + * @return A lazy nda::expr_call object (nda::Array and complex valued), the forwarded input object (nda::Array and + * not complex valued) or the complex conjugate of the scalar input. */ - template + template decltype(auto) conj(A &&a) { if constexpr (is_complex_v>) - return nda::map(conj_f{})(std::forward(a)); + return nda::map(detail::conj_f{})(std::forward(a)); else return std::forward(a); } diff --git a/c++/nda/mapped_functions.hxx b/c++/nda/mapped_functions.hxx index 5469dfbbf..d65b7ffaa 100644 --- a/c++/nda/mapped_functions.hxx +++ b/c++/nda/mapped_functions.hxx @@ -1,6 +1,7 @@ /** * @file - * @brief Provides lazy, coefficient-wise array operations of standard mathematical functions. + * @brief Provides lazy, coefficient-wise array operations of standard mathematical functions together with overloads + * for nda::Scalar types. */ #pragma once @@ -29,12 +30,12 @@ ---- normal mapping ------- VIMEXPAND abs imag floor - /// \brief Lazy, coefficient-wise @ function for nda::Array types. + /// \brief Function @ for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::@` applied to the object (nda::Scalar). + template auto @(A &&a) { return nda::map( [](auto const &x) { @@ -46,26 +47,26 @@ --------- same, no using std::------- VIMEXPAND real abs2 isnan - /// \brief Lazy, coefficient-wise @ function for nda::Array types. + /// \brief Function @ for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type.. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `nda::detail::@` applied to the object (nda::Scalar). + template auto @(A &&a) { return nda::map( - [](auto const &x) {return @(x); })(std::forward(a)); + [](auto const &x) {return detail::@(x); })(std::forward(a)); } --------- mapping with matrix excluded ------- VIMEXPAND exp cos sin tan cosh sinh tanh acos asin atan log sqrt - /// \brief Lazy, coefficient-wise @ function for non-matrix nda::Array types. + /// \brief Function @ for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::@` applied to the object (nda::Scalar). + template auto @(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -86,12 +87,12 @@ namespace nda { // --- VIMEXPAND_START --DO NOT EDIT BELOW -- - /// \brief Lazy, coefficient-wise abs function for nda::Array types. + /// \brief Function abs for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::abs` applied to the object (nda::Scalar). + template auto abs(A &&a) { return nda::map( [](auto const &x) { @@ -100,12 +101,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise imag function for nda::Array types. + /// \brief Function imag for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::imag` applied to the object (nda::Scalar). + template auto imag(A &&a) { return nda::map( [](auto const &x) { @@ -114,12 +115,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise floor function for nda::Array types. + /// \brief Function floor for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::floor` applied to the object (nda::Scalar). + template auto floor(A &&a) { return nda::map( [](auto const &x) { @@ -128,45 +129,45 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise real function for nda::Array types. + /// \brief Function real for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type.. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `nda::detail::real` applied to the object (nda::Scalar). + template auto real(A &&a) { return nda::map( - [](auto const &x) {return real(x); })(std::forward(a)); + [](auto const &x) {return detail::real(x); })(std::forward(a)); } - /// \brief Lazy, coefficient-wise abs2 function for nda::Array types. + /// \brief Function abs2 for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type.. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `nda::detail::abs2` applied to the object (nda::Scalar). + template auto abs2(A &&a) { return nda::map( - [](auto const &x) {return abs2(x); })(std::forward(a)); + [](auto const &x) {return detail::abs2(x); })(std::forward(a)); } - /// \brief Lazy, coefficient-wise isnan function for nda::Array types. + /// \brief Function isnan for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type.. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `nda::detail::isnan` applied to the object (nda::Scalar). + template auto isnan(A &&a) { return nda::map( - [](auto const &x) {return isnan(x); })(std::forward(a)); + [](auto const &x) {return detail::isnan(x); })(std::forward(a)); } - /// \brief Lazy, coefficient-wise exp function for non-matrix nda::Array types. + /// \brief Function exp for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::exp` applied to the object (nda::Scalar). + template auto exp(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -175,12 +176,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise cos function for non-matrix nda::Array types. + /// \brief Function cos for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::cos` applied to the object (nda::Scalar). + template auto cos(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -189,12 +190,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise sin function for non-matrix nda::Array types. + /// \brief Function sin for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::sin` applied to the object (nda::Scalar). + template auto sin(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -203,12 +204,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise tan function for non-matrix nda::Array types. + /// \brief Function tan for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::tan` applied to the object (nda::Scalar). + template auto tan(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -217,12 +218,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise cosh function for non-matrix nda::Array types. + /// \brief Function cosh for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::cosh` applied to the object (nda::Scalar). + template auto cosh(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -231,12 +232,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise sinh function for non-matrix nda::Array types. + /// \brief Function sinh for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::sinh` applied to the object (nda::Scalar). + template auto sinh(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -245,12 +246,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise tanh function for non-matrix nda::Array types. + /// \brief Function tanh for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::tanh` applied to the object (nda::Scalar). + template auto tanh(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -259,12 +260,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise acos function for non-matrix nda::Array types. + /// \brief Function acos for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::acos` applied to the object (nda::Scalar). + template auto acos(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -273,12 +274,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise asin function for non-matrix nda::Array types. + /// \brief Function asin for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::asin` applied to the object (nda::Scalar). + template auto asin(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -287,12 +288,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise atan function for non-matrix nda::Array types. + /// \brief Function atan for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::atan` applied to the object (nda::Scalar). + template auto atan(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -301,12 +302,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise log function for non-matrix nda::Array types. + /// \brief Function log for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::log` applied to the object (nda::Scalar). + template auto log(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -315,12 +316,12 @@ namespace nda { })(std::forward(a)); } - /// \brief Lazy, coefficient-wise sqrt function for non-matrix nda::Array types. + /// \brief Function sqrt for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types). /// - /// \tparam A nda::Array type. - /// \param a nda::Array object. - /// \return A lazy nda::expr_call object. - template + /// \tparam A nda::ArrayOrScalar type. + /// \param a nda::ArrayOrScalar object. + /// \return A lazy nda::expr_call object (nda::Array) or the result of `std::sqrt` applied to the object (nda::Scalar). + template auto sqrt(A &&a) requires(get_algebra != 'M') { return nda::map( [](auto const &x) { @@ -330,4 +331,5 @@ namespace nda { } /** @} */ + } diff --git a/doc/DoxygenLayout.xml b/doc/DoxygenLayout.xml index c7ddd5d4f..6831b411a 100644 --- a/doc/DoxygenLayout.xml +++ b/doc/DoxygenLayout.xml @@ -55,7 +55,6 @@ - diff --git a/test/c++/nda_math_functions.cpp b/test/c++/nda_math_functions.cpp index 217d27dca..cf9ef2d0e 100644 --- a/test/c++/nda_math_functions.cpp +++ b/test/c++/nda_math_functions.cpp @@ -36,6 +36,8 @@ struct NDAMathFunction : public ::testing::Test { nda::array A_d; nda::array, 3> A_c; static auto constexpr nan = std::numeric_limits::quiet_NaN(); + static auto constexpr dbl_c = -0.1234; + static auto constexpr cplx_c = std::complex{-1.234, 2.345}; }; TEST_F(NDAMathFunction, Abs) { @@ -45,6 +47,8 @@ TEST_F(NDAMathFunction, Abs) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::abs(A_d(idxs...))); EXPECT_DOUBLE_EQ(B_c(idxs...), std::abs(A_c(idxs...))); }); + EXPECT_DOUBLE_EQ(nda::abs(dbl_c), std::abs(dbl_c)); + EXPECT_DOUBLE_EQ(nda::abs(cplx_c), std::abs(cplx_c)); } TEST_F(NDAMathFunction, Abs2) { @@ -54,6 +58,8 @@ TEST_F(NDAMathFunction, Abs2) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::abs(A_d(idxs...)) * std::abs(A_d(idxs...))); EXPECT_DOUBLE_EQ(B_c(idxs...), std::abs(A_c(idxs...)) * std::abs(A_c(idxs...))); }); + EXPECT_DOUBLE_EQ(nda::abs2(dbl_c), std::abs(dbl_c) * std::abs(dbl_c)); + EXPECT_DOUBLE_EQ(nda::abs2(cplx_c), std::abs(cplx_c) * std::abs(cplx_c)); } TEST_F(NDAMathFunction, Acos) { @@ -63,6 +69,8 @@ TEST_F(NDAMathFunction, Acos) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::acos(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::acos(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::acos(dbl_c), std::acos(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::acos(cplx_c), std::acos(cplx_c)); } TEST_F(NDAMathFunction, Asin) { @@ -72,6 +80,8 @@ TEST_F(NDAMathFunction, Asin) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::asin(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::asin(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::asin(dbl_c), std::asin(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::asin(cplx_c), std::asin(cplx_c)); } TEST_F(NDAMathFunction, Atan) { @@ -81,6 +91,8 @@ TEST_F(NDAMathFunction, Atan) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::atan(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::atan(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::atan(dbl_c), std::atan(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::atan(cplx_c), std::atan(cplx_c)); } TEST_F(NDAMathFunction, Conj) { @@ -90,6 +102,8 @@ TEST_F(NDAMathFunction, Conj) { EXPECT_EQ(B_d(idxs...), std::conj(A_d(idxs...))); EXPECT_EQ(B_c(idxs...), std::conj(A_c(idxs...))); }); + EXPECT_EQ(nda::conj(dbl_c), std::conj(dbl_c)); + EXPECT_EQ(nda::conj(cplx_c), std::conj(cplx_c)); } TEST_F(NDAMathFunction, Cos) { @@ -99,6 +113,8 @@ TEST_F(NDAMathFunction, Cos) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::cos(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::cos(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::cos(dbl_c), std::cos(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::cos(cplx_c), std::cos(cplx_c)); } TEST_F(NDAMathFunction, Cosh) { @@ -108,6 +124,8 @@ TEST_F(NDAMathFunction, Cosh) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::cosh(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::cosh(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::cosh(dbl_c), std::cosh(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::cosh(cplx_c), std::cosh(cplx_c)); } TEST_F(NDAMathFunction, Dagger) { @@ -134,11 +152,14 @@ TEST_F(NDAMathFunction, Exp) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::exp(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::exp(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::exp(dbl_c), std::exp(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::exp(cplx_c), std::exp(cplx_c)); } TEST_F(NDAMathFunction, Floor) { auto B_d = nda::make_regular(nda::floor(A_d)); nda::for_each(shape, [&](auto... idxs) { EXPECT_EQ(B_d(idxs...), std::floor(A_d(idxs...))); }); + EXPECT_DOUBLE_EQ(nda::floor(dbl_c), std::floor(dbl_c)); } TEST_F(NDAMathFunction, FrobeniusNorm) { @@ -155,6 +176,8 @@ TEST_F(NDAMathFunction, Imag) { EXPECT_EQ(B_d(idxs...), std::imag(A_d(idxs...))); EXPECT_EQ(B_c(idxs...), std::imag(A_c(idxs...))); }); + EXPECT_EQ(nda::imag(dbl_c), std::imag(dbl_c)); + EXPECT_EQ(nda::imag(cplx_c), std::imag(cplx_c)); } TEST_F(NDAMathFunction, Isnan) { @@ -168,6 +191,8 @@ TEST_F(NDAMathFunction, Isnan) { EXPECT_EQ(B_d(idxs...), std::isnan(A_d(idxs...))); EXPECT_EQ(B_c(idxs...), nda::isnan(A_c(idxs...))); }); + EXPECT_EQ(nda::isnan(dbl_c), std::isnan(dbl_c)); + EXPECT_EQ(nda::isnan(cplx_c), nda::detail::isnan(cplx_c)); } TEST_F(NDAMathFunction, Log) { @@ -178,6 +203,8 @@ TEST_F(NDAMathFunction, Log) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::log(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::log(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::log(nda::abs(dbl_c)), std::log(std::abs(dbl_c))); + EXPECT_COMPLEX_NEAR(nda::log(cplx_c), std::log(cplx_c)); } TEST_F(NDAMathFunction, MapCustom) { @@ -197,6 +224,8 @@ TEST_F(NDAMathFunction, Pow) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::pow(A_d(idxs...), 2)); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::pow(A_c(idxs...), 2), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::pow(dbl_c, 2), std::pow(dbl_c, 2)); + EXPECT_COMPLEX_NEAR(nda::pow(cplx_c, 2), std::pow(cplx_c, 2)); } TEST_F(NDAMathFunction, Real) { @@ -206,6 +235,8 @@ TEST_F(NDAMathFunction, Real) { EXPECT_EQ(B_d(idxs...), std::real(A_d(idxs...))); EXPECT_EQ(B_c(idxs...), std::real(A_c(idxs...))); }); + EXPECT_EQ(nda::real(dbl_c), std::real(dbl_c)); + EXPECT_EQ(nda::real(cplx_c), std::real(cplx_c)); } TEST_F(NDAMathFunction, Sin) { @@ -215,6 +246,8 @@ TEST_F(NDAMathFunction, Sin) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::sin(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::sin(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::sin(dbl_c), std::sin(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::sin(cplx_c), std::sin(cplx_c)); } TEST_F(NDAMathFunction, Sinh) { @@ -224,6 +257,8 @@ TEST_F(NDAMathFunction, Sinh) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::sinh(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::sinh(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::sinh(dbl_c), std::sinh(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::sinh(cplx_c), std::sinh(cplx_c)); } TEST_F(NDAMathFunction, Sqrt) { @@ -234,6 +269,8 @@ TEST_F(NDAMathFunction, Sqrt) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::sqrt(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::sqrt(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::sqrt(nda::abs(dbl_c)), std::sqrt(std::abs(dbl_c))); + EXPECT_COMPLEX_NEAR(nda::sqrt(cplx_c), std::sqrt(cplx_c)); } TEST_F(NDAMathFunction, Tan) { @@ -243,6 +280,8 @@ TEST_F(NDAMathFunction, Tan) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::tan(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::tan(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::tan(dbl_c), std::tan(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::tan(cplx_c), std::tan(cplx_c)); } TEST_F(NDAMathFunction, Tanh) { @@ -252,6 +291,8 @@ TEST_F(NDAMathFunction, Tanh) { EXPECT_DOUBLE_EQ(B_d(idxs...), std::tanh(A_d(idxs...))); EXPECT_COMPLEX_NEAR(B_c(idxs...), std::tanh(A_c(idxs...)), 1e-14); }); + EXPECT_DOUBLE_EQ(nda::tanh(dbl_c), std::tanh(dbl_c)); + EXPECT_COMPLEX_NEAR(nda::tanh(cplx_c), std::tanh(cplx_c)); } TEST_F(NDAMathFunction, Trace) { @@ -309,3 +350,4 @@ TEST_F(NDAMathFunction, CombineMathFunctionsWithArithmeticOps) { EXPECT_ARRAY_NEAR(mat_t(transpose(C)), C_transp); EXPECT_ARRAY_NEAR(mat_t(C * conj(transpose(C))), mat_t(transpose(conj(C) * transpose(C)))); } +