You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Derive log CDF and log CCDF of transformed distributions from std_log_lcdf #3364
Issue #1985 highlighted how changes improving numerical accuracy do not always propagate their benefits to closely related distributions. In the case of standard transformations, the Stan community could capture some DRY benefits from connecting as many derived functions as possible to a reference function that keeps up with the latest numerical improvements (or at least tries to).
While I don't have the overview to make a super-issue of this – there are surely some places where it does make sense to duplicate even lengthy and complicated numerical code – I think there is some low-hanging fruit for the normal distribution. Along the lines of what I tried to do in pull request #3363, I would propose specifically:
refactoring normal_lcdf to rely on std_normal_lcdf (with some extra partials lines to avoid autodiffing, as discussed here);
refactoring lognormal_lcdf and lognormal_lccdf to rely on normal_lcdf (likewise with some extra partials lines);
ditto for exp_mod_normal_lcdf and exp_mod_normal_lccdf; and
ditto for skew_normal_lcdf and skew_normal_lccdf.
The wiener5_lpdf function already uses an approach somewhat like this.
On the OpenCL side, there is also std_normal_lcdf_scaled_impl, but I don't understand enough about OpenCL to know whether it would make sense to tie that to std_normal_lcdf also.
If there is agreement that this kind of structure would be useful, I am happy to take a stab at it.
Most of this issue is implemented in #3406. There, normal_lcdf, lognormal_lcdf/lognormal_lccdf and exp_mod_normal_lcdf/exp_mod_normal_lccdf compute the standard normal log CDF and its gradient through one shared internal implementation, internal::std_normal_lcdf_value_grad in prim/fun/std_normal_lcdf_impl.hpp. #3406 also answers the OpenCL question: it adds OpenCL device functions for the same computation, and the OpenCL normal, lognormal and exp_mod_normal functions use them. normal_lccdf and std_normal_lccdf already go through the lcdf functions on develop since #3363.
The skew normal item does not fit the same pattern. The skew normal CDF is Phi(z) - 2 T(z, alpha), with Owen's T function, and develop computes it with erfc and owens_t. In the tails the precision is lost in that subtraction, so a call to normal_lcdf would not make it stable. It needs its own representation of the skew normal tail.
I think this issue could be closed when #3406 is merged, and then open a separate issue for stable skew_normal_lcdf and skew_normal_lccdf if there is interest.
Activity
avehtari commentedon Oct 8, 2026
Most of this issue is implemented in #3406. There,
normal_lcdf,lognormal_lcdf/lognormal_lccdfandexp_mod_normal_lcdf/exp_mod_normal_lccdfcompute the standard normal log CDF and its gradient through one shared internal implementation,internal::std_normal_lcdf_value_gradinprim/fun/std_normal_lcdf_impl.hpp. #3406 also answers the OpenCL question: it adds OpenCL device functions for the same computation, and the OpenCLnormal,lognormalandexp_mod_normalfunctions use them.normal_lccdfandstd_normal_lccdfalready go through thelcdffunctions ondevelopsince #3363.The skew normal item does not fit the same pattern. The skew normal CDF is
Phi(z) - 2 T(z, alpha), with Owen's T function, anddevelopcomputes it witherfcandowens_t. In the tails the precision is lost in that subtraction, so a call tonormal_lcdfwould not make it stable. It needs its own representation of the skew normal tail.I think this issue could be closed when #3406 is merged, and then open a separate issue for stable
skew_normal_lcdfandskew_normal_lccdfif there is interest.avehtari commentedon Oct 11, 2026
As
skew_normal_lcdfandskew_normal_lccdfdo not benefit from #3406, I made an additional PR #3443