SpECTRE Documentation Coverage Report
Current view: top level - Utilities - Functional.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 52.9 % 70 37
Test Date: 2026-10-22 17:29:42
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"
      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            0 : using std::max;
      48            0 : 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 2.0-1