7namespace uni20::detail::native_math::upper_gamma_detail
9template <NativeReal R>
struct compensated_log
15 low += std::abs(sum) >= std::abs(v) ? (
sum - n) + v : (v - n) +
sum;
22 if (std::isfinite(v)) this->
add(std::fma(a, b, -v));
24 R
value()
const {
return sum + low; }
27 if (std::isinf(sum))
return sum > 0 ?
sum : R(0);
29 R e = std::abs(sum) >= std::abs(low) ? (
sum - v) + low : (low - v) +
sum;
30 R h = std::exp(v / 2);
31 if (h == 0 || std::isinf(h))
return h;
32 return h * (h * std::exp(e));
35template <NativeReal R> R log1p_ratio(R x)
37 return std::abs(x) < numeric_limits<R>::epsilon() / 2 ? R(1) :
std::
log1p(x) / x;
39template <NativeReal R> R expm1_ratio(R x)
41 return std::abs(x) < numeric_limits<R>::epsilon() / 2 ? R(1) :
std::
expm1(x) / x;
45template <NativeReal R> R gamma1p_log_quotient(R a)
47 if (a == 0)
return -euler_gamma<R>;
48 constexpr int p = numeric_limits<R>::digits(), N =
p <= 24 ? 16 :
p <= 64 ? 32 : 64;
49 constexpr int terms =
p <= 24 ? 4 :
p <= 53 ? 8 :
p <= 64 ? 10 : 16;
50 constexpr R
c[] = {R(1) / 12,
61 R(-236364091LL) / 1506960,
63 R(-3392780147LL) / 93960,
64 R(1723168255201LL) / 2492028,
65 R(-7709321041217LL) / 505920};
66 R r = a / N, l = std::log1p(r), q = log1p_ratio(r), power = R(1) / N;
68 s.add(std::log(R(N)));
69 s.add(std::fma((a - R(.5)) / N, q, q - 1));
70 for (
int k = 1; k < N; ++k)
71 s.add(-log1p_ratio(a / R(k)) / R(k));
72 for (
int k = 0; k < terms; ++k)
75 s.add(
c[k] * power * (-m / R(N)) * q * expm1_ratio(-m * l));
80template <NativeReal R> R small_scaled(R a, R x)
82 using L = numeric_limits<R>;
83 R d = gamma1p_log_quotient(a) - std::log(x);
85 s.add(d * expm1_ratio(a * d));
87 for (
int k = 1; k <= 2 * L::digits() + 32; ++k)
90 R term = power / (a + R(k));
92 if (std::abs(term) < L::epsilon() * std::abs(s.sum) / 16)
return std::exp(x) * s.value();
94 return L::quiet_NaN();
98template <NativeReal R> R positive_fraction(R a, R x)
100 using L = numeric_limits<R>;
101 for (
int depth = 16; depth <= 64 * L::digits(); depth *= 2)
103 R lo = (R(depth) - a) / (1 + R(depth) / x), hi = R(depth) - a;
104 for (
int k = depth - 1; k >= 1; --k)
106 lo = (R(k) - a) / (1 + R(k) / (x + lo));
107 hi = (R(k) - a) / (1 + R(k) / (x + hi));
111 if (std::abs(hi - lo) <= 4 * L::epsilon() * lo)
return lo + (hi - lo) / 2;
113 return L::quiet_NaN();
115template <NativeReal R> R tail_fraction(R a, R x)
117 using L = numeric_limits<R>;
119 for (
int depth = 8; depth <= 64 * L::digits(); depth *= 2)
122 for (
int k = depth; k >= 1; --k)
123 value = (a - R(k)) * (R(k) / (x - a + R(2 * k + 1) +
value));
125 if (std::abs(
value - previous) <= 2 * L::epsilon() *
value)
return value;
128 return L::quiet_NaN();
130template <NativeReal R> compensated_log<R>
lgamma(R a)
132 constexpr int cutoff = numeric_limits<R>::digits() <= 24 ? 8 : numeric_limits<R>::digits() <= 64 ? 16 : 32;
133 compensated_log<R> s;
136 s.add(std::log(std::tgamma(a)));
143 s.add(std::log(2 * pi<R>) / 2);
144 s.add(detail::native_math::stirling_correction(R(1) / a));
147template <NativeReal R> R scaled_result(R a, R x, R u)
149 if (std::isnan(u))
return u;
150 using L = numeric_limits<R>;
151 R
estimate = std::fma(a, std::log(x), -x) + std::log(u);
152 if (L::epsilon() * std::abs(a) * std::abs(std::log(x)) + L::epsilon() * x > R(1) / 32 ||
153 std::abs(estimate - std::log(L::max())) < 4 || std::abs(estimate - std::log(L::denorm_min())) < 4)
154 return upper_gamma_guard::upper_gamma(a, x);
157 R half = std::pow(x, a / 2) * std::exp(-x / 2);
158 return half * (half * u);
160 compensated_log<R> s;
162 if (std::isfinite(a * lx))
169 s.add(std::fma(a, lx, -x));
171 if (std::isinf(s.sum))
return s.sum > 0 ? s.sum : R(0);
175template <NativeReal R> R evaluate(R a, R x)
177 using L = numeric_limits<R>;
178 if (std::isnan(a) || std::isnan(x))
return L::quiet_NaN();
179 if (a == L::infinity())
return std::isfinite(x) ? a : L::quiet_NaN();
180 if (a == -L::infinity())
return x > 0 ? (x < 1 ? L::infinity() : R(0)) : L::quiet_NaN();
181 if (x < 0)
return L::quiet_NaN();
182 if (x == 0)
return std::tgamma(a);
183 if (x == L::infinity())
return R(0);
184 if (a <= 1 && x > R(2 * L::max_exponent))
return R(0);
187 if (a > R(2 * L::max_exponent) && x / 2 <= a)
return L::infinity();
188 if (a == 1)
return std::exp(-x);
189 if (a >= R(.5) && x <= a)
192 compensated_log<R> lb;
193 lb.product(a, std::log(a));
194 lb.add(-std::log(a));
197 if (a >= 1 && lb.value() > std::log(L::max()) + 1)
return L::infinity();
198 compensated_log<R> s;
201 for (
int k = 1; k <= 64 * L::digits(); ++k)
203 term *= x / (a + R(k));
205 if (term < L::epsilon() * s.sum / 16)
207 compensated_log<R> l;
208 l.product(a, std::log(x));
213 R lower = l.exp() * s.value();
214 g.add(std::log1p(-lower));
215 if (std::abs(g.value() - std::log(L::max())) < 4)
return upper_gamma_guard::lower_complement(a, x);
219 return L::quiet_NaN();
221 constexpr int cutoff = L::digits() <= 24 ? 8 : L::digits() <= 64 ? 16 : 32;
222 if (x <= 1 && a > -R(cutoff) && a < R(.5))
224 int count = int(std::ceil(-a - R(.5)));
225 if (count < 0)
count = 0;
227 R u = small_scaled(b, x);
228 for (
int k = 1; k <=
count; ++k)
229 u = (std::fma(x, u, -R(1))) / (b - R(k));
230 return scaled_result(a, x, u);
232 R u = a <= 0 && x >= 1 ? positive_fraction(a, x) : tail_fraction(a, x);
233 return scaled_result(a, x, u);
237namespace uni20::detail::native_math
243template <NativeReal R> R
upper_gamma(R a, R x) {
return upper_gamma_detail::evaluate(a, x); }
locale_t value
Definition data_table.cpp:49
T product(std::vector< T > const &values)
Definition mpreal_exact_example.cpp:13
constexpr detail::scalar_math::exp_fn exp
Natural exponential with scalar-specific precision handling.
Definition math.hpp:465
constexpr detail::scalar_math::upper_gamma_fn upper_gamma
Upper unregularized incomplete Gamma, with shape and lower integration endpoint.
Definition math.hpp:593
constexpr detail::scalar_math::lgamma_fn lgamma
Logarithm of the absolute Gamma function; native providers are reentrant.
Definition math.hpp:569
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
mpreal expm1(mpreal const &x)
Exponential minus one without cancellation; retain input precision or exact zero.
Definition mpreal.hpp:722
int add(int a, int b)
Definition dummy.cpp:6
mpreal log1p(mpreal const &x)
Logarithm of one plus x without cancellation; retain input precision or exact zero.
Definition mpreal.hpp:743