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 : 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
|