Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_airy_math.hpp
Go to the documentation of this file.
1#pragma once
2
6
7namespace uni20::detail::native_math::airy_detail
8{
9template <NativeReal R> Expansion<R> constant(std::uint16_t const* a)
10{
11 Expansion<R> r;
12 constexpr int n = (4 * uni20::numeric_limits<R>::digits() + 16 + 15) / 16;
13 for (int k = 0; k < n; ++k)
14 r = r + Expansion<R>(std::ldexp(R(a[k]), -16 * (k + 1)));
15 return r;
16}
17template <NativeReal R> Expansion<R> wide_sqrt(R x)
18{
19 using E = Expansion<R>;
20 E s(std::sqrt(x));
21 for (int k = 0; k < 3; ++k)
22 s = s + (E(x) - s * s) / (E(2) * s);
23 return s;
24}
25template <NativeReal R> Expansion<R> wide_log(Expansion<R> x)
26{
27 using E = Expansion<R>;
29 int n;
30 R m = std::frexp(x.high(), &n);
31 if (m < R(.75)) --n;
32 x = x.scaled(-n);
33 E t = (x - E(1)) / (x + E(1)), square = t * t, term = t, sum = t;
34 R eps = std::ldexp(R(1), -4 * L::digits() + 8);
35 for (int k = 1; k < 4 * L::digits(); ++k)
36 {
37 term = term * square;
38 E v = term / R(2 * k + 1);
39 sum = sum + v;
40 if (std::abs(v.high()) < eps * std::abs(sum.high())) break;
41 }
42 return E(2) * sum + E(R(n)) * constant<R>(log_two_bits);
43}
44template <NativeReal R> R wide_exp(Expansion<R> x)
45{
46 using E = Expansion<R>;
48 E ln2 = constant<R>(log_two_bits);
49 int n = int(std::nearbyint(x.high() / ln2.high()));
50 E r = x - E(R(n)) * ln2, sum(1), term(1);
51 R eps = std::ldexp(R(1), -4 * L::digits() + 8);
52 for (int k = 1; k < 4 * L::digits(); ++k)
53 {
54 term = term * r / R(k);
55 sum = sum + term;
56 if (std::abs(term.high()) < eps) break;
57 }
58 if (n < L::min_exponent)
59 {
60 constexpr int d = L::min_exponent - L::digits();
61 E units = sum.scaled(n - d);
62 R q = std::floor(units.high());
63 E rem = units - E(q);
64 if (rem.high() < 0)
65 {
66 q -= 1;
67 rem = rem + E(1);
68 }
69 E half = rem - E(R(.5));
70 if (half.high() > 0 || (half.high() == 0 && std::fmod(q, R(2)) != 0)) q += 1;
71 return std::ldexp(q, d);
72 }
73 return std::ldexp(sum.rounded(), n);
74}
75// Recompute the exponent and asymptotic sum before rounding to a subnormal.
76// A rounded native exponential multiplied by the prefactor loses the last bit.
77template <NativeReal R> R guarded_positive(R x)
78{
79 using E = Expansion<R>;
81 E z = E(2) * E(x) * wide_sqrt(x) / R(3), inv = E(1) / z, term(1), sum(1);
82 R eps = std::ldexp(R(1), -4 * L::digits() + 8);
83 bool done = false;
84 for (int k = 1; k < 4 * L::digits(); ++k)
85 {
86 term = term * E(R((6 * k - 5) * (6 * k - 1))) * inv / R(72 * k);
87 sum = sum + (k % 2 ? -term : term);
88 if (term.high() < eps)
89 {
90 done = true;
91 break;
92 }
93 }
94 if (!done) return L::quiet_NaN();
95 E logpi = E(1) + constant<R>(log_pi_fraction_bits);
96 return wide_exp(-z - logpi / R(2) - wide_log(E(x)) / R(4) - constant<R>(log_two_bits) + wide_log(sum));
97}
98// Ai(0)*F(x^3) + Ai'(0)*x*G(x^3), with recurrence coefficients from
99// y''=x*y. Four words retain the cancellation at the precision-specific join.
100template <NativeReal R> R central(R x)
101{
102 using L = uni20::numeric_limits<R>;
103 using E = Expansion<R>;
104 E z = E(x) * E(x) * E(x), f(1), g(1), tf(1), tg(1);
105 R eps = std::ldexp(R(1), -4 * L::digits() + 8);
106 for (int k = 1; k < 4 * L::digits() + 64; ++k)
107 {
108 tf = tf * z / R((3 * k) * (3 * k - 1));
109 tg = tg * z / R((3 * k) * (3 * k + 1));
110 f = f + tf;
111 g = g + tg;
112 if (std::abs(tf.high()) < eps * std::abs(f.high()) && std::abs(tg.high()) < eps * std::abs(g.high()))
113 return (constant<R>(ai_zero_bits) * f - constant<R>(ai_prime_zero_bits) * E(x) * g).rounded();
114 }
115 return L::quiet_NaN();
116}
117// Real-variable remainders are bounded by the first omitted term in each
118// series. The chosen joins reach the stopping target before terms grow.
119template <NativeReal R> R asymptotic(R x)
120{
121 using L = uni20::numeric_limits<R>;
122 bool positive = x > 0;
123 R v = std::abs(x), inverse = (R(1.5) / v) / std::sqrt(v), term = 1, even = 1, odd = 0, previous = 1;
124 bool done = false;
125 for (int k = 1; k < 4 * L::digits() + 64; ++k)
126 {
127 term *= R((6 * k - 5) * (6 * k - 1)) / R(72 * k) * inverse;
128 if (term > previous) return L::quiet_NaN();
129 if (positive)
130 even += k % 2 ? -term : term;
131 else if (k % 2)
132 odd += (k / 2) % 2 ? -term : term;
133 else
134 even += (k / 2) % 2 ? -term : term;
135 if (term < L::epsilon() / 32)
136 {
137 done = true;
138 break;
139 }
140 previous = term;
141 }
142 if (!done) return L::quiet_NaN();
143 R amplitude = 1 / (std::sqrt(uni20::pi<R>) * std::sqrt(std::sqrt(v)));
144 if (positive)
145 {
146 R zeta = (R(2) / 3) * v * std::sqrt(v);
147 R estimate = -zeta + std::log(amplitude * (even / 2));
148 if (std::abs(estimate - std::log(L::denorm_min())) < 8) return guarded_positive(v);
149 R half = std::exp(-zeta / 2);
150 return half * (half * (amplitude * (even / 2)));
151 }
152 auto [s, c] = phase(v);
153 return amplitude * std::fma(s, even, -c * odd);
154}
155template <NativeReal R> R airy(R x)
156{
157 using L = uni20::numeric_limits<R>;
158 if (std::isnan(x)) return x;
159 if (std::isinf(x)) return 0;
160 constexpr int join = L::digits() <= 24 ? 8 : L::digits() <= 53 ? 12 : L::digits() <= 64 ? 14 : 20;
161 if (std::abs(x) <= join) return central(x);
162 if (x > R(2 * (L::digits() - L::min_exponent))) return 0;
163 return asymptotic(x);
164}
165} // namespace uni20::detail::native_math::airy_detail
166
167namespace uni20::detail::native_math
168{
171template <NativeReal R> R airy_ai(R x) { return airy_detail::airy(x); }
172} // namespace uni20::detail::native_math
AsyncTask square(ReadBuffer< int > in, WriteBuffer< int > out)
Definition async_tbb_reduction_example.cpp:39
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
constexpr detail::scalar_math::airy_ai_fn airy_ai
Real Airy Ai; native inputs retain their precision and full negative-axis phase.
Definition math.hpp:620
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
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16