summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release/1.17.0-notes.rst11
-rw-r--r--numpy/random/mtrand/distributions.c31
2 files changed, 33 insertions, 9 deletions
diff --git a/doc/release/1.17.0-notes.rst b/doc/release/1.17.0-notes.rst
index 71ad17673..60614673f 100644
--- a/doc/release/1.17.0-notes.rst
+++ b/doc/release/1.17.0-notes.rst
@@ -83,6 +83,17 @@ The functions ``np.load``, and ``np.lib.format.read_array`` take an
`allow_pickle` keyword which now defaults to ``False`` in response to
`CVE-2019-6446 <https://nvd.nist.gov/vuln/detail/CVE-2019-6446>`_.
+Potential changes to the random stream
+--------------------------------------
+Due to bugs in the application of log to random floating point numbers,
+the stream may change when sampling from ``np.random.beta``, ``np.random.binomial``,
+``np.random.laplace``, ``np.random.logistic``, ``np.random.logseries`` or
+``np.random.multinomial`` if a 0 is generated in the underlying MT19937 random stream.
+There is a 1 in :math:`10^{53}` chance of this occurring, and so the probability that
+the stream changes for any given seed is extremely small. If a 0 is encountered in the
+underlying generator, then the incorrect value produced (either ``np.inf``
+or ``np.nan``) is now dropped.
+
C API changes
=============
diff --git a/numpy/random/mtrand/distributions.c b/numpy/random/mtrand/distributions.c
index de397ccfa..2b1f5e7a9 100644
--- a/numpy/random/mtrand/distributions.c
+++ b/numpy/random/mtrand/distributions.c
@@ -198,9 +198,10 @@ double rk_beta(rk_state *state, double a, double b)
X = pow(U, 1.0/a);
Y = pow(V, 1.0/b);
- if ((X + Y) <= 1.0)
+ /* Reject if both U and V are 0.0, which is approx 1 in 10^106 */
+ if (((X + Y) <= 1.0) && ((U + V) > 0.0))
{
- if (X +Y > 0)
+ if (X + Y > 0)
{
return X / (X + Y);
}
@@ -329,13 +330,15 @@ long rk_binomial_btpe(rk_state *state, long n, double p)
Step30:
if (u > p3) goto Step40;
y = (long)floor(xl + log(v)/laml);
- if (y < 0) goto Step10;
+ /* Reject if v == 0.0 since cast of inf not well defined */
+ if ((y < 0) || (v == 0.0)) goto Step10;
v = v*(u-p2)*laml;
goto Step50;
Step40:
y = (long)floor(xr - log(v)/lamr);
- if (y > n) goto Step10;
+ /* Reject if v == 0.0 since cast of inf not well defined */
+ if ((y > n) || (v == 0.0)) goto Step10;
v = v*(u-p3)*lamr;
Step50:
@@ -666,12 +669,17 @@ double rk_laplace(rk_state *state, double loc, double scale)
double U;
U = rk_double(state);
- if (U < 0.5)
+ if (U >= 0.5)
+ {
+ U = loc - scale * log(2.0 - U - U);
+
+ } else if (U > 0.0)
{
U = loc + scale * log(U + U);
} else
{
- U = loc - scale * log(2.0 - U - U);
+ /* Reject if U == 0.0 */
+ return rk_laplace(state, loc, scale);
}
return U;
}
@@ -681,7 +689,9 @@ double rk_gumbel(rk_state *state, double loc, double scale)
double U;
U = 1.0 - rk_double(state);
- return loc - scale * log(-log(U));
+ if (U < 1.0)
+ return loc - scale * log(-log(U));
+ return rk_gumbel(state, loc, scale);
}
double rk_logistic(rk_state *state, double loc, double scale)
@@ -689,7 +699,9 @@ double rk_logistic(rk_state *state, double loc, double scale)
double U;
U = rk_double(state);
- return loc + scale * log(U/(1.0 - U));
+ if (U > 0.0)
+ return loc + scale * log(U/(1.0 - U));
+ return rk_logistic(state, loc, scale);
}
double rk_lognormal(rk_state *state, double mean, double sigma)
@@ -914,7 +926,8 @@ long rk_logseries(rk_state *state, double p)
q = 1.0 - exp(r*U);
if (V <= q*q) {
result = (long)floor(1 + log(V)/log(q));
- if (result < 1) {
+ /* Reject if v == 0.0 since cast of inf not well defined */
+ if ((result < 1) || (V == 0.0)) {
continue;
}
else {