1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/******************************************************************************
* This file is part of libome *
* Copyright (C) 2025 Arnd Behring, Kay Schoenwald *
* SPDX-License-Identifier: GPL-3.0-or-later *
******************************************************************************/
/**
* \file
* \brief Function wrappers that modify arguments passed to a wrapped callable
*/
#ifndef LIBOME_FUNCTIONS_H
#define LIBOME_FUNCTIONS_H
#include <type_traits>
#include <functional>
#include <algorithm>
#include <numeric>
#include <limits>
#include <cmath>
#include <cassert>
namespace ome
{
/// Single parameter identity function
template<typename Tnum>
Tnum f_id(Tnum x) { return(x); }
/// Single parameter function: 1/2-x
template<typename Tnum>
Tnum f_half(Tnum x) { return(static_cast<Tnum>(1)/static_cast<Tnum>(2) - x); }
/// Single parameter function: 1-x
template<typename Tnum>
Tnum f_omx(Tnum x) { return(static_cast<Tnum>(1)-x); }
/// Single parameter function: (a+b*x)
template<typename Tnum, int a, int b>
Tnum f_linear(Tnum x)
{
return(static_cast<Tnum>(a) + static_cast<Tnum>(b)*x);
}
/// Single parameter function: (a+b*x)/(c+d*x)
template<typename Tnum, int a, int b, int c, int d>
Tnum f_moebius(Tnum x)
{
return((static_cast<Tnum>(a) + static_cast<Tnum>(b)*x) /
(static_cast<Tnum>(c) + static_cast<Tnum>(d)*x));
}
/**
* \brief Wrapper for callable that shifts the first argument by a fixed value
*
* \details
* Takes a shift and callable at construction and acts as a wrapper which,
* upon evaluation, shifts the first argument by the shift.
*
* \tparam Tnum Numerical type for the shift, the first argument and return
* type of the evaluation operator.
* \tparam Tfunc Callable type to wrap
* \tparam Trest Type parameter pack for the remainder of the arguments
*/
template<typename Tnum, typename Tfunc, typename... Trest>
class func_shift
{
public:
/// Type alias for the numerical type template parameter
using numeric_type = Tnum;
/// Type alias for the callable type template parameter
using element_type = Tfunc;
/**
* \brief Default constructor
*
* \details
* Calls the default constructor on the target function class and
* initialises shift to zero (i.e. the func_shift class acts as an
* identity).
*/
func_shift()
: target_function_(element_type()),
shift_(static_cast<numeric_type>(0)) {};
/**
* \brief Construct wrapper with given shift and callable
*
* \param shift Value of the shift
* \param target_function Callable object to wrap
*/
func_shift(numeric_type shift, element_type target_function)
: target_function_(target_function), shift_(shift) {};
/**
* \brief Evaluate the wrapped callable with shifted first argument
*
* \param x Argument that gets shifted
* \param rest Function parameter pack that is passed on to the wrapped
* callable unchanged
*
* \return The value returned by the wrapped callable
*/
numeric_type operator()(numeric_type x, Trest... rest) const
{
return(target_function_(x+shift_, rest...));
};
private:
element_type target_function_;
numeric_type shift_;
};
/**
* \brief Wrapper for callable that applies a function to the first argument
*
* \details
* Wraps a callable and applies a function to the first argument upon
* evaluation. The callalbe and function are specified at the time of
* construction.
*
* \tparam Tnum Numerical type for the first argument and return type
* of the evaluation operator
* \tparam Tfunc Callable type to wrap
* \tparam Trest Pack of types for the remaining arguments of the wrapped
* callable
*/
template<typename Tnum, typename Tfunc, typename... Trest>
class func_apply
{
public:
/// Type alias for the numerical type template parameter
using numeric_type = Tnum;
/// Type alias for the callable type template parameter
using element_type = Tfunc;
/**
* \brief Default constructor
*
* \details
* Calls default constructor on the target function and initialises the
* apply function to f_id<numeric_type> (that is func_apply behaves as
* the identity).
*/
func_apply()
: target_function_(element_type()),
apply_function_(f_id<numeric_type>) {};
/**
* \brief Construct wrapper with given function and callable
*
* \param apply_function Function to apply to the first agument
* \param target_function Callable object to wrap
*/
func_apply(std::function<numeric_type(numeric_type)> apply_function,
element_type target_function)
: target_function_(target_function),
apply_function_(apply_function) {};
/**
* \brief Evaluate the wrapped callable with the first argument fed
* through the specified function
*
* \param x Argument that gets modified by the function
* \param rest Function parameter pack that is passed on to the wrapped
* callable unchanged
*
* \return The value returned by the wrapped callable
*/
numeric_type operator()(numeric_type x, Trest... rest) const
{
return(target_function_(apply_function_(x), rest...));
};
private:
element_type target_function_;
std::function<numeric_type(numeric_type)> apply_function_;
};
/**
* \brief Wrapper for callable that copies the first argument and applies
* the logarithm to the first copy
*
* \details
* Wraps a callable that takes one more argument than this wrapper. Upon
* evaluation this wrapper passes the first argument of the wrapper to
* the first two arguments of the wrapped callable. Moreover, it applies
* the logarithm to the first passed argument. I.e. if f wraps g and f is
* called as f(x,...) it calls g(log(x),x,...).
*
* \tparam Tnum Numerical type for the first argument and the return type of
* the wrapped callable
* \tparam Tfunc Callable type to wrap
* \tparam Trest Pack of types for the remaining arguments of the wrapped
* callable
*/
template<typename Tnum, typename Tfunc, typename... Trest>
class func_copy_and_log
{
public:
/// Type alias for the numerical type template parameter
using numeric_type = Tnum;
/// Type alias for the callable type template parameter
using element_type = Tfunc;
/**
* \brief Default constructor
*
* \details
* Calls the default constructor on the target function.
*/
func_copy_and_log()
: target_function_(element_type()) {};
/**
* \brief Construct wrapper from callable
*
* \param target_function Callable object to wrap
*/
explicit
func_copy_and_log(element_type target_function)
: target_function_(target_function) {};
/**
* \brief Evaluate the wrapped callable object with the first argument
* copied and with log() applied to the first copy
*
* \param x Argument to copy
* \param rest Function parameter pack that is passed on to the wrapped
* callable unchanged
*
* \return The value returned by the wrapped callable
*/
numeric_type operator()(numeric_type x, Trest... rest) const
{
using std::log;
return(target_function_(x > static_cast<numeric_type>(0) ? log(x)
: std::numeric_limits<numeric_type>::quiet_NaN(), x, rest...));
};
private:
element_type target_function_;
};
/**
* \brief Wrapper for callable that emulates \f$1-x\f$ plus function kernels
*
* \details
* Upon evaluation this wrapper takes the first argument \f$x\f$, takes the
* logarithm \f$\log(1-x)\f$ of it and passes it on to the wrapped callable.
* The result from the callable is then divided by \f$1-x\f$. If \f$f\f$
* wraps \f$g\f$, evaluating \f$f\f$ corresponds to
* \f[
* f(x,\dots) = \frac{g(\log(1-x),\dots)}{1-x}
* \f]
* The idea is that by wrapping a laurent_polynomial with this wrapper, it is
* straightforward to construct a sum of plus function kernels
* \f[
* p(x,\dots) = \sum_k \frac{\log^k(1-x)}{1-x} c_i(\dots)
* \f]
*
* \tparam Tnum Numerical type for the first argument and the return type of
* the callable
* \tparam Tfunc Callable type to wrap
* \tparam Trest Pack of types for the remaining arguments of the wrapped
* callable
*/
template<typename Tnum, typename Tfunc, typename... Trest>
class func_plusfunc_omx
{
public:
/// Type alias for the numerical type template parameter
using numeric_type = Tnum;
/// Type alias for the callable type template parameter
using element_type = Tfunc;
/// Boolean type alias indicating that this class has an eval_plus_int method
using has_eval_plus_int = std::true_type;
/**
* \brief Default constructor
*
* \details
* Calls the default constructor on the target function class.
*/
func_plusfunc_omx()
: target_function_(element_type()) {};
/**
* \brief Construct wrapper from callable
*
* \param target_function Callable object to wrap
*/
explicit
func_plusfunc_omx(element_type target_function)
: target_function_(target_function) {};
/**
* \brief Evaluate the wrapped callable with the first argument x passed
* through the logarithm and the result divided by x
*
* \param x Argument to be passed through the logarithm before being
* passed to the wrapped callable
* \param rest Function parameter pack that is passed on to the wrapped
* callable unchanged
*
* \return The value returned by the wrapped callable, divided by x
*/
numeric_type operator()(numeric_type x, Trest... rest) const
{
using std::log;
return(target_function_(log(static_cast<numeric_type>(1)-x), rest...)
/ (static_cast<numeric_type>(1)-x));
};
/**
* \brief Evaluate the integral over the plus function
*
* \details
* For Mellin convolutions, we need an extra term which corresponds to
* \f[
* I(x) = \int_0^x \mathrm{d}y g(y,\dots)
* \f]
* Since this wrapper is supposed to model \f$1-x\f$ plus function kernels
* and the coeffients \f$c_i(\dots)\f$ do not depend on \f$x\f$, we can
* analytically calculate the integral and just evaluate the result:
* \f[
* I(x) = \sum_k c_i(\dots) \int_0^x \mathrm{d}y \frac{\log^k(1-y)}{1-y}
* = \sum_k c_i(\dots) \frac{-\log^{k+1}(1-x)}{k+1}
* \f]
* The implementation is only valid if the wrapped polynomial has no
* negative powers.
*
* \param x Upper integration bound \f$x\f$
* \param rest Function parameter pack that is passed on to the wrapped
* callable unchanged
*
* \return The value of the integral
*/
numeric_type eval_plus_int(numeric_type x, Trest... rest) const
{
using std::log;
using std::pow;
int min_power = target_function_.min_power();
assert((min_power >= 0, "Only non-negative exponents are supported"));
numeric_type log_omx = log(static_cast<numeric_type>(1)-x);
// Calculate the integral over the plus function
size_t num_coeffs = (target_function_.max_power()+1) - min_power;
std::vector<numeric_type> plusfunc_ints(num_coeffs,
static_cast<numeric_type>(0));
// Compute the exponents that appear in the integral over the plus
// functions
std::iota(
plusfunc_ints.begin(),
plusfunc_ints.end(),
static_cast<numeric_type>(min_power+1)
);
// Evaluate the integrals over the plus functions
std::transform(
plusfunc_ints.begin(),
plusfunc_ints.end(),
plusfunc_ints.begin(),
[log_omx](numeric_type e) { return(-pow(log_omx,e)/e); }
);
// Combine the integrals over the individual plus functions with the
// coefficients
return(target_function_.eval_subst(plusfunc_ints));
};
private:
element_type target_function_;
};
}
#endif