Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_beta_math.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace uni20::detail::native_math
7{
8
9template <NativeReal R> R portable_beta(R a, R b)
10{
11 using L = numeric_limits<R>;
12 if (!(a > 0) || !(b > 0)) return L::quiet_NaN();
13 if (std::isinf(a) || std::isinf(b)) return R(0);
14 if (a > b) std::swap(a, b);
15 if (a == 1) return R(1) / b;
16 if (b == 1) return R(1) / a;
17 if (a < L::min())
18 {
19 // Here B(a,b) differs from 1/a+1/b by at most a modest logarithmic
20 // correction, far below one result spacing. Ordinary reciprocal addition
21 // can still overflow prematurely. Sum half-reciprocals with their FMA
22 // division residuals before restoring the scale.
23 R qa = R(.5) / a, qb = R(.5) / b, sum = qa + qb;
24 if (std::isinf(sum)) return L::infinity();
25 R low = qb - (sum - qa);
26 low += std::fma(-qa, a, R(.5)) / a + std::fma(-qb, b, R(.5)) / b;
27 return R(2) * (sum + low);
28 }
29 constexpr int bits = L::digits();
30 constexpr int cutoff = bits <= 24 ? 8 : bits <= 64 ? 16 : 32;
31 // Reduce modest arguments before Gamma evaluation: adding rounded arguments
32 // near the cutoff otherwise amplifies their lost low bits through Gamma.
33 if (b < cutoff)
34 {
35 R factor = 1;
36 while (b > 2)
37 {
38 b -= 1;
39 factor /= R(1) + a / b;
40 }
41 while (a > 2)
42 {
43 a -= 1;
44 factor /= R(1) + b / a;
45 }
46 R ratio = (std::tgamma(R(1) + a) / std::tgamma(R(1) + (a + b))) * std::tgamma(R(1) + b);
47 return (R(1) / a + R(1) / b) * (ratio * factor);
48 }
49 // Reciprocal of the sum avoids overflowing a+b.
50 R r = a / b, l = std::log1p(r), invb = R(1) / b, invsum = invb / (R(1) + r);
51 R logbeta;
52 if (a < cutoff)
53 {
54 R lgamma = std::log(std::tgamma(R(1) + a));
55 // a-b*log1p(a/b) is O(a*a/b); below epsilon its contribution is negligible.
56 R remainder = r <= L::epsilon() ? R(0) : std::fma(-b, l, a);
57 logbeta = std::fma(-a, std::log(b), lgamma) + remainder - (a - R(.5)) * l +
58 native_math::stirling_correction(invb) - native_math::stirling_correction(invsum);
59 // Keep 1/a out of the exponent near the final overflow boundary.
60 // Here b>=cutoff and a<1, so exp(logbeta) itself remains representable.
61 if (a < 1) return std::exp(logbeta) / a;
62 logbeta -= std::log(a);
63 }
64 else
65 {
66 // Entropy form: never subtract separately rounded, enormous log-Gamma values.
67 logbeta = a * std::log(r) - a * l - b * l + (std::log(R(2) * pi<R>) - std::log(a) + l) / 2 +
68 native_math::stirling_correction(R(1) / a) + native_math::stirling_correction(invb) -
69 native_math::stirling_correction(invsum);
70 }
71 return std::exp(logbeta);
72}
73
74} // namespace uni20::detail::native_math
Definition qnum.hpp:403
R ratio(DiagnosticMagnitude< R > const &a, DiagnosticMagnitude< R > const &b)
Definition gmres_diagnostics.hpp:125
constexpr detail::scalar_math::lgamma_fn lgamma
Logarithm of the absolute Gamma function; native providers are reentrant.
Definition math.hpp:569
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
mpreal fma(mpreal const &a, mpreal const &b, mpreal const &c)
Compute a*b+c with one rounding, including mixed exact rational operands.
Definition mpreal_functions.hpp:192
mpreal remainder(mpreal const &a, mpreal const &b)
Nearest-even-quotient remainder; exact operands retain exact rational results.
Definition mpreal_functions.hpp:534