Fix numba CauchyRV loc/scale - #2309
Merged
jessegrabowski merged 2 commits intoJul 30, 2026
Merged
Conversation
The numba dispatch computed `(loc + z) / scale` instead of `loc + scale * z`, giving the sampled variable location `loc/scale` and scale `1/scale`. `rng_fn_scipy` and the JAX dispatch are both correct, so only the numba backend was affected -- but it is the default linker. `logp` is untouched, so NUTS posteriors were fine; everything that draws was not, including `sample_prior_predictive` on a `HalfCauchy` prior. The existing `cauchy` case in `test_unaligned_RandomVariable` passed `scale=1.0`, where `(loc + z) / 1` is algebraically identical to `loc + 1 * z`, so both the location and scale errors vanished together. Bumping that to `scale=3.0` makes the Cramer-von Mises check fail on the old code (p = 1.7e-08) and pass on the new. Closes pymc-devs#2308
`test_unaligned_RandomVariable` is the only test that checks these samplers against their distribution, and it has three cases. `cauchy` and `gumbel` both used `scale=1.0`; `t` already passes `scale=np.pi`. `GumbelRV` has a hand-written core too (`loc - scale * np.log(-np.log(U))`) and is correct, but at `scale=1` a `(loc - z) / scale`-style slip would be invisible there for the same algebraic reason. Bumped to `scale=4.0`. Verified by injecting exactly that slip: the test fails at p=7.2e-08 with it and passes without. Audited the other hand-written numba cores (`t`, `halfnormal`, `pareto`, `invgamma`, `gumbel`, `wald`, plus the default-path RVs) against scipy with non-degenerate parameters -- all match, so `cauchy` was the only actual bug.
ricardoV94
approved these changes
Jul 27, 2026
notluquis
added a commit
to notluquis/erotica
that referenced
this pull request
Aug 2, 2026
Asked whether other distributions share the cauchy blind spot. Two axes checked.
1. Empirical. Every hand-written numba core (StudentT, HalfNormal, Pareto,
InvGamma, Gumbel, Wald, Cauchy) plus the default-path RVs (normal, laplace,
logistic, gamma, exponential, vonmises, weibull) sampled with NON-degenerate
loc/scale/shape and compared to scipy by median, IQR and Cramer-von Mises.
All match. Cauchy was the only real bug.
2. Structural. test_unaligned_RandomVariable is the only test that checks these
samplers against their distribution, and it has exactly three cases. Two used
scale=1.0, where (loc +/- z)/scale is algebraically identical to loc +/- 1*z,
so location and scale errors vanish together:
cauchy scale=1.0 -> 3.0 (catches the actual bug, p=1.7e-08)
gumbel scale=1.0 -> 4.0 (no bug, but equally undetectable)
t scale=np.pi already fine
Gumbel has a hand-written loc-scale core too. Proved the new case works by
injecting the analogous slip into it: fails at p=7.2e-08 with, passes without.
Corrected one of my own readings along the way: I first flagged the t case as
degenerate, but it passes loc=[1,2] and scale=np.pi -- my regex only missed it
because np.e and np.pi are not numeric literals.
PR pymc-devs/pytensor#2309 updated with both. Full file: 84 passed, 2 xfailed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Closes #2308.
The numba dispatch computed
(loc + z) / scaleinstead ofloc + scale * z, so the sampled variable had locationloc/scaleand scale1/scale.rng_fn_scipyand the JAX dispatch were already correct, so only numba was affected — but it is the default linker.logpis untouched, so NUTS posteriors were fine; anything that draws was not, includingsample_prior_predictiveon aHalfCauchyprior (HalfCauchyRV.rv_opbuildsabs(cauchy(0, beta))).Tests
test_unaligned_RandomVariableis the only test checking these samplers against their distribution, and it has three cases. Two used a degenerate scale:cauchyscale=1.0scale=3.0gumbelscale=1.0scale=4.0tscale=np.piAt
scale=1,(loc ± z) / 1is algebraically identical toloc ± 1 * z, so both the location and scale errors vanish together and Cramér–von Mises passes.GumbelRVhas a hand-written core too (loc - scale * np.log(-np.log(U))) and is correct, but was equally undetectable. Verified by injecting the analogous slip: the new case fails at p=7.2e-08 with it, passes without.Audit
Checked every hand-written numba core (
t,halfnormal,pareto,invgamma,gumbel,wald) plus the default-path RVs against scipy with non-degenerate parameters. All match —cauchywas the only real bug.tests/link/numba/test_random.py: 84 passed, 2 xfailed.