Implement icdf for Poisson, Binomial, and NegativeBinomial#8339
Implement icdf for Poisson, Binomial, and NegativeBinomial#8339jacobpstein wants to merge 4 commits into
Conversation
Documentation build overview
165 files changed ·
|
|
Can you split the bernoulli into it's own PR, it's much more trivial to review and approve than the ones requiring search |
1f662e1 to
1fb6f6f
Compare
|
Done! split the Bernoulli |
| whose quantile function has no closed form (e.g. ``Poisson``, ``Binomial``, | ||
| ``NegativeBinomial``). | ||
|
|
||
| The search uses fixed-length ``scan`` loops (rather than a data-dependent |
There was a problem hiding this comment.
Have to think about this, C/Numba are more than happy with data dependent loops.
There was a problem hiding this comment.
Yeahhhh, the real constraint I was designing around is JAX (and static-shape lowering generally), which can't do a data-dependent number of steps.
I went with fixed-length scan so the same icdf graph works across all backends without special-casing, at the cost of always running max_iters iterations even after convergence. One option is to use a data-dependent while loop, accepting that JAX would be unsupported (or fall back) for these icdfs. Something to think about.
| return expr | ||
|
|
||
|
|
||
| def discrete_icdf_via_search(logcdf, value, lower, upper=None, *, max_iters=64): |
There was a problem hiding this comment.
I may be misremembering, but one of the libraries I looked at also allowed some candidate bounds or something to inform the starting point of the search, in case the callers can offer something useful. Not saying we have to, but if there are obvious cases and it's not too much complexity would be good to discuss
There was a problem hiding this comment.
I'm always open to learning some new libraries! Right now upper is already the big lever-- Binomial passes upper=n and skips the expansion phase. For the unbounded cases the obvious hint is the mean (mu for Poisson, n(1-p)/p for NegativeBinomial), so I could add an optional start that seeds the geometric expansion there instead of doubling from 1.
Payoff is modest though since bisection is already logarithmic, so a hint just trims a few iterations off phase 1. Low-complexity to add if you think it's worth it, otherwise happy to leave as a follow-up, and if there's an existing package whose approach we can reference here, even better
|
@jacobpstein implementation looks really clean. I don't have concrete feedback yet, have to marinate a bit on it. Let's some vague questions/thoughts above |
The geometric bracket expansion can now be seeded with a caller-provided guess (typically the distribution mean) instead of always doubling from lower + 1. Poisson and NegativeBinomial pass their means; Binomial is unchanged since it already provides upper=n.
|
Went ahead and added the On the loop question, I dug into this a bit more and unfortunately there's no middle ground. Adding an Fun discovery while stress-testing at large |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8339 +/- ##
===========================================
+ Coverage 79.80% 90.12% +10.32%
===========================================
Files 125 128 +3
Lines 20526 20733 +207
===========================================
+ Hits 16380 18685 +2305
+ Misses 4146 2048 -2098
🚀 New features to boost your workflow:
|
|
Opened pymc-devs/pytensor#2291 for the gammaincc thing. |
icdf graphs are always float-typed because out-of-domain values map to nan. When Truncated uses inverse-CDF sampling of a discrete base distribution, the resulting RV silently became float64, so e.g. Hurdle mixtures rejected it as a continuous component. Cast the icdf draw back to the base RV dtype.
Description
Adds
icdf(quantile / inverse-CDF) methods for four discrete distributions:icdf(q) = 0 if q ≤ 1 − p else 1.gammaincinv/betaincinv) don't apply since they invert the CDF in its continuous probability argument rather than over the integer support. This adds a shareddiscrete_icdf_via_searchhelper (indist_math.py) that inverts the monotonelogcdfby bisection, with geometric bracket expansion for the unbounded-support cases (Poisson, NegativeBinomial).The helper uses fixed-length
scanloops (no data-dependentuntil) so the graph remains convertible to the C, Numba, and JAX backends — verified for all three. As a side effect this enablespm.Truncatedfor these distributions, which falls back toicdffor inverse-transform sampling.Tests compare against SciPy's
ppfusing the existingcheck_icdfhelper.A few other distributions in the same family (BetaBinomial, HyperGeometric) could reuse the same helper if you all want! This was very fun to work on. I met Daniel Lee at the Sloan conf back in March and have been using PyMc ever since--though I still enjoy Stan.
Related Issue
Checklist
Type of change