Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_digamma_math.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "native_pi_math.hpp"
4
5namespace uni20::detail::native_math
6{
7template <NativeReal R> R positive_digamma(R x)
8{
9 constexpr int bits = numeric_limits<R>::digits();
10 constexpr int cutoff = bits <= 24 ? 8 : bits <= 64 ? 16 : 32;
11 constexpr int terms = bits <= 24 ? 4 : bits <= 53 ? 8 : bits <= 64 ? 10 : 16;
12 // B_(2k)/(2k), generated by the exact rational recurrence in
13 // scripts/generate-digamma-coefficients.py. All integer literals fit int64.
14 constexpr R coefficients[] = {R(1LL) / R(12LL),
15 R(-1LL) / R(120LL),
16 R(1LL) / R(252LL),
17 R(-1LL) / R(240LL),
18 R(1LL) / R(132LL),
19 R(-691LL) / R(32760LL),
20 R(1LL) / R(12LL),
21 R(-3617LL) / R(8160LL),
22 R(43867LL) / R(14364LL),
23 R(-174611LL) / R(6600LL),
24 R(77683LL) / R(276LL),
25 R(-236364091LL) / R(65520LL),
26 R(657931LL) / R(12LL),
27 R(-3392780147LL) / R(3480LL),
28 R(1723168255201LL) / R(85932LL),
29 R(-7709321041217LL) / R(16320LL)};
30 int shifts = x < R(cutoff) ? static_cast<int>(std::ceil(R(cutoff) - x)) : 0;
31 R y = x + R(shifts), inverse = R(1) / y, square = inverse * inverse;
32 R polynomial = coefficients[terms - 1];
33 for (int k = terms - 2; k >= 0; --k)
34 polynomial = std::fma(polynomial, square, coefficients[k]);
35
36 R sum = std::log(y), low = 0;
37 auto add = [&](R term) {
38 R next = sum + term;
39 low += std::abs(sum) >= std::abs(term) ? (sum - next) + term : (term - next) + sum;
40 sum = next;
41 };
42 add(-inverse / 2);
43 add(-square * polynomial);
44 // Form each shifted argument from the original x, without accumulating the
45 // rounding error of repeated x += 1. Retain cancellation residuals in low.
46 for (int k = 0; k < shifts; ++k)
47 add(-R(1) / (x + R(k)));
48 return sum + low;
49}
50
51template <NativeReal R> R digamma(R x)
52{
53 using L = numeric_limits<R>;
54 if (std::isnan(x) || x == -L::infinity()) return L::quiet_NaN();
55 if (x == L::infinity()) return x;
56 // The omitted term is O(x), negligible relative to the reciprocal here.
57 // This also preserves signed-zero poles and the final overflow boundary,
58 // without passing tiny x through a subnormal trigonometric denominator.
59 if (std::abs(x) <= L::epsilon()) return -R(1) / x - euler_gamma<R>;
60 if (x > 0) return native_math::positive_digamma(x);
61 if (x == std::trunc(x)) return L::quiet_NaN();
62 return native_math::positive_digamma(R(1) - x) - pi<R> / native_math::tanpi(x);
63}
64} // namespace uni20::detail::native_math
AsyncTask square(ReadBuffer< int > in, WriteBuffer< int > out)
Definition async_tbb_reduction_example.cpp:39
constexpr detail::scalar_math::digamma_fn digamma
Logarithmic derivative of Gamma.
Definition math.hpp:578
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
int add(int a, int b)
Definition dummy.cpp:6