Automatic Differentiation
 
Loading...
Searching...
No Matches
std_normal_lcdf.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_OPENCL_PRIM_STD_NORMAL_LCDF_HPP
2#define STAN_MATH_OPENCL_PRIM_STD_NORMAL_LCDF_HPP
3#ifdef STAN_OPENCL
4
12
13namespace stan {
14namespace math {
15namespace internal {
17 double std_normal_lcdf_lcdf_n; if (std_normal_lcdf_scaled_y > 0.0) {
18 // CDF(x) = 1/2 + 1/2erf(x) = 1 - 1/2erfc(x)
19 std_normal_lcdf_lcdf_n = log1p(-0.5 * erfc(std_normal_lcdf_scaled_y));
20 if (isnan(std_normal_lcdf_lcdf_n)) {
21 std_normal_lcdf_lcdf_n = 0;
22 }
23 } else if (std_normal_lcdf_scaled_y > -20.0) {
24 // CDF(x) = 1/2 - 1/2erf(-x) = 1/2erfc(-x)
25 std_normal_lcdf_lcdf_n = log(erfc(-std_normal_lcdf_scaled_y)) - M_LN2;
26 } else if (10.0 * log(fabs(std_normal_lcdf_scaled_y)) < log(DBL_MAX)) {
27 // entering territory where erfc(-x)~0
28 // need to use direct numerical approximation of lcdf instead
29 // the following based on W. J. Cody, Math. Comp. 23(107):631-638 (1969)
30 // CDF(x) = 1/2erfc(-x)
31 const double x4 = pow(std_normal_lcdf_scaled_y, 4);
32 const double x6 = pow(std_normal_lcdf_scaled_y, 6);
33 const double x8 = pow(std_normal_lcdf_scaled_y, 8);
34 const double x10 = pow(std_normal_lcdf_scaled_y, 10);
35 const double temp_p
36 = 0.000658749161529837803157
37 + 0.0160837851487422766278 / std_normal_lcdf_x2
38 + 0.125781726111229246204 / x4 + 0.360344899949804439429 / x6
39 + 0.305326634961232344035 / x8 + 0.0163153871373020978498 / x10;
40 const double temp_q = -0.00233520497626869185443
41 - 0.0605183413124413191178 / std_normal_lcdf_x2
42 - 0.527905102951428412248 / x4
43 - 1.87295284992346047209 / x6
44 - 2.56852019228982242072 / x8 - 1.0 / x10;
45 std_normal_lcdf_lcdf_n
46 += log(0.5 * M_2_SQRTPI + (temp_p / temp_q) / std_normal_lcdf_x2)
47 - M_LN2 - log(-std_normal_lcdf_scaled_y) - std_normal_lcdf_x2;
48 } else {
49 // std_normal_lcdf_scaled_y^10 term will overflow
50 std_normal_lcdf_lcdf_n = -INFINITY;
51 });
52// NOLINTBEGIN
54 // compute partial derivatives
55 // based on analytic form given by:
56 // dln(CDF)/dx = exp(-x^2)/(sqrt(pi)*(1/2+erf(x)/2)
57 double std_normal_lcdf_dnlcdf = 0.0; double t = 0.0; double t2 = 0.0;
58 double t4 = 0.0;
59
60 // calculate using piecewise function
61 // (due to instability / inaccuracy in the various approximations)
62 if (std_normal_lcdf_deriv_scaled_y > 2.9) {
63 // approximation derived from Abramowitz and Stegun (1964) 7.1.26
64 t = 1.0 / (1.0 + 0.3275911 * std_normal_lcdf_deriv_scaled_y);
65 t2 = t * t;
66 t4 = pow(t, 4);
67 std_normal_lcdf_dnlcdf
68 = 0.5 * M_2_SQRTPI
69 / (exp(std_normal_lcdf_deriv_x2) - 0.254829592 + 0.284496736 * t
70 - 1.421413741 * t2 + 1.453152027 * t2 * t - 1.061405429 * t4);
71 } else if (std_normal_lcdf_deriv_scaled_y > 2.5) {
72 // in the trouble area where all of the standard numerical
73 // approximations are unstable - bridge the gap using Taylor
74 // expansions of the analytic function
75 // use Taylor expansion centred around x=2.7
76 t = std_normal_lcdf_deriv_scaled_y - 2.7;
77 t2 = t * t;
78 t4 = pow(t, 4);
79 std_normal_lcdf_dnlcdf = 0.0003849882382 - 0.002079084702 * t
80 + 0.005229340880 * t2 - 0.008029540137 * t2 * t
81 + 0.008232190507 * t4 - 0.005692364250 * t4 * t
82 + 0.002399496363 * pow(t, 6);
83 } else if (std_normal_lcdf_deriv_scaled_y > 2.1) {
84 // use Taylor expansion centred around x=2.3
85 t = std_normal_lcdf_deriv_scaled_y - 2.3;
86 t2 = t * t;
87 t4 = pow(t, 4);
88 std_normal_lcdf_dnlcdf = 0.002846135439 - 0.01310032351 * t
89 + 0.02732189391 * t2 - 0.03326906904 * t2 * t
90 + 0.02482478940 * t4 - 0.009883071924 * t4 * t
91 - 0.0002771362254 * pow(t, 6);
92 } else if (std_normal_lcdf_deriv_scaled_y > 1.5) {
93 // use Taylor expansion centred around x=1.85
94 t = std_normal_lcdf_deriv_scaled_y - 1.85;
95 t2 = t * t;
96 t4 = pow(t, 4);
97 std_normal_lcdf_dnlcdf = 0.01849212058 - 0.06876280470 * t
98 + 0.1099906382 * t2 - 0.09274533184 * t2 * t
99 + 0.03543327418 * t4 + 0.005644855518 * t4 * t
100 - 0.01111434424 * pow(t, 6);
101 } else if (std_normal_lcdf_deriv_scaled_y > 0.8) {
102 // use Taylor expansion centred around x=1.15
103 t = std_normal_lcdf_deriv_scaled_y - 1.15;
104 t2 = t * t;
105 t4 = pow(t, 4);
106 std_normal_lcdf_dnlcdf = 0.1585747034 - 0.3898677543 * t
107 + 0.3515963775 * t2 - 0.09748053605 * t2 * t
108 - 0.04347986191 * t4 + 0.02182506378 * t4 * t
109 + 0.01074751427 * pow(t, 6);
110 } else if (std_normal_lcdf_deriv_scaled_y > 0.1) {
111 // use Taylor expansion centred around x=0.45
112 t = std_normal_lcdf_deriv_scaled_y - 0.45;
113 t2 = t * t;
114 t4 = pow(t, 4);
115 std_normal_lcdf_dnlcdf = 0.6245634904 - 0.9521866949 * t
116 + 0.3986215682 * t2 + 0.04700850676 * t2 * t
117 - 0.03478651979 * t4 - 0.01772675404 * t4 * t
118 + 0.0006577254811 * pow(t, 6);
119 } else if (10.0 * log(fabs(std_normal_lcdf_deriv_scaled_y))
120 < log(DBL_MAX)) {
121 // approximation derived from Abramowitz and Stegun (1964) 7.1.26
122 // use fact that erf(x)=-erf(-x)
123 // Abramowitz and Stegun define this for -inf<x<0 but seems to be
124 // accurate for -inf<x<0.1
125 t = 1.0 / (1.0 - 0.3275911 * std_normal_lcdf_deriv_scaled_y);
126 t2 = t * t;
127 t4 = pow(t, 4);
128 std_normal_lcdf_dnlcdf
129 = M_2_SQRTPI
130 / (0.254829592 * t - 0.284496736 * t2 + 1.421413741 * t2 * t
131 - 1.453152027 * t4 + 1.061405429 * t4 * t);
132 // check if we need to add a correction term
133 // (from cubic fit of residuals)
134 if (std_normal_lcdf_deriv_scaled_y < -29.0) {
135 std_normal_lcdf_dnlcdf
136 += 0.0015065154280332 * std_normal_lcdf_deriv_x2
137 - 0.3993154819705530 * std_normal_lcdf_deriv_scaled_y
138 - 4.2919418242931700;
139 } else if (std_normal_lcdf_deriv_scaled_y < -17.0) {
140 std_normal_lcdf_dnlcdf
141 += 0.0001263257217272 * std_normal_lcdf_deriv_x2
142 * std_normal_lcdf_deriv_scaled_y
143 + 0.0123586859488623 * std_normal_lcdf_deriv_x2
144 - 0.0860505264736028 * std_normal_lcdf_deriv_scaled_y
145 - 1.252783383752970;
146 } else if (std_normal_lcdf_deriv_scaled_y < -7.0) {
147 std_normal_lcdf_dnlcdf
148 += 0.000471585349920831 * std_normal_lcdf_deriv_x2
149 * std_normal_lcdf_deriv_scaled_y
150 + 0.0296839305424034 * std_normal_lcdf_deriv_x2
151 + 0.207402143352332 * std_normal_lcdf_deriv_scaled_y
152 + 0.425316974683324;
153 } else if (std_normal_lcdf_deriv_scaled_y < -3.9) {
154 std_normal_lcdf_dnlcdf
155 += -0.0006972280656443 * std_normal_lcdf_deriv_x2
156 * std_normal_lcdf_deriv_scaled_y
157 + 0.0068218494628567 * std_normal_lcdf_deriv_x2
158 + 0.0585761964460277 * std_normal_lcdf_deriv_scaled_y
159 + 0.1034397670201370;
160 } else if (std_normal_lcdf_deriv_scaled_y < -2.1) {
161 std_normal_lcdf_dnlcdf
162 += -0.0018742199480885 * std_normal_lcdf_deriv_x2
163 * std_normal_lcdf_deriv_scaled_y
164 - 0.0097119598291202 * std_normal_lcdf_deriv_x2
165 - 0.0170137970924080 * std_normal_lcdf_deriv_scaled_y
166 - 0.0100428567412041;
167 }
168 } else { std_normal_lcdf_dnlcdf = INFINITY; });
169// NOLINTEND
170} // namespace internal
171
180template <typename T_y_cl,
181 require_all_prim_or_rev_kernel_expression_t<T_y_cl>* = nullptr,
182 require_any_not_stan_scalar_t<T_y_cl>* = nullptr>
183inline return_type_t<T_y_cl> std_normal_lcdf(const T_y_cl& y) {
184 static constexpr const char* function = "std_normal_lcdf(OpenCL)";
185 using std::isfinite;
186 using std::isnan;
187
188 const size_t N = math::size(y);
189 if (N == 0) {
190 return 1.0;
191 }
192
193 const auto& y_col = as_column_vector_or_scalar(y);
194 const auto& y_val = value_of(y_col);
195
196 auto check_y_not_nan
197 = check_cl(function, "Random variable", y_val, "not NaN");
198 auto y_not_nan_expr = !isnan(y_val);
199
200 auto scaled_y = y_val * INV_SQRT_TWO;
201 auto x2 = square(scaled_y);
202 auto lcdf_expr = colwise_sum(
203 opencl_code<internal::opencl_std_normal_lcdf_impl>(
204 std::make_tuple("std_normal_lcdf_scaled_y", "std_normal_lcdf_x2"),
205 scaled_y, x2)
206 .template output<double>("std_normal_lcdf_lcdf_n"));
207 auto dnlcdf = opencl_code<internal::opencl_std_normal_lcdf_dnlcdf>(
208 std::make_tuple("std_normal_lcdf_deriv_scaled_y",
209 "std_normal_lcdf_deriv_x2"),
210 scaled_y, x2)
211 .template output<double>("std_normal_lcdf_dnlcdf");
212 auto y_deriv = dnlcdf * INV_SQRT_TWO;
213
214 matrix_cl<double> lcdf_cl;
215 matrix_cl<double> y_deriv_cl;
216
217 results(check_y_not_nan, lcdf_cl, y_deriv_cl) = expressions(
218 y_not_nan_expr, lcdf_expr, calc_if<is_autodiff_v<T_y_cl>>(y_deriv));
219
220 double lcdf = from_matrix_cl(lcdf_cl).sum();
221
222 auto ops_partials = make_partials_propagator(y_col);
223
224 if constexpr (is_autodiff_v<T_y_cl>) {
225 partials<0>(ops_partials) = std::move(y_deriv_cl);
226 }
227 return ops_partials.build(lcdf);
228}
229
230} // namespace math
231} // namespace stan
232#endif
233#endif
Represents an arithmetic matrix on the OpenCL device.
Definition matrix_cl.hpp:47
auto check_cl(const char *function, const char *var_name, T &&y, const char *must_be)
Constructs a check on opencl matrix or expression.
Definition check_cl.hpp:219
results_cl< T_results... > results(T_results &&... results)
Deduces types for constructing results_cl object.
auto as_column_vector_or_scalar(T &&a)
as_column_vector_or_scalar of a kernel generator expression.
calc_if_< true, as_operation_cl_t< T > > calc_if(T &&a)
Definition calc_if.hpp:121
auto colwise_sum(T &&a)
Column wise sum - reduction of a kernel generator expression.
expressions_cl< T_expressions... > expressions(T_expressions &&... expressions)
Deduces types for constructing expressions_cl object.
return_type_t< T_y_cl > std_normal_lcdf(const T_y_cl &y)
Returns the log standard normal complementary cumulative distribution function.
auto from_matrix_cl(const T &src)
Copies the source matrix that is stored on the OpenCL device to the destination Eigen matrix.
Definition copy.hpp:61
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
int64_t size(const T &m)
Returns the size (number of the elements) of a matrix_cl or var_value<matrix_cl<T>>.
Definition size.hpp:19
const char opencl_std_normal_lcdf_dnlcdf[]
bool isnan(double_d a)
Definition double_d.hpp:327
const char opencl_std_normal_lcdf_impl[]
auto pow(const T1 &x1, const T2 &x2)
Definition pow.hpp:32
T value_of(const fvar< T > &v)
Return the value of the specified variable.
Definition value_of.hpp:18
fvar< T > log(const fvar< T > &x)
Definition log.hpp:18
static constexpr double INV_SQRT_TWO
The value of 1 over the square root of 2, .
fvar< T > erfc(const fvar< T > &x)
Definition erfc.hpp:16
fvar< T > log1p(const fvar< T > &x)
Definition log1p.hpp:12
auto make_partials_propagator(Ops &&... ops)
Construct an partials_propagator.
fvar< T > fabs(const fvar< T > &x)
Definition fabs.hpp:16
fvar< T > square(const fvar< T > &x)
Definition square.hpp:12
fvar< T > exp(const fvar< T > &x)
Definition exp.hpp:15
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
bool isnan(const stan::math::var &a)
Checks if the given number is NaN.
Definition std_isnan.hpp:18
#define STRINGIFY(...)
Definition stringify.hpp:9