summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/random/include/legacy-distributions.h1
-rw-r--r--numpy/random/mtrand.pyx4
-rw-r--r--numpy/random/src/distributions/distributions.c29
-rw-r--r--numpy/random/src/legacy/legacy-distributions.c68
4 files changed, 82 insertions, 20 deletions
diff --git a/numpy/random/include/legacy-distributions.h b/numpy/random/include/legacy-distributions.h
index b8ba0841c..f7ccd2cb5 100644
--- a/numpy/random/include/legacy-distributions.h
+++ b/numpy/random/include/legacy-distributions.h
@@ -31,6 +31,7 @@ extern double legacy_f(aug_bitgen_t *aug_state, double dfnum, double dfden);
extern double legacy_normal(aug_bitgen_t *aug_state, double loc, double scale);
extern double legacy_standard_gamma(aug_bitgen_t *aug_state, double shape);
extern double legacy_exponential(aug_bitgen_t *aug_state, double scale);
+extern double legacy_vonmises(bitgen_t *bitgen_state, double mu, double kappa);
extern int64_t legacy_random_binomial(bitgen_t *bitgen_state, double p,
int64_t n, binomial_t *binomial);
extern int64_t legacy_negative_binomial(aug_bitgen_t *aug_state, double n,
diff --git a/numpy/random/mtrand.pyx b/numpy/random/mtrand.pyx
index 8d349b7d8..6f44e271f 100644
--- a/numpy/random/mtrand.pyx
+++ b/numpy/random/mtrand.pyx
@@ -51,7 +51,6 @@ cdef extern from "numpy/random/distributions.h":
void random_standard_uniform_fill(bitgen_t* bitgen_state, np.npy_intp cnt, double *out) nogil
int64_t random_positive_int(bitgen_t *bitgen_state) nogil
double random_uniform(bitgen_t *bitgen_state, double lower, double range) nogil
- double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa) nogil
double random_laplace(bitgen_t *bitgen_state, double loc, double scale) nogil
double random_gumbel(bitgen_t *bitgen_state, double loc, double scale) nogil
double random_logistic(bitgen_t *bitgen_state, double loc, double scale) nogil
@@ -100,6 +99,7 @@ cdef extern from "include/legacy-distributions.h":
double legacy_f(aug_bitgen_t *aug_state, double dfnum, double dfden) nogil
double legacy_exponential(aug_bitgen_t *aug_state, double scale) nogil
double legacy_power(aug_bitgen_t *state, double a) nogil
+ double legacy_vonmises(bitgen_t *bitgen_state, double mu, double kappa) nogil
np.import_array()
@@ -2281,7 +2281,7 @@ cdef class RandomState:
>>> plt.show()
"""
- return cont(&random_vonmises, &self._bitgen, size, self.lock, 2,
+ return cont(&legacy_vonmises, &self._bitgen, size, self.lock, 2,
mu, 'mu', CONS_NONE,
kappa, 'kappa', CONS_NON_NEGATIVE,
0.0, '', CONS_NONE, None)
diff --git a/numpy/random/src/distributions/distributions.c b/numpy/random/src/distributions/distributions.c
index ce909933e..f47c54a53 100644
--- a/numpy/random/src/distributions/distributions.c
+++ b/numpy/random/src/distributions/distributions.c
@@ -1,7 +1,6 @@
#include "numpy/random/distributions.h"
#include "ziggurat_constants.h"
#include "logfactorial.h"
-#include <stdio.h>
#if defined(_MSC_VER) && defined(_WIN64)
#include <intrin.h>
@@ -844,6 +843,7 @@ double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa) {
return NPY_NAN;
}
if (kappa < 1e-8) {
+ /* Use a uniform for very small values of kappa */
return M_PI * (2 * next_double(bitgen_state) - 1);
} else {
/* with double precision rho is zero until 1.4e-8 */
@@ -854,30 +854,23 @@ double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa) {
*/
s = (1. / kappa + kappa);
} else {
-#ifndef NP_RANDOM_LEGACY
- /* Fallback to normal distribution for big values of kappa
- * Fix only applies to Generator, not RandomState
- * RandomState defines NP_RANDOM_LEGACY=1
- */
- if (kappa > 1e6) {
+ if (kappa <= 1e6) {
+ /* Path for 1e-5 <= kappa <= 1e6 */
+ double r = 1 + sqrt(1 + 4 * kappa * kappa);
+ double rho = (r - sqrt(2 * r)) / (2 * kappa);
+ s = (1 + rho * rho) / (2 * rho);
+ } else {
+ /* Fallback to wrapped normal distribution for kappa > 1e6 */
result = mu + sqrt(1. / kappa) * random_standard_normal(bitgen_state);
- /* Check if result is within bounds */
+ /* Ensure result is within bounds */
if (result < -M_PI) {
- return result + 2*M_PI;
+ result += 2*M_PI;
}
if (result > M_PI) {
- return result - 2*M_PI;
+ result -= 2*M_PI;
}
return result;
- } else {
-#endif
- /* Path for 1e-5 <= kappa <= 1e6 */
- double r = 1 + sqrt(1 + 4 * kappa * kappa);
- double rho = (r - sqrt(2 * r)) / (2 * kappa);
- s = (1 + rho * rho) / (2 * rho);
-#if !defined NP_RANDOM_LEGACY
}
-#endif
}
while (1) {
diff --git a/numpy/random/src/legacy/legacy-distributions.c b/numpy/random/src/legacy/legacy-distributions.c
index fd067fe8d..5b17401dd 100644
--- a/numpy/random/src/legacy/legacy-distributions.c
+++ b/numpy/random/src/legacy/legacy-distributions.c
@@ -1,3 +1,13 @@
+/*
+ * This file contains generation code for distribution that have been modified
+ * since Generator was introduced. These are preserved using identical code
+ * to what was in NumPy 1.16 so that the stream of values generated by
+ * RandomState is not changed when there are changes that affect Generator.
+ *
+ * These functions should not be changed except if they contain code that
+ * cannot be compiled. They should not be changed for bug fixes, performance
+ * improvements that can change the values produced, or enhancements to precision.
+ */
#include "include/legacy-distributions.h"
@@ -390,3 +400,61 @@ int64_t legacy_random_logseries(bitgen_t *bitgen_state, double p) {
binomial_t *binomial) {
return random_multinomial(bitgen_state, n, mnix, pix, d, binomial);
}
+
+double legacy_vonmises(bitgen_t *bitgen_state, double mu, double kappa) {
+ double s;
+ double U, V, W, Y, Z;
+ double result, mod;
+ int neg;
+ if (npy_isnan(kappa)) {
+ return NPY_NAN;
+ }
+ if (kappa < 1e-8) {
+ return M_PI * (2 * next_double(bitgen_state) - 1);
+ } else {
+ /* with double precision rho is zero until 1.4e-8 */
+ if (kappa < 1e-5) {
+ /*
+ * second order taylor expansion around kappa = 0
+ * precise until relatively large kappas as second order is 0
+ */
+ s = (1. / kappa + kappa);
+ } else {
+ /* Path for 1e-5 <= kappa <= 1e6 */
+ double r = 1 + sqrt(1 + 4 * kappa * kappa);
+ double rho = (r - sqrt(2 * r)) / (2 * kappa);
+ s = (1 + rho * rho) / (2 * rho);
+ }
+
+ while (1) {
+ U = next_double(bitgen_state);
+ Z = cos(M_PI * U);
+ W = (1 + s * Z) / (s + Z);
+ Y = kappa * (s - W);
+ V = next_double(bitgen_state);
+ /*
+ * V==0.0 is ok here since Y >= 0 always leads
+ * to accept, while Y < 0 always rejects
+ */
+ if ((Y * (2 - Y) - V >= 0) || (log(Y / V) + 1 - Y >= 0)) {
+ break;
+ }
+ }
+
+ U = next_double(bitgen_state);
+
+ result = acos(W);
+ if (U < 0.5) {
+ result = -result;
+ }
+ result += mu;
+ neg = (result < 0);
+ mod = fabs(result);
+ mod = (fmod(mod + M_PI, 2 * M_PI) - M_PI);
+ if (neg) {
+ mod *= -1;
+ }
+
+ return mod;
+ }
+} \ No newline at end of file