Skip to content

Commit b427e83

Browse files
mmaaz-gitcharris
authored andcommitted
BUG: fix negative samples generated by Wald distribution (#29609)
In numpy.random, when generating Wald samples, if the mean and scale have a large discrepancy, the current implementation suffers from catastrophic cancellation. An explicit example of this has been added to test_generator_mt19937.py. The key line implicated in distributions.c: `X = mean + mu_2l * (Y - sqrt(4 * scale * Y + Y * Y));` which has numerical issues when Y >> scale. I have replaced this with the equivalent rationalized form: ``` d = Y + sqrt(Y) * sqrt(Y + 4 * scale); X = mean - (2 * mean * Y) / d; ``` And now the test passes.
1 parent 4491392 commit b427e83

2 files changed

Lines changed: 8 additions & 3 deletions

File tree

numpy/random/src/distributions/distributions.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -845,12 +845,12 @@ double random_noncentral_f(bitgen_t *bitgen_state, double dfnum, double dfden,
845845

846846
double random_wald(bitgen_t *bitgen_state, double mean, double scale) {
847847
double U, X, Y;
848-
double mu_2l;
848+
double d;
849849

850-
mu_2l = mean / (2 * scale);
851850
Y = random_standard_normal(bitgen_state);
852851
Y = mean * Y * Y;
853-
X = mean + mu_2l * (Y - sqrt(4 * scale * Y + Y * Y));
852+
d = 1 + sqrt(1 + 4 * scale / Y);
853+
X = mean * (1 - 2 / d);
854854
U = next_double(bitgen_state);
855855
if (U <= mean / (mean + X)) {
856856
return X;

numpy/random/tests/test_generator_mt19937.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,11 @@ def test_wald(self):
18581858
[2.07093587449261, 0.73073890064369]])
18591859
assert_array_almost_equal(actual, desired, decimal=14)
18601860

1861+
def test_wald_nonnegative(self):
1862+
random = Generator(MT19937(self.seed))
1863+
samples = random.wald(mean=1e9, scale=2.25, size=1000)
1864+
assert_(np.all(samples >= 0.0))
1865+
18611866
def test_weibull(self):
18621867
random = Generator(MT19937(self.seed))
18631868
actual = random.weibull(a=1.23, size=(3, 2))

0 commit comments

Comments
 (0)