Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
native_agm_math.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "native_math.hpp"
4#include <bit>
5
6namespace uni20::detail::native_math
7{
8template <NativeReal R> R agm(R a, R b)
9{
10 using L = numeric_limits<R>;
11 if (std::isnan(a) || std::isnan(b)) return L::quiet_NaN();
12 if (a == 0 || b == 0) return std::isfinite(a) && std::isfinite(b) ? R(0) : L::quiet_NaN();
13 if (a < 0 || b < 0) return L::quiet_NaN();
14 if (a == b) return a;
15 if (std::isinf(a) || std::isinf(b)) return L::infinity();
16 if (a < b) std::swap(a, b);
17
18 int ae, be;
19 a = std::frexp(a, &ae);
20 b = std::frexp(b, &be);
21 while (ae - be > L::digits() + 2)
22 {
23 // Keep separate scales until neither input can be erased by a common
24 // scale. Here the smaller term changes the arithmetic mean by <u/4,
25 // where u=2^-digits. The larger significand is unchanged when halved.
26 int sum = ae + be;
27 int odd = (sum % 2 + 2) % 2;
28 int exponent = (sum - odd) / 2;
29 R geometric = std::sqrt(a * b * R(odd ? 2 : 1));
30 int shift;
31 b = std::frexp(geometric, &shift);
32 be = exponent + shift;
33 --ae;
34 }
35
36 // Both scaled values and their product are normal for every supported
37 // native format. Restore the original exponent only at the final rounding.
38 b = std::ldexp(b, be - ae);
39 constexpr int limit = 2 * std::bit_width(static_cast<unsigned>(L::digits())) + 8;
40 for (int iteration = 0; iteration < limit; ++iteration)
41 {
42 R arithmetic = (a + b) / 2;
43 if (std::abs(a - b) <= std::ldexp(a, 1 - L::digits())) return std::ldexp(arithmetic, ae);
44 b = std::sqrt(a * b);
45 a = arithmetic;
46 }
47 // Quadratic convergence should reach the spacing test well before this
48 // conservative bound. Never return a silently unfinished approximation.
49 return L::quiet_NaN();
50}
51} // namespace uni20::detail::native_math
constexpr detail::scalar_math::agm_fn agm
Nonnegative arithmetic-geometric mean.
Definition math.hpp:625
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