Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vectorise inv_Phi calculations and optimise log-scale implementation #3046

Merged
merged 9 commits into from
Apr 30, 2024
Prev Previous commit
Next Next commit
Begin centralising with std_normal_log_qf
  • Loading branch information
andrjohns committed Apr 11, 2024
commit 76ef6a787a5e9b82ac250ae5efe96f1ba677d968
27 changes: 13 additions & 14 deletions stan/math/prim/fun/inv_Phi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@ const int BIGINT = 2000000000;
* @param p argument between 0 and 1 inclusive
* @return Real value of the inverse cdf for the standard normal distribution.
*/
inline double inv_Phi_lambda(double p) {
check_bounded("inv_Phi", "Probability variable", p, 0, 1);

if (p < 8e-311) {
return NEGATIVE_INFTY;
}
if (p == 1) {
return INFTY;
}

inline double inv_Phi_impl(double p, bool log_p) {
static constexpr double log_a[8]
= {1.2199838032983212, 4.8914137334471356, 7.5865960847956080,
9.5274618535358388, 10.734698580862359, 11.116406781896242,
Expand Down Expand Up @@ -95,7 +86,7 @@ inline double inv_Phi_lambda(double p) {
}

// As computation requires evaluating r^8, this causes a loss of precision,
// even when using log space. We can mitigate this by scaling the
// even when on the log space. We can mitigate this by scaling the
// exponentiated result (dividing by 10), since the same scaling is applied
// to the numerator and denominator.
Eigen::VectorXd log_r_pow = Eigen::ArrayXd::LinSpaced(8, 0, 7) * log(inner_r)
Expand All @@ -121,9 +112,17 @@ inline double inv_Phi_lambda(double p) {
* @return real value of the inverse cdf for the standard normal distribution
*/
inline double inv_Phi(double p) {
return p >= 0.9999 ? -internal::inv_Phi_lambda(
(internal::BIGINT - internal::BIGINT * p) / internal::BIGINT)
: internal::inv_Phi_lambda(p);
check_bounded("inv_Phi", "Probability variable", p, 0, 1);

if (p < 8e-311) {
return NEGATIVE_INFTY;
}
if (p == 1) {
return INFTY;
}
return p >= 0.9999 ? -internal::inv_Phi_impl(
(internal::BIGINT - internal::BIGINT * p) / internal::BIGINT, false)
: internal::inv_Phi_impl(p, false);
}

/**
Expand Down