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