Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_zeta_math.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "native_pi_math.hpp"
5
6namespace uni20::detail::native_math
7{
8template <NativeReal R> R zeta_coefficient(int k)
9{
10 // B_(2k)/(2k)!, generated from exact rationals. Binary chunks preserve
11 // native precision without first rounding a coefficient to long double.
12 constexpr R coefficients[] = {
13 (R(0xaaaaaaaaaaaaaaaaULL) * R(0x1p-63L) + R(0xaaaaaaaaaaaaaaaaULL) * R(0x1p-127L)) * R(0x1p-4L),
14 -(R(0xb60b60b60b60b60bULL) * R(0x1p-63L) + R(0x60b60b60b60b60b6ULL) * R(0x1p-127L)) * R(0x1p-10L),
15 (R(0x8ab355e008ab355eULL) * R(0x1p-63L) + R(0x008ab355e008ab35ULL) * R(0x1p-127L)) * R(0x1p-15L),
16 -(R(0xddebbc99a7785563ULL) * R(0x1p-63L) + R(0x34111eefccdaab88ULL) * R(0x1p-127L)) * R(0x1p-21L),
17 (R(0xb354795fb875ed14ULL) * R(0x1p-63L) + R(0xb089f4cc1f12f738ULL) * R(0x1p-127L)) * R(0x1p-26L),
18 -(R(0x91402eb22133f7baULL) * R(0x1p-63L) + R(0x56337ff27cb7da47ULL) * R(0x1p-127L)) * R(0x1p-31L),
19 (R(0xeb6d9627048b0fdcULL) * R(0x1p-63L) + R(0x257768c274f0337cULL) * R(0x1p-127L)) * R(0x1p-37L),
20 -(R(0xbed270fbccaae25fULL) * R(0x1p-63L) + R(0xf129a06f42d0dc42ULL) * R(0x1p-127L)) * R(0x1p-42L),
21 (R(0x9aac38eb2974eceeULL) * R(0x1p-63L) + R(0x56665d7d7bc1dbfaULL) * R(0x1p-127L)) * R(0x1p-47L),
22 -(R(0xfabecb4655678663ULL) * R(0x1p-63L) + R(0xce0f50adaa0b8212ULL) * R(0x1p-127L)) * R(0x1p-53L),
23 (R(0xcb3f0f84e1bb77d8ULL) * R(0x1p-63L) + R(0x2b4adb048eaa5e7bULL) * R(0x1p-127L)) * R(0x1p-58L),
24 -(R(0xa4bec819d15ae370ULL) * R(0x1p-63L) + R(0x87e6555a6c876af6ULL) * R(0x1p-127L)) * R(0x1p-63L),
25 (R(0x858996be35683203ULL) * R(0x1p-63L) + R(0xa8a4d91cebb6a6c7ULL) * R(0x1p-127L)) * R(0x1p-68L),
26 -(R(0xd87b96acf8e0b3e6ULL) * R(0x1p-63L) + R(0x16e913f6cf2056caULL) * R(0x1p-127L)) * R(0x1p-74L),
27 (R(0xaf796d2665136ad7ULL) * R(0x1p-63L) + R(0x3275bfba8ce6d0edULL) * R(0x1p-127L)) * R(0x1p-79L),
28 -(R(0x8e3befcb6f1c57e2ULL) * R(0x1p-63L) + R(0x53a622f2cc53f6c7ULL) * R(0x1p-127L)) * R(0x1p-84L),
29 };
30 return coefficients[k];
31}
32
33// Euler-Maclaurin tail for s>=1/2, using the integral-remainder bounds
34// generated in scripts/generate-zeta-coefficients.py.
35template <NativeReal R> R positive_zeta(R s)
36{
37 using L = numeric_limits<R>;
38 constexpr int p = L::digits(), N = p <= 24 ? 8 : p <= 64 ? 16 : 32;
39 constexpr int M = p <= 24 ? 4 : p <= 53 ? 8 : p <= 64 ? 10 : 16;
40 if (s == 1) return L::infinity();
41 if (s >= R(p + 1)) return R(1);
42 R sum = 0, low = 0;
43 auto add = [&](R v) {
44 R next = sum + v;
45 low += std::abs(sum) >= std::abs(v) ? (sum - next) + v : (v - next) + sum;
46 sum = next;
47 };
48 R logn = std::log(R(N));
49 for (int n = 1; n < N; ++n)
50 add(std::pow(R(n), -s));
51 add(std::exp((R(1) - s) * logn) / (s - R(1)));
52 add(std::exp(-s * logn) / 2);
53 R power = std::exp((-s - R(1)) * logn) * s;
54 for (int k = 1; k <= M; ++k)
55 {
56 add(native_math::zeta_coefficient<R>(k - 1) * power);
57 power *= (s + R(2 * k - 1)) / R(N);
58 power *= (s + R(2 * k)) / R(N);
59 }
60 return sum + low;
61}
62// Compute delta*zeta(1+delta) without constructing its pole. The exact
63// delta enters the singular tail even if 1+delta rounds to one.
64template <NativeReal R> R zeta_residue(R delta)
65{
66 using L = numeric_limits<R>;
67 constexpr int p = L::digits(), N = p <= 24 ? 8 : p <= 64 ? 16 : 32;
68 constexpr int M = p <= 24 ? 4 : p <= 53 ? 8 : p <= 64 ? 10 : 16;
69 R sum = 0, low = 0;
70 auto add = [&](R v) {
71 R next = sum + v;
72 low += std::abs(sum) >= std::abs(v) ? (sum - next) + v : (v - next) + sum;
73 sum = next;
74 };
75 for (int n = 1; n < N; ++n)
76 add(std::exp(-delta * std::log(R(n))) / R(n));
77 R tail = std::exp(-delta * std::log(R(N)));
78 add(tail / (2 * R(N)));
79 R power = tail * (R(1) + delta) / (R(N) * R(N));
80 for (int k = 1; k <= M; ++k)
81 {
82 add(native_math::zeta_coefficient<R>(k - 1) * power);
83 power *= (delta + R(2 * k)) / R(N);
84 power *= (delta + R(2 * k + 1)) / R(N);
85 }
86 return std::fma(delta, sum, tail) + delta * low;
87}
88template <NativeReal R> R zeta(R s)
89{
90 using L = numeric_limits<R>;
91 if (std::isnan(s) || s == -L::infinity()) return L::quiet_NaN();
92 if (s == L::infinity()) return R(1);
93 if (s == 0) return -R(.5);
94 if (std::abs(s) < L::epsilon() / 2) return -R(.5) - s * std::log(R(2) * pi<R>) / 2;
95 if (s >= R(.5)) return native_math::positive_zeta(s);
96 if (std::abs(s) < R(.5))
97 {
98 R t = -s;
99 return native_math::sinpi(s / 2) / t * (std::tgamma(R(1) + t) / pi<R>) * std::exp(-t * std::log(R(2) * pi<R>)) *
100 native_math::zeta_residue(t);
101 }
102 // Exact phase reduction precedes any amplitude that might overflow.
103 R phase = native_math::sinpi(s / 2);
104 if (phase == 0) return R(0);
105 if (s < -R(2 * L::max_exponent)) return std::copysign(L::infinity(), phase);
106 R t = -s, c = std::log(R(2) * pi<R>);
107 constexpr int cutoff = L::digits() <= 24 ? 8 : L::digits() <= 64 ? 16 : 32;
108 if (t < cutoff)
109 return (std::tgamma(t) * std::pow(R(2) * pi<R>, -t)) * (t / pi<R>) * native_math::positive_zeta(R(1) + t) * phase;
110 else
111 {
112 R sum = 0, low = 0;
113 auto add = [&](R v) {
114 R next = sum + v;
115 low += std::abs(sum) >= std::abs(v) ? (sum - next) + v : (v - next) + sum;
116 sum = next;
117 };
118 R lt = std::log(t), v = t * lt;
119 add(v);
120 add(std::fma(t, lt, -v));
121 v = -t * c;
122 add(v);
123 add(std::fma(-t, c, -v));
124 add(-t);
125 add(lt / 2);
126 add(-c / 2);
127 add(log_two<R>);
128 add(native_math::stirling_correction(R(1) / t));
129 add(std::log(native_math::positive_zeta(R(1) + t)));
130 // Keep both the small sine factor and the logarithm residual outside
131 // the rounded exponent. Logging the sine loses accuracy near zeros.
132 R half = std::exp(sum / 2);
133 return half * (half * (phase * std::exp(low)));
134 }
135}
136} // namespace uni20::detail::native_math
Definition cli.cpp:6
constexpr detail::scalar_math::zeta_fn zeta
Real Riemann zeta with native Euler-Maclaurin evaluation and reflection.
Definition math.hpp:601
Definition data_table.cpp:8
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