Repository navigation
Conversation
Open
4 tasks done
SteveBronder
added this pull request to stack #3459
October 14, 2026 19:41
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR is based on #3434 and must be merged after it.
This PR improves the accuracy and speed of
beta_neg_binomial_cdf,beta_neg_binomial_lcdfandbeta_neg_binomial_lccdf. The speed-up is from 4x to 9e5x better.This was assisted by Claude
Summary
beta_neg_binomial_cdf,beta_neg_binomial_lcdfandbeta_neg_binomial_lccdfcomputed the upper tail aspmf(n + 1) * 3F2(1, beta + n + 1, r + n + 1; n + 2, alpha + r + beta + n + 1; 1). This series has the parameter excessalpha, so its terms fall only likek^(-1 - alpha). On a grid of 2352 points (alpha0.25 to 1e4,randbeta0.5 to 1e6,n0 to 20000), the three functions threw at 56 to 57 % of the points and returned a wrong value or gradient at another 22 to 28 %. Only 16 to 21 % of the points were correct, and a call took 15 ms (median) and up to 14 s. References: mpmath, the cdf as the finite sum of the pmf, the ccdf as1 - cdfwith extra digits or as the sum of the pmf overk > n.The causes: the value of the 3F2 overflows for shapes from 300;
grad_F32runs into its limit of1e8steps (8 to 13 s, then a throw); Boosthypergeometric_pFqreturns the series wrong by a factor up toe^24at smallalpha;grad_F32stops at a term below1e-8, while the rest of such a series is aboutk t_k / alpha(gradient errors of 1e-5 even atalpha = 10); andcdfandlcdfformed1 - ccdf, solcdfwas wrong by up to 490 where the cdf is small.This PR computes one tail as a sum and the other one as its complement, so that no result cancels:
ndown to 0, in log scale, with the gradient as the sum ofpmf * d log pmf; the pmf is unimodal, so below the mode the sum stops when the remaining terms are negligible;k > nwhen its terms fall fast, or by a Thomae transformation of the 3F2, whose new series has the excessx + n + 1(xisrorbeta) instead ofalpha; for an integerxthe new series is a polynomial, and its derivative with respect toxcontinues past the zero factor;beta_neg_binomial_cdfas the exponential of the sum of the log cdfs.Other libraries that have this cdf (SciPy
betanbinom, RextraDistr::pbnbinom, the generic discretelogcdfof PyMC) sum the pmf for the lower tail and use1 - cdffor the upper tail; that cancels where the ccdf is small. This PR adds the direct upper-tail methods for that region.cdf/lcdf/lccdflcdfwrong by 490cdf), 4.7e-10 absolute (lcdfat -1.4e6)lcdf)"Correct" means that the value and the gradients with respect to
log(r),log(alpha),log(beta)are within 1e-8, absolutely or relatively.Time per call with
vararguments (value and three gradients), Xeon E5-2680 v3, median over 30 points per region, two interleaved rounds:alpha2 to 10, shapes up to 300)alpha(0.25 to 1)n = 20000alpha >= 100The slowest of the 158 timed points takes 0.72 ms (a lower sum of 2e4 terms below the mode); before, that call threw after 13.9 s. For the upper tail, the cheaper of the tail sum and the Thomae series runs first, by an estimate of their numbers of terms.
With #3434 alone, the
cdfgradient isNaNat 15 grid points where the cdf is below 1e-15: the more accurate log pmf makesexp(C) Fround to exactly 1, so1 - ccdf = 0. This PR does not form1 - ccdfthere.Two smaller changes:
nequal to the largestintstands for infinity.beta_neg_binomial_cdfandbeta_neg_binomial_lcdfreturned 1 and 0 for the whole vector when one element had it; now that element contributes a factor of 1 (a term of 0).precisionargument is no longer used (the sums stop when their remaining terms are below 1e-17 of the partial sums).max_stepsis now the largest number of terms of a sum; a function throws if a sum needs more. No call on the grid comes near it.Limits that remain
n = 20000,r = 0.5,beta = 1e6andalpha <= 1the log pmf atn + 1has an absolute error of about 2e-11, because its terms (for examplelbeta(20001, 1e6), of size 1e5) cancel to about -15.lccdfinherits this, andcdfas its complement multiplies it by 17: 3.6e-10 relative.beta_neg_binomial_lpmfhas the same error there.Tests
test/unit/math/rev/prob/beta_neg_binomial_cdfs_test.cpp: values and log-scale gradients of the three functions at 15 points that cover every route (lower sum, both complements, short and long tail sum, the Thomae series with a non-integer and an integerx), including smallalpha, shapes of 1e6, a cdf of 2.6e-35 andn = 20000; tolerances 1e-11 (value) and 1e-10 (gradient), relative tomax(1, |reference|). A second test checks vectors and the largestint. With the previous headers both tests fail.test/unit/math/rev/prob/large_shapes_test.cpp: thebeta_neg_binomial_cdfand_lccdfrows now use the default tolerances (1e-12, 1e-11) instead of (1e-6, 1e-4); with the previous headers they fail.test/prob/beta_neg_binomialpass (69 498 cases).Side Effects
None.
Release notes
Improved the accuracy and speed of
beta_neg_binomial_cdf,beta_neg_binomial_lcdfandbeta_neg_binomial_lccdfChecklist
Copyright holder: Aalto University
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit)make test-headers)make test-math-dependencies)make doxygen)make cpplint)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested