Automatic Differentiation
 
Loading...
Searching...
No Matches
erfcx.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_FUN_ERFCX_HPP
2#define STAN_MATH_PRIM_FUN_ERFCX_HPP
3
7#include <array>
8#include <cmath>
9#include <cstddef>
10#include <utility>
11
12namespace stan {
13namespace math {
14
15namespace internal {
16
17// Cody (1969) third interval, ascending powers of `u = 1 / x^2`.
18inline constexpr std::array<double, 6> erfcx_tail_p{
19 0.000658749161529837803157, 0.0160837851487422766278,
20 0.125781726111229246204, 0.360344899949804439429,
21 0.305326634961232344035, 0.0163153871373020978498};
22inline constexpr std::array<double, 6> erfcx_tail_q{
23 -0.00233520497626869185443, -0.0605183413124413191178,
24 -0.527905102951428412248, -1.87295284992346047209,
25 -2.56852019228982242072, -1.0};
26
27// above this the correction is below eps / 8 and x^12 overflows
28inline constexpr double erfcx_tail_leading_only = 0x1p27;
29
30// Cody (1969) second interval, ascending powers of `y`.
31inline constexpr std::array<double, 9> erfcx_middle_p{
32 1230.33935479799725, 2051.07837782607147, 1712.04761263407058,
33 881.952221241769090, 298.635138197400131, 66.1191906371416295,
34 8.88314979438837594, 5.64188496988670089e-1, 2.15311535474403846e-8};
35inline constexpr std::array<double, 9> erfcx_middle_q{
36 1230.33935480374942, 3439.36767414372164, 4362.61909014324716,
37 3290.79923573345963, 1621.38957456669019, 537.181101862009858,
38 117.693950891312499, 15.7449261107098347, 1.0};
39
52template <std::size_t N, typename T, typename It>
53inline std::pair<T, T> erfcx_tail_polynomials(It p, It q, const T& t) {
54 T numerator = *p;
55 T denominator = *q;
56 for (std::size_t i = 1; i < N; ++i) {
57 numerator = numerator * t + *++p;
58 denominator = denominator * t + *++q;
59 }
60 return {numerator, denominator};
61}
62
74template <typename T, std::size_t N>
75inline T erfcx_paired_horner(const std::array<double, N>& c, const T& y,
76 const T& y2) {
77 int j = static_cast<int>(N) - (N % 2 ? 3 : 4);
78 T r = N % 2 ? T(c[N - 1]) : T(c[N - 2] + c[N - 1] * y);
79 for (; j >= 0; j -= 2) {
80 r = r * y2 + (c[j] + c[j + 1] * y);
81 }
82 return r;
83}
84
95inline double erfcx_cody_tail(double x) {
97 return INV_SQRT_PI / x;
98 }
99 const double s = x * x;
100 const auto pq = erfcx_tail_polynomials<erfcx_tail_p.size()>(
101 erfcx_tail_p.cbegin(), erfcx_tail_q.cbegin(), s);
102 return (INV_SQRT_PI + pq.first / (s * pq.second)) / x;
103}
104
127template <typename T>
128inline T erfcx_derivative(const T& x, const T& value) {
129 if (x < 4.0) {
130 return 2.0 * x * value - TWO_OVER_SQRT_PI;
131 }
132 const T x2 = x * x;
133 if (x < 30.0) {
134 const auto pq = erfcx_tail_polynomials<erfcx_tail_p.size()>(
135 erfcx_tail_p.cbegin(), erfcx_tail_q.cbegin(), x2);
136 return 2.0 * pq.first / (x2 * pq.second);
137 }
138 const T u = 1.0 / x2;
139 static constexpr std::array<double, 8> series_coefficients{
140 1.0, -1.5, 3.75, -13.125,
141 59.0625, -324.84375, 2111.484375, -15836.1328125};
142 const T series = erfcx_paired_horner(series_coefficients, u, u * u);
143 return -INV_SQRT_PI * u * series;
144}
145
156inline double erfcx_cody_middle(double y) {
157 const double y2 = y * y;
160}
161
176inline double erfcx_small(double x) {
177 static constexpr std::array<double, 19> c{1.0,
178 -1.12837916709551256,
179 1.0,
180 -7.52252778063651983e-01,
181 4.99999999999992839e-01,
182 -3.00901111227312890e-01,
183 1.66666666667239644e-01,
184 -8.59717459974174147e-02,
185 4.16666666458337179e-02,
186 -1.91048337772546720e-02,
187 8.33333374332981443e-03,
188 -3.47359067853470795e-03,
189 1.38888415444527033e-03,
190 -5.34506929034156810e-04,
191 1.98445679338826757e-04,
192 -7.08163358203131886e-05,
193 2.46655529768908249e-05,
194 -9.35890030086883823e-06,
195 3.05977060678449757e-06};
196 const double x2 = x * x;
197 const double x4 = x2 * x2;
198 const double x8 = x4 * x4;
199 std::array<double, 4> quad;
200 for (int i = 0; i < 4; ++i) {
201 const int j = 4 * i + 2;
202 quad[i] = (c[j] + c[j + 1] * x) + (c[j + 2] + c[j + 3] * x) * x2;
203 }
204 const double rest
205 = (quad[0] + quad[1] * x4) + (quad[2] + quad[3] * x4 + c[18] * x8) * x8;
206 return (c[0] + c[1] * x) + x2 * rest;
207}
208
209} // namespace internal
210
267template <typename T, require_arithmetic_t<T>* = nullptr>
268inline double erfcx(T&& xx) {
269 const double x = static_cast<double>(xx);
270 constexpr double cody_min = 4.0;
271 constexpr double middle_min = 0.46875;
272 constexpr double overflow_max = -27.0;
273 if (x >= cody_min) {
275 } else if (x >= middle_min) {
277 } else if (x > -middle_min) {
278 return internal::erfcx_small(x);
279 } else if (x < overflow_max) {
280 return INFTY;
281 }
282 const double h = x * x;
283 const double two_exp_x2 = 2.0 * std::exp(h) * (1.0 + std::fma(x, x, -h));
284 constexpr double reflect_min = -6.1;
285 if (x < reflect_min) {
286 return two_exp_x2;
287 }
288 const double y = -x;
289 return two_exp_x2
290 - (y >= cody_min ? internal::erfcx_cody_tail(y)
292}
293
301struct erfcx_fun {
302 template <typename T>
303 static inline auto fun(T&& x) {
304 return erfcx(std::forward<T>(x));
305 }
306};
307
316template <
317 typename T,
320inline auto erfcx(T&& x) {
321 return apply_scalar_unary<erfcx_fun, T>::apply(std::forward<T>(x));
322}
323
324} // namespace math
325} // namespace stan
326
327#endif
require_t< is_container< std::decay_t< T > > > require_container_t
Require type satisfies is_container.
require_all_not_t< is_nonscalar_prim_or_rev_kernel_expression< std::decay_t< Types > >... > require_all_not_nonscalar_prim_or_rev_kernel_expression_t
Require none of the types satisfy is_nonscalar_prim_or_rev_kernel_expression.
require_not_t< is_var_matrix< std::decay_t< T > > > require_not_var_matrix_t
Require type does not satisfy is_var_matrix.
double erfcx_cody_tail(double x)
Cody (1969) third-interval rational, valid for x >= 4.
Definition erfcx.hpp:95
constexpr std::array< double, 6 > erfcx_tail_p
Definition erfcx.hpp:18
constexpr double erfcx_tail_leading_only
Definition erfcx.hpp:28
constexpr std::array< double, 9 > erfcx_middle_p
Definition erfcx.hpp:31
T erfcx_derivative(const T &x, const T &value)
Derivative of erfcx, 2 * x * erfcx(x) - 2 / sqrt(pi).
Definition erfcx.hpp:128
T erfcx_paired_horner(const std::array< double, N > &c, const T &y, const T &y2)
Horner in y^2 over adjacent coefficient pairs, so the two halves of the chain are independent.
Definition erfcx.hpp:75
double erfcx_cody_middle(double y)
Cody (1969) second-interval rational, valid for 0.46875 <= x <= 4.
Definition erfcx.hpp:156
constexpr std::array< double, 6 > erfcx_tail_q
Definition erfcx.hpp:22
std::pair< T, T > erfcx_tail_polynomials(It p, It q, const T &t)
The two tail polynomials, taking the coefficients in the order the iterators give them as descending ...
Definition erfcx.hpp:53
double erfcx_small(double x)
Degree-18 polynomial for |x| < 0.46875.
Definition erfcx.hpp:176
constexpr std::array< double, 9 > erfcx_middle_q
Definition erfcx.hpp:35
fvar< T > erfcx(const fvar< T > &x)
Return the scaled complementary error function of the argument.
Definition erfcx.hpp:26
static constexpr double INV_SQRT_PI
The value of 1 over the square root of , .
static constexpr double TWO_OVER_SQRT_PI
The value of 2 over the square root of , .
static constexpr double INFTY
Positive infinity.
Definition constants.hpp:46
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
Base template class for vectorization of unary scalar functions defined by a template class F to a sc...
static auto fun(T &&x)
Definition erfcx.hpp:303
Structure to wrap erfcx() so that it can be vectorized.
Definition erfcx.hpp:301