SpECTRE Documentation Coverage Report
Current view: top level - Utilities - Functional.hpp Hit Total Coverage
Commit: 3c072f0ce967e2e56649d3fa12aa2a0e4fe2a42e Lines: 37 68 54.4 %
Date: 2024-04-23 20:50:18
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <algorithm>
       7             : #include <cmath>
       8             : #include <complex>
       9             : #include <cstddef>
      10             : #include <tuple>
      11             : #include <utility>
      12             : 
      13             : #include "Utilities/ConstantExpressions.hpp"  // IWYU pragma: keep  // for pow<>
      14             : #include "Utilities/ContainerHelpers.hpp"
      15             : #include "Utilities/ErrorHandling/Assert.hpp"
      16             : #include "Utilities/ErrorHandling/StaticAssert.hpp"
      17             : #include "Utilities/ForceInline.hpp"
      18             : #include "Utilities/Math.hpp"
      19             : 
      20             : /*!
      21             :  * \ingroup UtilitiesGroup
      22             :  * \brief Higher order function objects similar to `std::plus`, etc.
      23             :  *
      24             :  * \details
      25             :  * These chaining function objects can be used to represent highly general
      26             :  * mathematical operations
      27             :  * 1. as types, which can be passed around in template arguments, and
      28             :  * 2. such that any time they can be evaluated at compile time, they will be.
      29             :  *
      30             :  * As an illustrative example, consider the definition of a general sinusoid
      31             :  * function object type :
      32             :  * \snippet Utilities/Test_Functional.cpp using_sinusoid
      33             :  * which then gives a type which when instantiated and evaluated will give the
      34             :  * answer \f$ a\times\sin(b + c \times d)\f$ from calling `Sinusoid{}(a,b,c,d)`
      35             :  *
      36             :  * As a more creative example, we can take advantage of literals to make, for
      37             :  * instance, distributions. Let's make a Gaussian with mean at 5.0 and unity
      38             :  * variance
      39             :  * \snippet Utilities/Test_Functional.cpp using_gaussian
      40             :  *
      41             :  * This gives us a function object whose call operator takes one argument that
      42             :  * gives the value of the desired Gaussian distribution \f$ e^{-(x - 5.0)^2}
      43             :  * \f$
      44             :  */
      45           1 : namespace funcl {
      46             : // using for overload resolution with blaze
      47             : using std::max;
      48             : using std::min;
      49             : 
      50             : /// \cond
      51             : template <size_t Arity>
      52             : struct Functional {
      53             :   static constexpr size_t arity = Arity;
      54             : 
      55             :  protected:
      56             :   template <class C, size_t Offset, class... Ts, size_t... Is>
      57             :   static constexpr decltype(auto) helper(const std::tuple<Ts...>& t,
      58             :                                          std::index_sequence<Is...> /*meta*/) {
      59             :     return C{}(std::get<Offset + Is>(t)...);
      60             :   }
      61             : };
      62             : 
      63             : struct Identity;
      64             : /// \endcond
      65             : 
      66             : /// Functional that asserts that the function object `C` applied to the first
      67             : /// and second arguments are equal and returns the function object C applied to
      68             : /// the first argument
      69             : template <class C = Identity>
      70           1 : struct AssertEqual : Functional<2> {
      71             :   template <class T>
      72           0 :   const T& operator()(const T& t0, const T& t1) {
      73             :     DEBUG_STATIC_ASSERT(
      74             :         C::arity == 1,
      75             :         "The arity of the functional passed to AssertEqual must be 1");
      76             :     ASSERT(C{}(t0) == C{}(t1), "Values are not equal in funcl::AssertEqual "
      77             :            << C{}(t0) << " and " << C{}(t1));
      78             :     return C{}(t0);
      79             :   }
      80             : };
      81             : 
      82           0 : #define MAKE_BINARY_FUNCTIONAL(NAME, OPERATOR)                                 \
      83             :   /** Functional for computing `OPERATOR` from two objects */                  \
      84             :   template <class C0 = Identity, class C1 = C0>                                \
      85             :   struct NAME : Functional<C0::arity + C1::arity> {                            \
      86             :     using base = Functional<C0::arity + C1::arity>;                            \
      87             :     template <class... Ts>                                                     \
      88             :     constexpr auto operator()(const Ts&... ts) {                               \
      89             :       return OPERATOR(                                                         \
      90             :           base::template helper<C0, 0>(std::tuple<const Ts&...>(ts...),        \
      91             :                                        std::make_index_sequence<C0::arity>{}), \
      92             :           base::template helper<C1, C0::arity>(                                \
      93             :               std::tuple<const Ts&...>(ts...),                                 \
      94             :               std::make_index_sequence<C1::arity>{}));                         \
      95             :     }                                                                          \
      96             :   };                                                                           \
      97             :   /** \cond */                                                                 \
      98             :   template <class C1>                                                          \
      99             :   struct NAME<Identity, C1> : Functional<1 + C1::arity> {                      \
     100             :     template <class T0, class... Ts>                                           \
     101             :     constexpr auto operator()(const T0& t0, const Ts&... ts) {                 \
     102             :       return OPERATOR(t0, C1{}(ts...));                                        \
     103             :     }                                                                          \
     104             :   };                                                                           \
     105             :   template <>                                                                  \
     106             :   struct NAME<Identity, Identity> : Functional<2> {                            \
     107             :     template <class T0, class T1>                                              \
     108             :     constexpr auto operator()(const T0& t0, const T1& t1) {                    \
     109             :       return OPERATOR(t0, t1);                                                 \
     110             :     }                                                                          \
     111             :   } /** \endcond */
     112             : 
     113           0 : #define MAKE_BINARY_INPLACE_OPERATOR(NAME, OPERATOR)               \
     114             :   /** Functional for computing `OPERATOR` of two objects */        \
     115             :   template <class C0 = Identity, class C1 = C0>                    \
     116             :   struct NAME : Functional<C0::arity + C1::arity> {                \
     117             :     using base = Functional<C0::arity + C1::arity>;                \
     118             :     template <class... Ts>                                         \
     119             :     constexpr decltype(auto) operator()(Ts&... ts) {               \
     120             :       return base::template helper<C0, 0>(                         \
     121             :           std::tuple<const Ts&...>(ts...),                         \
     122             :           std::make_index_sequence<C0::arity>{})                   \
     123             :           OPERATOR base::template helper<C1, C0::arity>(           \
     124             :               std::tuple<const Ts&...>(ts...),                     \
     125             :               std::make_index_sequence<C1::arity>{});              \
     126             :     }                                                              \
     127             :   };                                                               \
     128             :   /** \cond */                                                     \
     129             :   template <class C1>                                              \
     130             :   struct NAME<Identity, C1> : Functional<1 + C1::arity> {          \
     131             :     template <class T0, class... Ts>                               \
     132             :     constexpr decltype(auto) operator()(T0& t0, const Ts&... ts) { \
     133             :       return t0 OPERATOR C1{}(ts...);                              \
     134             :     }                                                              \
     135             :   };                                                               \
     136             :   template <>                                                      \
     137             :   struct NAME<Identity, Identity> : Functional<2> {                \
     138             :     static constexpr size_t arity = 2;                             \
     139             :     template <class T0, class T1>                                  \
     140             :     constexpr decltype(auto) operator()(T0& t0, const T1& t1) {    \
     141             :       return t0 OPERATOR t1;                                       \
     142             :     }                                                              \
     143             :   } /** \endcond */
     144             : 
     145           0 : #define MAKE_BINARY_OPERATOR(NAME, OPERATOR)                   \
     146             :   /** Functional for computing `OPERATOR` of two objects */    \
     147             :   template <class C0 = Identity, class C1 = C0>                \
     148             :   struct NAME : Functional<C0::arity + C1::arity> {            \
     149             :     using base = Functional<C0::arity + C1::arity>;            \
     150             :     template <class... Ts>                                     \
     151             :     constexpr auto operator()(const Ts&... ts) {               \
     152             :       return base::template helper<C0, 0>(                     \
     153             :           std::tuple<const Ts&...>(ts...),                     \
     154             :           std::make_index_sequence<C0::arity>{})               \
     155             :           OPERATOR base::template helper<C1, C0::arity>(       \
     156             :               std::tuple<const Ts&...>(ts...),                 \
     157             :               std::make_index_sequence<C1::arity>{});          \
     158             :     }                                                          \
     159             :   };                                                           \
     160             :   /** \cond */                                                 \
     161             :   template <class C1>                                          \
     162             :   struct NAME<Identity, C1> : Functional<1 + C1::arity> {      \
     163             :     template <class T0, class... Ts>                           \
     164             :     constexpr auto operator()(const T0& t0, const Ts&... ts) { \
     165             :       return t0 OPERATOR C1{}(ts...);                          \
     166             :     }                                                          \
     167             :   };                                                           \
     168             :   template <>                                                  \
     169             :   struct NAME<Identity, Identity> : Functional<2> {            \
     170             :     static constexpr size_t arity = 2;                         \
     171             :     template <class T0, class T1>                              \
     172             :     constexpr auto operator()(const T0& t0, const T1& t1) {    \
     173             :       return t0 OPERATOR t1;                                   \
     174             :     }                                                          \
     175             :   } /** \endcond */
     176             : 
     177           0 : #define MAKE_LITERAL_VAL(NAME, VAL)                                    \
     178             :   /** Functional literal for `VAL` */                                  \
     179             :   struct Literal##NAME : Functional<0> {                               \
     180             :     constexpr double operator()() { return static_cast<double>(VAL); } \
     181             :   }
     182             : 
     183           0 : #define MAKE_UNARY_FUNCTIONAL(NAME, OPERATOR)             \
     184             :   /** Functional for computing `OPERATOR` on an object */ \
     185             :   template <typename C0 = Identity>                       \
     186             :   struct NAME;                                            \
     187             :   /** \cond */                                            \
     188             :   template <typename C0>                                  \
     189             :   struct NAME : Functional<C0::arity> {                   \
     190             :     template <class... Ts>                                \
     191             :     constexpr auto operator()(const Ts&... ts) {          \
     192             :       return OPERATOR(C0{}(ts...));                       \
     193             :     }                                                     \
     194             :   };                                                      \
     195             :   template <>                                             \
     196             :   struct NAME<Identity> : Functional<1> {                 \
     197             :     template <class T0>                                   \
     198             :     constexpr auto operator()(const T0& t0) {             \
     199             :       return OPERATOR(t0);                                \
     200             :     }                                                     \
     201             :   } /** \endcond */
     202             : 
     203             : /// Functional to retrieve the `ArgumentIndex`th argument
     204             : template <size_t Arity, size_t ArgumentIndex = 0, class C = Identity>
     205           1 : struct GetArgument : Functional<Arity> {
     206             :   template <class... Ts>
     207           0 :   constexpr decltype(auto) operator()(const Ts&... ts) {
     208             :     static_assert(Arity == sizeof...(Ts),
     209             :                   "The arity passed to GetArgument must be the same as the "
     210             :                   "actually arity of the function.");
     211             :     return C{}(std::get<ArgumentIndex>(std::tuple<const Ts&...>(ts...)));
     212             :   }
     213             : };
     214             : 
     215             : /// The identity higher order function object
     216           1 : struct Identity : Functional<1> {
     217             :   template <class T>
     218           0 :   SPECTRE_ALWAYS_INLINE constexpr const T& operator()(const T& t) {
     219             :     return t;
     220             :   }
     221             : };
     222             : 
     223             : template <int val, typename Type = double>
     224           0 : struct Literal : Functional<0> {
     225           0 :   constexpr Type operator()() { return static_cast<Type>(val); }
     226             : };
     227             : 
     228           0 : MAKE_BINARY_INPLACE_OPERATOR(DivAssign, /=);
     229           0 : MAKE_BINARY_INPLACE_OPERATOR(MinusAssign, -=);
     230           0 : MAKE_BINARY_INPLACE_OPERATOR(MultAssign, *=);
     231           0 : MAKE_BINARY_INPLACE_OPERATOR(PlusAssign, +=);
     232             : 
     233           0 : MAKE_BINARY_OPERATOR(Divides, /);
     234           0 : MAKE_BINARY_OPERATOR(Minus, -);
     235           0 : MAKE_BINARY_OPERATOR(Multiplies, *);
     236           0 : MAKE_BINARY_OPERATOR(Plus, +);
     237           0 : MAKE_BINARY_OPERATOR(And, and);
     238           0 : MAKE_BINARY_OPERATOR(Or, or);
     239             : 
     240           0 : MAKE_BINARY_FUNCTIONAL(Atan2, atan2);
     241           0 : MAKE_BINARY_FUNCTIONAL(Hypot, hypot);
     242           0 : MAKE_BINARY_FUNCTIONAL(Max, max);
     243           0 : MAKE_BINARY_FUNCTIONAL(Min, min);
     244           0 : MAKE_BINARY_FUNCTIONAL(Pow, pow);
     245             : 
     246           0 : MAKE_LITERAL_VAL(Pi, M_PI);
     247           0 : MAKE_LITERAL_VAL(E, M_E);
     248             : 
     249           1 : MAKE_UNARY_FUNCTIONAL(Abs, abs);
     250           1 : MAKE_UNARY_FUNCTIONAL(Acos, acos);
     251           1 : MAKE_UNARY_FUNCTIONAL(Acosh, acosh);
     252           1 : MAKE_UNARY_FUNCTIONAL(Asin, asin);
     253           1 : MAKE_UNARY_FUNCTIONAL(Asinh, asinh);
     254           1 : MAKE_UNARY_FUNCTIONAL(Atan, atan);
     255           1 : MAKE_UNARY_FUNCTIONAL(Atanh, atanh);
     256           1 : MAKE_UNARY_FUNCTIONAL(Cbrt, cbrt);
     257           1 : MAKE_UNARY_FUNCTIONAL(Conj, conj);
     258           1 : MAKE_UNARY_FUNCTIONAL(Cos, cos);
     259           1 : MAKE_UNARY_FUNCTIONAL(Cosh, cosh);
     260           1 : MAKE_UNARY_FUNCTIONAL(Erf, erf);
     261           1 : MAKE_UNARY_FUNCTIONAL(Exp, exp);
     262           1 : MAKE_UNARY_FUNCTIONAL(Exp2, exp2);
     263           1 : MAKE_UNARY_FUNCTIONAL(Fabs, fabs);
     264           1 : MAKE_UNARY_FUNCTIONAL(Imag, imag);
     265           1 : MAKE_UNARY_FUNCTIONAL(InvCbrt, invcbrt);
     266           1 : MAKE_UNARY_FUNCTIONAL(InvSqrt, invsqrt);
     267           1 : MAKE_UNARY_FUNCTIONAL(Log, log);
     268           1 : MAKE_UNARY_FUNCTIONAL(Log10, log10);
     269           1 : MAKE_UNARY_FUNCTIONAL(Log2, log2);
     270           1 : MAKE_UNARY_FUNCTIONAL(Real, real);
     271           1 : MAKE_UNARY_FUNCTIONAL(Sin, sin);
     272           1 : MAKE_UNARY_FUNCTIONAL(Sinh, sinh);
     273           1 : MAKE_UNARY_FUNCTIONAL(Sqrt, sqrt);
     274           1 : MAKE_UNARY_FUNCTIONAL(StepFunction, step_function);
     275           1 : MAKE_UNARY_FUNCTIONAL(Tan, tan);
     276           1 : MAKE_UNARY_FUNCTIONAL(Tanh, tanh);
     277           1 : MAKE_UNARY_FUNCTIONAL(Negate, -);
     278             : 
     279             : /// Function for computing an integer power, forwards to template pow<N>()
     280             : template <int N, typename C0 = Identity>
     281           1 : struct UnaryPow;
     282             : 
     283             : /// \cond
     284             : template <int N, typename C0>
     285             : struct UnaryPow : Functional<C0::arity> {
     286             :   template <class... Ts>
     287             :   constexpr auto operator()(const Ts&... ts) {
     288             :     return pow<N>(C0{}(ts...));
     289             :   }
     290             : };
     291             : 
     292             : template <int N>
     293             : struct UnaryPow<N, Identity> : Functional<1> {
     294             :   template <class T0>
     295             :   constexpr auto operator()(const T0& t0) {
     296             :     return pow<N>(t0);
     297             :   }
     298             : };
     299             : /// \endcond
     300             : 
     301             : /// Function for squaring a quantity
     302             : template <class C = Identity>
     303           1 : struct Square : Functional<C::arity> {
     304             :   template <class... Ts>
     305           0 :   constexpr auto operator()(const Ts&... ts) {
     306             :     decltype(auto) result = C{}(ts...);
     307             :     return result * result;
     308             :   }
     309             : };
     310             : 
     311             : /// Function that applies `C` to every element of the operands. This function is
     312             : /// currently only tested for `std::vector` operands. Operands other than the
     313             : /// first may be a single value, which is applied element-wise to the vector. If
     314             : /// needed, this function can be generalized further.
     315             : template <typename C>
     316           1 : struct ElementWise : Functional<C::arity> {
     317             :   template <typename T0, typename... Ts>
     318           0 :   auto operator()(const T0& t0, const Ts&... ts) {
     319             :     const size_t size = get_size(t0);
     320             :     ASSERT(((get_size(ts) == size or get_size(ts) == 1) and ...),
     321             :            "Sizes must be the same but got "
     322             :                << (std::vector<size_t>{size, get_size(ts)...}));
     323             :     T0 result(size);
     324             :     for (size_t i = 0; i < size; ++i) {
     325             :       get_element(result, i) = C{}(get_element(t0, i), get_element(ts, i)...);
     326             :     }
     327             :     return result;
     328             :   }
     329             : };
     330             : 
     331             : /// Function that merges two containers using the `merge` method of the first
     332             : /// container. Can be used to collect data in a `std::map` in a reduction.
     333             : template <typename C0 = Identity, typename C1 = C0>
     334           1 : struct Merge : Functional<2> {
     335             :   template <typename T>
     336           0 :   T operator()(const T& t0, const T& t1) {
     337             :     auto result = C0{}(t0);
     338             :     auto to_merge = C1{}(t1);
     339             :     result.merge(to_merge);
     340             :     return result;
     341             :   }
     342             : };
     343             : 
     344             : #undef MAKE_BINARY_FUNCTIONAL
     345             : #undef MAKE_BINARY_INPLACE_OPERATOR
     346             : #undef MAKE_BINARY_OPERATOR
     347             : #undef MAKE_UNARY_FUNCTIONAL
     348             : }  // namespace funcl

Generated by: LCOV version 1.14