Automatic Differentiation
 
Loading...
Searching...
No Matches
normal_lcdf.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_PROB_NORMAL_LCDF_HPP
2#define STAN_MATH_PRIM_PROB_NORMAL_LCDF_HPP
3
21#include <cmath>
22#include <limits>
23
24namespace stan {
25namespace math {
26namespace internal {
27constexpr char normal_lcdf_func[] = "normal_lcdf";
28} // namespace internal
29
139template <const char* func = internal::normal_lcdf_func, typename T_y,
140 typename T_loc, typename T_scale,
142 T_y, T_loc, T_scale>* = nullptr>
144 const T_loc& mu,
145 const T_scale& sigma) {
146 using T_partials_return = partials_return_t<T_y, T_loc, T_scale>;
147 using T_y_ref = ref_type_t<T_y>;
148 using T_mu_ref = ref_type_t<T_loc>;
149 using T_sigma_ref = ref_type_t<T_scale>;
150 static constexpr const char* function = func;
151 check_consistent_sizes(function, "Random variable", y, "Location parameter",
152 mu, "Scale parameter", sigma);
153 T_y_ref y_ref = y;
154 T_mu_ref mu_ref = mu;
155 T_sigma_ref sigma_ref = sigma;
156 check_not_nan(function, "Random variable", y_ref);
157 check_finite(function, "Location parameter", mu_ref);
158 check_positive(function, "Scale parameter", sigma_ref);
159
160 if (size_zero(y, mu, sigma)) {
161 return 0;
162 }
163
164 T_partials_return cdf_log(0.0);
165 auto ops_partials = make_partials_propagator(y_ref, mu_ref, sigma_ref);
166
167 scalar_seq_view<T_y_ref> y_vec(y_ref);
168 scalar_seq_view<T_mu_ref> mu_vec(mu_ref);
169 scalar_seq_view<T_sigma_ref> sigma_vec(sigma_ref);
170 size_t N = max_size(y, mu, sigma);
171
172 for (size_t n = 0; n < N; n++) {
173 const T_partials_return y_dbl = y_vec.val(n);
174 const T_partials_return mu_dbl = mu_vec.val(n);
175 const T_partials_return sigma_dbl = sigma_vec.val(n);
176
177 const T_partials_return scaled_diff
178 = (y_dbl - mu_dbl) / (sigma_dbl * SQRT_TWO);
179
180 const T_partials_return x2 = square(scaled_diff);
181
182 // Rigorous numerical approximations are applied here to deal with values
183 // of |scaled_diff|>>0. This is needed to deal with rare base-rate
184 // logistic regression problems where it is useful to use an alternative
185 // link function instead.
186 //
187 // use erfc() instead of erf() in order to retain precision
188 // since for x>0 erfc()->0
189 if (scaled_diff > 0.0) {
190 // CDF(x) = 1/2 + 1/2erf(x) = 1 - 1/2erfc(x)
191 cdf_log += log1p(-0.5 * erfc(scaled_diff));
192 if (!is_not_nan(cdf_log)) {
193 cdf_log = 0;
194 }
195 } else if (scaled_diff > -4.0) {
196 // CDF(x) = 1/2 - 1/2erf(-x) = 1/2erfc(-x); -4 is R pnorm's M_SQRT_32
197 // Since we scale by sqrt(2), we use sqrt(32)/sqrt(2) = 4
198 cdf_log += log(erfc(-scaled_diff)) + LOG_HALF;
199 } else if (10.0 * log(fabs(scaled_diff))
200 < log(std::numeric_limits<T_partials_return>::max())) {
201 // entering territory where erfc(-x)~0
202 // need to use direct numerical approximation of cdf_log instead
203 // the following based on W. J. Cody, Math. Comp. 23(107):631-638 (1969)
204 // CDF(x) = 1/2erfc(-x)
205 const T_partials_return x4 = pow(scaled_diff, 4);
206 const T_partials_return x6 = pow(scaled_diff, 6);
207 const T_partials_return x8 = pow(scaled_diff, 8);
208 const T_partials_return x10 = pow(scaled_diff, 10);
209 const T_partials_return temp_p
210 = 0.000658749161529837803157 + 0.0160837851487422766278 / x2
211 + 0.125781726111229246204 / x4 + 0.360344899949804439429 / x6
212 + 0.305326634961232344035 / x8 + 0.0163153871373020978498 / x10;
213 const T_partials_return temp_q
214 = -0.00233520497626869185443 - 0.0605183413124413191178 / x2
215 - 0.527905102951428412248 / x4 - 1.87295284992346047209 / x6
216 - 2.56852019228982242072 / x8 - 1.0 / x10;
217 cdf_log += LOG_HALF + log(INV_SQRT_PI + (temp_p / temp_q) / x2)
218 - log(-scaled_diff) - x2;
219 } else {
220 // scaled_diff^10 term will overflow
222 }
223
224 if constexpr (is_any_autodiff_v<T_y, T_loc, T_scale>) {
225 // compute partial derivatives
226 // based on analytic form given by:
227 // dln(CDF)/dx = exp(-x^2)/(sqrt(pi)*(1/2+erf(x)/2)
228 T_partials_return dncdf_log = 0.0;
229 T_partials_return t = 0.0;
230 T_partials_return t2 = 0.0;
231 T_partials_return t4 = 0.0;
232
233 // calculate using piecewise function
234 // (due to instability / inaccuracy in the various approximations)
235 if (scaled_diff > 2.9) {
236 // approximation derived from Abramowitz and Stegun (1964) 7.1.26
237 t = 1.0 / (1.0 + 0.3275911 * scaled_diff);
238 t2 = square(t);
239 t4 = pow(t, 4);
240 // A&S 7.1.26 puts exp(-x2) in the numerator; keep it there so it
241 // underflows to zero instead of overflowing inside a denominator
242 const T_partials_return exp_m_x2 = exp(-x2);
243 dncdf_log
244 = exp_m_x2
245 / (SQRT_PI
246 * (1.0
247 - exp_m_x2
248 * (0.254829592 - 0.284496736 * t + 1.421413741 * t2
249 - 1.453152027 * t2 * t + 1.061405429 * t4)));
250 } else if (scaled_diff > 2.5) {
251 // in the trouble area where all of the standard numerical
252 // approximations are unstable - bridge the gap using Taylor
253 // expansions of the analytic function
254 // use Taylor expansion centred around x=2.7
255 t = scaled_diff - 2.7;
256 t2 = square(t);
257 t4 = pow(t, 4);
258 dncdf_log = 0.0003849882382 - 0.002079084702 * t + 0.005229340880 * t2
259 - 0.008029540137 * t2 * t + 0.008232190507 * t4
260 - 0.005692364250 * t4 * t + 0.002399496363 * pow(t, 6);
261 } else if (scaled_diff > 2.1) {
262 // use Taylor expansion centred around x=2.3
263 t = scaled_diff - 2.3;
264 t2 = square(t);
265 t4 = pow(t, 4);
266 dncdf_log = 0.002846135439 - 0.01310032351 * t + 0.02732189391 * t2
267 - 0.03326906904 * t2 * t + 0.02482478940 * t4
268 - 0.009883071924 * t4 * t - 0.0002771362254 * pow(t, 6);
269 } else if (scaled_diff > 1.5) {
270 // use Taylor expansion centred around x=1.85
271 t = scaled_diff - 1.85;
272 t2 = square(t);
273 t4 = pow(t, 4);
274 dncdf_log = 0.01849212058 - 0.06876280470 * t + 0.1099906382 * t2
275 - 0.09274533184 * t2 * t + 0.03543327418 * t4
276 + 0.005644855518 * t4 * t - 0.01111434424 * pow(t, 6);
277 } else if (scaled_diff > 0.8) {
278 // use Taylor expansion centred around x=1.15
279 t = scaled_diff - 1.15;
280 t2 = square(t);
281 t4 = pow(t, 4);
282 dncdf_log = 0.1585747034 - 0.3898677543 * t + 0.3515963775 * t2
283 - 0.09748053605 * t2 * t - 0.04347986191 * t4
284 + 0.02182506378 * t4 * t + 0.01074751427 * pow(t, 6);
285 } else if (scaled_diff > 0.1) {
286 // use Taylor expansion centred around x=0.45
287 t = scaled_diff - 0.45;
288 t2 = square(t);
289 t4 = pow(t, 4);
290 dncdf_log = 0.6245634904 - 0.9521866949 * t + 0.3986215682 * t2
291 + 0.04700850676 * t2 * t - 0.03478651979 * t4
292 - 0.01772675404 * t4 * t + 0.0006577254811 * pow(t, 6);
293 } else if (scaled_diff < -29.0) {
294 // asymptotic Mills ratio, DLMF 7.12.1: dncdf_log grows linearly as
295 // -2*scaled_diff, so no quadratic residual fit can track it
296 const T_partials_return inv_x2 = 1.0 / x2;
297 dncdf_log
298 = -2.0 * scaled_diff
299 / (1.0 + inv_x2 * (-0.5 + inv_x2 * (0.75 + inv_x2 * -1.875)));
300 } else if (10.0 * log(fabs(scaled_diff))
301 < log(std::numeric_limits<T_partials_return>::max())) {
302 // approximation derived from Abramowitz and Stegun (1964) 7.1.26
303 // use fact that erf(x)=-erf(-x)
304 // Abramowitz and Stegun define this for -inf<x<0 but seems to be
305 // accurate for -inf<x<0.1
306 t = 1.0 / (1.0 - 0.3275911 * scaled_diff);
307 t2 = square(t);
308 t4 = pow(t, 4);
309 dncdf_log
310 = 2.0
311 / (SQRT_PI
312 * (0.254829592 * t - 0.284496736 * t2 + 1.421413741 * t2 * t
313 - 1.453152027 * t4 + 1.061405429 * t4 * t));
314 // check if we need to add a correction term
315 // (from cubic fit of residuals)
316 if (scaled_diff < -17.0) {
317 dncdf_log += 0.0001263257217272 * x2 * scaled_diff
318 + 0.0123586859488623 * x2
319 - 0.0860505264736028 * scaled_diff - 1.252783383752970;
320 } else if (scaled_diff < -7.0) {
321 dncdf_log += 0.000471585349920831 * x2 * scaled_diff
322 + 0.0296839305424034 * x2
323 + 0.207402143352332 * scaled_diff + 0.425316974683324;
324 } else if (scaled_diff < -3.9) {
325 dncdf_log += -0.0006972280656443 * x2 * scaled_diff
326 + 0.0068218494628567 * x2
327 + 0.0585761964460277 * scaled_diff + 0.1034397670201370;
328 } else if (scaled_diff < -2.1) {
329 dncdf_log += -0.0018742199480885 * x2 * scaled_diff
330 - 0.0097119598291202 * x2
331 - 0.0170137970924080 * scaled_diff - 0.0100428567412041;
332 }
333 } else {
334 dncdf_log = stan::math::positive_infinity();
335 }
336 const T_partials_return sigma_sqrt2 = sigma_dbl * SQRT_TWO;
337 if constexpr (is_autodiff_v<T_y>) {
338 partials<0>(ops_partials)[n] += dncdf_log / sigma_sqrt2;
339 }
340 if constexpr (is_autodiff_v<T_loc>) {
341 partials<1>(ops_partials)[n] -= dncdf_log / sigma_sqrt2;
342 }
343 if constexpr (is_autodiff_v<T_scale>) {
344 partials<2>(ops_partials)[n] -= dncdf_log * scaled_diff / sigma_dbl;
345 }
346 }
347 }
348 return ops_partials.build(cdf_log);
349}
350
351} // namespace math
352} // namespace stan
353#endif
scalar_seq_view provides a uniform sequence-like wrapper around either a scalar or a sequence of scal...
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.
return_type_t< T_y_cl, T_loc_cl, T_scale_cl > normal_lcdf(const T_y_cl &y, const T_loc_cl &mu, const T_scale_cl &sigma)
Returns the normal log complementary cumulative distribution function for the given location,...
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
constexpr char normal_lcdf_func[]
static constexpr double negative_infinity()
Return negative infinity.
static constexpr double LOG_HALF
The natural logarithm of 0.5, .
Definition constants.hpp:92
static constexpr double positive_infinity()
Return positive infinity.
bool size_zero(const T &x)
Returns 1 if input is of length 0, returns 0 otherwise.
Definition size_zero.hpp:19
auto pow(const T1 &x1, const T2 &x2)
Definition pow.hpp:32
fvar< T > log(const fvar< T > &x)
Definition log.hpp:18
bool is_not_nan(const T_y &y)
Return true if y is not NaN.
static constexpr double SQRT_TWO
The value of the square root of 2, .
static constexpr double SQRT_PI
The value of the square root of , .
void check_consistent_sizes(const char *)
Trivial no input case, this function is a no-op.
fvar< T > erfc(const fvar< T > &x)
Definition erfc.hpp:16
fvar< T > log1p(const fvar< T > &x)
Definition log1p.hpp:12
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
void check_not_nan(const char *function, const char *name, const T_y &y)
Check if y is not NaN.
static constexpr double INV_SQRT_PI
The value of 1 over the square root of , .
void check_positive(const char *function, const char *name, const T_y &y)
Check if y is positive.
int64_t max_size(const T1 &x1, const Ts &... xs)
Calculate the size of the largest input.
Definition max_size.hpp:20
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
typename ref_type_if< true, T >::type ref_type_t
Definition ref_type.hpp:56
typename partials_return_type< Args... >::type partials_return_t
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...