summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2016-01-03 15:29:31 -0700
committerCharles Harris <charlesr.harris@gmail.com>2016-01-03 15:29:31 -0700
commit9fb9288f461c7c7af9708f136fa4f29acdc734a8 (patch)
treee4848c8e1ad3298b2c0856e446566388ce87f5d7
parent5dab9275475e70630d7b1242c3530a6726c61fa2 (diff)
parentbc2a97bde26a026e75adec7ec70566be3005c47c (diff)
downloadnumpy-9fb9288f461c7c7af9708f136fa4f29acdc734a8.tar.gz
Merge pull request #6910 from charris/add-64-bit-random-int
ENH: Add dtype argument to random.randint.
-rw-r--r--appveyor.yml6
-rw-r--r--doc/release/1.11.0-notes.rst17
-rw-r--r--numpy/core/include/numpy/npy_common.h3
-rw-r--r--numpy/core/src/multiarray/multiarray_tests.c.src2
-rw-r--r--numpy/core/tests/test_mem_overlap.py41
-rw-r--r--numpy/random/mtrand/mt_compat.h68
-rw-r--r--numpy/random/mtrand/mtrand.pyx390
-rw-r--r--numpy/random/mtrand/numpy.pxd16
-rw-r--r--numpy/random/mtrand/randomkit.c224
-rw-r--r--numpy/random/mtrand/randomkit.h41
-rw-r--r--numpy/random/tests/test_random.py77
-rwxr-xr-xtools/travis-test.sh6
12 files changed, 820 insertions, 71 deletions
diff --git a/appveyor.yml b/appveyor.yml
index 59389462d..e9467e093 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -12,12 +12,6 @@ environment:
- PY_MAJOR_VER: 3
PYTHON_ARCH: "x86"
-matrix:
- #fast_finish: true
- allow_failures:
- - PY_MAJOR_VER: 2
- - PY_MAJOR_VER: 3
-
build_script:
- ps: Start-FileDownload "https://repo.continuum.io/miniconda/Miniconda$env:PY_MAJOR_VER-latest-Windows-$env:PYTHON_ARCH.exe" C:\Miniconda.exe; echo "Finished downloading miniconda"
- cmd: C:\Miniconda.exe /S /D=C:\Py
diff --git a/doc/release/1.11.0-notes.rst b/doc/release/1.11.0-notes.rst
index 7c078eed9..705ce73c1 100644
--- a/doc/release/1.11.0-notes.rst
+++ b/doc/release/1.11.0-notes.rst
@@ -86,6 +86,23 @@ New Features
the files can be specifies to be ``*.f90``. The ``verbose`` argument is
also activated, it was previously ignored.
+* A ``dtype`` parameter has been added to ``np.random.randint``
+ Random ndarrays of the following types can now be generated:
+
+ - np.bool,
+ - np.int8, np.uint8,
+ - np.int16, np.uint16,
+ - np.int32, np.uint32,
+ - np.int64, np.uint64,
+ - np.int_ (long), np.intp
+
+ The specification is by precision rather than by C type. Hence, on some
+ platforms np.int64 may be a `long` instead of `long long` even if the
+ specified dtype is `long long` because the two may have the same
+ precision. The resulting type depends on which c type numpy uses for the
+ given precision. The byteorder specification is also ignored, the
+ generated arrays are always in native byte order.
+
Improvements
============
diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h
index 47ef94c92..baf5549d9 100644
--- a/numpy/core/include/numpy/npy_common.h
+++ b/numpy/core/include/numpy/npy_common.h
@@ -7,6 +7,9 @@
#include <npy_config.h>
#endif
+/* need Python.h for npy_intp, npy_uintp */
+#include <Python.h>
+
/*
* gcc does not unroll even with -O3
* use with care, unrolling on modern cpus rarely speeds things up
diff --git a/numpy/core/src/multiarray/multiarray_tests.c.src b/numpy/core/src/multiarray/multiarray_tests.c.src
index 5e247e15e..45092dc0c 100644
--- a/numpy/core/src/multiarray/multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/multiarray_tests.c.src
@@ -778,7 +778,7 @@ static PyObject *
test_as_c_array(PyObject *NPY_UNUSED(self), PyObject *args)
{
PyArrayObject *array_obj;
- npy_intp dims[3]; // max 3-dim
+ npy_intp dims[3]; /* max 3-dim */
npy_intp i=0, j=0, k=0;
npy_intp num_dims = 0;
PyArray_Descr *descr = NULL;
diff --git a/numpy/core/tests/test_mem_overlap.py b/numpy/core/tests/test_mem_overlap.py
index a8b29ecd1..82e66db5b 100644
--- a/numpy/core/tests/test_mem_overlap.py
+++ b/numpy/core/tests/test_mem_overlap.py
@@ -110,17 +110,19 @@ def test_diophantine_fuzz():
numbers = []
while min(feasible_count, infeasible_count) < min_count:
# Ensure big and small integer problems
- A_max = 1 + rng.randint(0, 11)**6
- U_max = rng.randint(0, 11)**6
+ A_max = 1 + rng.randint(0, 11, dtype=np.intp)**6
+ U_max = rng.randint(0, 11, dtype=np.intp)**6
A_max = min(max_int, A_max)
U_max = min(max_int-1, U_max)
- A = tuple(rng.randint(1, A_max+1) for j in range(ndim))
- U = tuple(rng.randint(0, U_max+2) for j in range(ndim))
+ A = tuple(rng.randint(1, A_max+1, dtype=np.intp)
+ for j in range(ndim))
+ U = tuple(rng.randint(0, U_max+2, dtype=np.intp)
+ for j in range(ndim))
b_ub = min(max_int-2, sum(a*ub for a, ub in zip(A, U)))
- b = rng.randint(-1, b_ub+2)
+ b = rng.randint(-1, b_ub+2, dtype=np.intp)
if ndim == 0 and feasible_count < min_count:
b = 0
@@ -258,9 +260,9 @@ def check_may_share_memory_easy_fuzz(get_max_work, same_steps, min_count):
rng = np.random.RandomState(1234)
def random_slice(n, step):
- start = rng.randint(0, n+1)
- stop = rng.randint(start, n+1)
- if rng.randint(0, 2) == 0:
+ start = rng.randint(0, n+1, dtype=np.intp)
+ stop = rng.randint(start, n+1, dtype=np.intp)
+ if rng.randint(0, 2, dtype=np.intp) == 0:
stop, start = start, stop
step *= -1
return slice(start, stop, step)
@@ -269,12 +271,14 @@ def check_may_share_memory_easy_fuzz(get_max_work, same_steps, min_count):
infeasible = 0
while min(feasible, infeasible) < min_count:
- steps = tuple(rng.randint(1, 11) if rng.randint(0, 5) == 0 else 1
+ steps = tuple(rng.randint(1, 11, dtype=np.intp)
+ if rng.randint(0, 5, dtype=np.intp) == 0 else 1
for j in range(x.ndim))
if same_steps:
steps2 = steps
else:
- steps2 = tuple(rng.randint(1, 11) if rng.randint(0, 5) == 0 else 1
+ steps2 = tuple(rng.randint(1, 11, dtype=np.intp)
+ if rng.randint(0, 5, dtype=np.intp) == 0 else 1
for j in range(x.ndim))
t1 = np.arange(x.ndim)
@@ -374,9 +378,9 @@ def test_internal_overlap_slices():
rng = np.random.RandomState(1234)
def random_slice(n, step):
- start = rng.randint(0, n+1)
- stop = rng.randint(start, n+1)
- if rng.randint(0, 2) == 0:
+ start = rng.randint(0, n+1, dtype=np.intp)
+ stop = rng.randint(start, n+1, dtype=np.intp)
+ if rng.randint(0, 2, dtype=np.intp) == 0:
stop, start = start, stop
step *= -1
return slice(start, stop, step)
@@ -385,7 +389,8 @@ def test_internal_overlap_slices():
min_count = 5000
while cases < min_count:
- steps = tuple(rng.randint(1, 11) if rng.randint(0, 5) == 0 else 1
+ steps = tuple(rng.randint(1, 11, dtype=np.intp)
+ if rng.randint(0, 5, dtype=np.intp) == 0 else 1
for j in range(x.ndim))
t1 = np.arange(x.ndim)
rng.shuffle(t1)
@@ -469,10 +474,12 @@ def test_internal_overlap_fuzz():
rng = np.random.RandomState(1234)
while min(overlap, no_overlap) < min_count:
- ndim = rng.randint(1, 4)
+ ndim = rng.randint(1, 4, dtype=np.intp)
- strides = tuple(rng.randint(-1000, 1000) for j in range(ndim))
- shape = tuple(rng.randint(1, 30) for j in range(ndim))
+ strides = tuple(rng.randint(-1000, 1000, dtype=np.intp)
+ for j in range(ndim))
+ shape = tuple(rng.randint(1, 30, dtype=np.intp)
+ for j in range(ndim))
a = as_strided(x, strides=strides, shape=shape)
result = check_internal_overlap(a)
diff --git a/numpy/random/mtrand/mt_compat.h b/numpy/random/mtrand/mt_compat.h
new file mode 100644
index 000000000..ab56a553c
--- /dev/null
+++ b/numpy/random/mtrand/mt_compat.h
@@ -0,0 +1,68 @@
+/*
+ * This is a convenience header file providing compatibility utilities
+ * for supporting Python 2 and Python 3 in the same code base.
+ *
+ * It can be removed when Python 2.6 is dropped as PyCapsule is available
+ * in both Python 3.1+ and Python 2.7.
+ */
+
+#ifndef _MT_COMPAT_H_
+#define _MT_COMPAT_H_
+
+#include <Python.h>
+#include <numpy/npy_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * PyCObject functions adapted to PyCapsules.
+ *
+ * The main job here is to get rid of the improved error handling
+ * of PyCapsules. It's a shame...
+ */
+#if PY_VERSION_HEX >= 0x03000000
+
+static NPY_INLINE PyObject *
+NpyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *))
+{
+ PyObject *ret = PyCapsule_New(ptr, NULL, dtor);
+ if (ret == NULL) {
+ PyErr_Clear();
+ }
+ return ret;
+}
+
+static NPY_INLINE void *
+NpyCapsule_AsVoidPtr(PyObject *obj)
+{
+ void *ret = PyCapsule_GetPointer(obj, NULL);
+ if (ret == NULL) {
+ PyErr_Clear();
+ }
+ return ret;
+}
+
+#else
+
+static NPY_INLINE PyObject *
+NpyCapsule_FromVoidPtr(void *ptr, void (*dtor)(void *))
+{
+ return PyCObject_FromVoidPtr(ptr, dtor);
+}
+
+static NPY_INLINE void *
+NpyCapsule_AsVoidPtr(PyObject *ptr)
+{
+ return PyCObject_AsVoidPtr(ptr);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMPAT_H_ */
diff --git a/numpy/random/mtrand/mtrand.pyx b/numpy/random/mtrand/mtrand.pyx
index 655b708b3..110b60a9b 100644
--- a/numpy/random/mtrand/mtrand.pyx
+++ b/numpy/random/mtrand/mtrand.pyx
@@ -67,6 +67,17 @@ cdef extern from "randomkit.h":
rk_error rk_altfill(void *buffer, size_t size, int strong,
rk_state *state) nogil
double rk_gauss(rk_state *state) nogil
+ void rk_random_uint64(npy_uint64 off, npy_uint64 rng, npy_intp cnt,
+ npy_uint64 *out, rk_state *state) nogil
+ void rk_random_uint32(npy_uint32 off, npy_uint32 rng, npy_intp cnt,
+ npy_uint32 *out, rk_state *state) nogil
+ void rk_random_uint16(npy_uint16 off, npy_uint16 rng, npy_intp cnt,
+ npy_uint16 *out, rk_state *state) nogil
+ void rk_random_uint8(npy_uint8 off, npy_uint8 rng, npy_intp cnt,
+ npy_uint8 *out, rk_state *state) nogil
+ void rk_random_bool(npy_bool off, npy_bool rng, npy_intp cnt,
+ npy_bool *out, rk_state *state) nogil
+
cdef extern from "distributions.h":
# do not need the GIL, but they do need a lock on the state !! */
@@ -131,6 +142,7 @@ cimport cython
import numpy as np
import operator
import warnings
+
try:
from threading import Lock
except ImportError:
@@ -569,6 +581,304 @@ def _shape_from_size(size, d):
shape = tuple(size) + (d,)
return shape
+
+# Set up dictionary of integer types and relevant functions.
+#
+# The dictionary is keyed by dtype(...).name and the values
+# are a tuple (low, high, function), where low and high are
+# the bounds of the largest half open interval `[low, high)`
+# and the function is the relevant function to call for
+# that precision.
+#
+# The functions are all the same except for changed types in
+# a few places. It would be easy to template them.
+
+def _rand_bool(low, high, size, rngstate):
+ """
+ _rand_bool(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_bool off, rng, buf
+ cdef npy_bool *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_bool>(high - low)
+ off = <npy_bool>(low)
+ if size is None:
+ rk_random_bool(off, rng, 1, &buf, state)
+ return buf
+ else:
+ array = <ndarray>np.empty(size, np.bool_)
+ cnt = PyArray_SIZE(array)
+ out = <npy_bool *>PyArray_DATA(array)
+ with nogil:
+ rk_random_bool(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_int8(low, high, size, rngstate):
+ """
+ _rand_int8(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint8 off, rng, buf
+ cdef npy_uint8 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint8>(high - low)
+ off = <npy_uint8>(<npy_int8>low)
+ if size is None:
+ rk_random_uint8(off, rng, 1, &buf, state)
+ return <npy_int8>buf
+ else:
+ array = <ndarray>np.empty(size, np.int8)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint8 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint8(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_int16(low, high, size, rngstate):
+ """
+ _rand_int16(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint16 off, rng, buf
+ cdef npy_uint16 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint16>(high - low)
+ off = <npy_uint16>(<npy_int16>low)
+ if size is None:
+ rk_random_uint16(off, rng, 1, &buf, state)
+ return <npy_int16>buf
+ else:
+ array = <ndarray>np.empty(size, np.int16)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint16 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint16(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_int32(low, high, size, rngstate):
+ """
+ _rand_int32(self, low, high, size, rngstate)
+
+ Return random np.int32 integers between `low` and `high`, inclusive.
+
+ Return random integers from the "discrete uniform" distribution in the
+ closed interval [`low`, `high`). If `high` is None (the default),
+ then results are from [0, `low`). On entry the arguments are presumed
+ to have been validated for size and order for the np.int32 type.
+
+ Parameters
+ ----------
+ low : int
+ Lowest (signed) integer to be drawn from the distribution (unless
+ ``high=None``, in which case this parameter is the *highest* such
+ integer).
+ high : int
+ If provided, the largest (signed) integer to be drawn from the
+ distribution (see above for behavior if ``high=None``).
+ size : int or tuple of ints
+ Output shape. If the given shape is, e.g., ``(m, n, k)``, then
+ ``m * n * k`` samples are drawn. Default is None, in which case a
+ single value is returned.
+ rngstate : encapsulated pointer to rk_state
+ The specific type depends on the python version. In Python 2 it is
+ a PyCObject, in Python 3 a PyCapsule object.
+
+ Returns
+ -------
+ out : python scalar or ndarray of np.int32
+ `size`-shaped array of random integers from the appropriate
+ distribution, or a single such random int if `size` not provided.
+
+ """
+ cdef npy_uint32 off, rng, buf
+ cdef npy_uint32 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint32>(high - low)
+ off = <npy_uint32>(<npy_int32>low)
+ if size is None:
+ rk_random_uint32(off, rng, 1, &buf, state)
+ return <npy_int32>buf
+ else:
+ array = <ndarray>np.empty(size, np.int32)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint32 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint32(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_int64(low, high, size, rngstate):
+ """
+ _rand_int64(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint64 off, rng, buf
+ cdef npy_uint64 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint64>(high - low)
+ off = <npy_uint64>(<npy_int64>low)
+ if size is None:
+ rk_random_uint64(off, rng, 1, &buf, state)
+ return <npy_int64>buf
+ else:
+ array = <ndarray>np.empty(size, np.int64)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint64 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint64(off, rng, cnt, out, state)
+ return array
+
+def _rand_uint8(low, high, size, rngstate):
+ """
+ _rand_uint8(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint8 off, rng, buf
+ cdef npy_uint8 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint8>(high - low)
+ off = <npy_uint8>(low)
+ if size is None:
+ rk_random_uint8(off, rng, 1, &buf, state)
+ return buf
+ else:
+ array = <ndarray>np.empty(size, np.uint8)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint8 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint8(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_uint16(low, high, size, rngstate):
+ """
+ _rand_uint16(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint16 off, rng, buf
+ cdef npy_uint16 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint16>(high - low)
+ off = <npy_uint16>(low)
+ if size is None:
+ rk_random_uint16(off, rng, 1, &buf, state)
+ return buf
+ else:
+ array = <ndarray>np.empty(size, np.uint16)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint16 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint16(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_uint32(low, high, size, rngstate):
+ """
+ _rand_uint32(self, low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint32 off, rng, buf
+ cdef npy_uint32 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint32>(high - low)
+ off = <npy_uint32>(low)
+ if size is None:
+ rk_random_uint32(off, rng, 1, &buf, state)
+ return <npy_uint32>buf
+ else:
+ array = <ndarray>np.empty(size, np.uint32)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint32 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint32(off, rng, cnt, out, state)
+ return array
+
+
+def _rand_uint64(low, high, size, rngstate):
+ """
+ _rand_uint64(low, high, size, rngstate)
+
+ See `_rand_int32` for documentation, only the return type changes.
+
+ """
+ cdef npy_uint64 off, rng, buf
+ cdef npy_uint64 *out
+ cdef ndarray array "arrayObject"
+ cdef npy_intp cnt
+ cdef rk_state *state = <rk_state *>NpyCapsule_AsVoidPtr(rngstate)
+
+ rng = <npy_uint64>(high - low)
+ off = <npy_uint64>(low)
+ if size is None:
+ rk_random_uint64(off, rng, 1, &buf, state)
+ return <npy_uint64>buf
+ else:
+ array = <ndarray>np.empty(size, np.uint64)
+ cnt = PyArray_SIZE(array)
+ out = <npy_uint64 *>PyArray_DATA(array)
+ with nogil:
+ rk_random_uint64(off, rng, cnt, out, state)
+ return array
+
+# Look up table for randint functions keyed by type name. The stored data
+# is a tuple (lbnd, ubnd, func), where lbnd is the smallest value for the
+# type, ubnd is one greater than the largest value, and func is the
+# function to call.
+_randint_type = {
+ 'bool': (0, 2, _rand_bool),
+ 'int8': (-2**7, 2**7, _rand_int8),
+ 'int16': (-2**15, 2**15, _rand_int16),
+ 'int32': (-2**31, 2**31, _rand_int32),
+ 'int64': (-2**63, 2**63, _rand_int64),
+ 'uint8': (0, 2**8, _rand_uint8),
+ 'uint16': (0, 2**16, _rand_uint16),
+ 'uint32': (0, 2**32, _rand_uint32),
+ 'uint64': (0, 2**64, _rand_uint64)
+ }
+
+
cdef class RandomState:
"""
RandomState(seed=None)
@@ -613,11 +923,12 @@ cdef class RandomState:
"""
cdef rk_state *internal_state
cdef object lock
+ cdef object state_address
poisson_lam_max = np.iinfo('l').max - np.sqrt(np.iinfo('l').max)*10
def __init__(self, seed=None):
self.internal_state = <rk_state*>PyMem_Malloc(sizeof(rk_state))
-
+ self.state_address = NpyCapsule_FromVoidPtr(self.internal_state, NULL)
self.lock = Lock()
self.seed(seed)
@@ -880,15 +1191,15 @@ cdef class RandomState:
"""
return disc0_array(self.internal_state, rk_long, size, self.lock)
- def randint(self, low, high=None, size=None):
+ def randint(self, low, high=None, size=None, dtype='l'):
"""
- randint(low, high=None, size=None)
+ randint(low, high=None, size=None, dtype='l')
Return random integers from `low` (inclusive) to `high` (exclusive).
- Return random integers from the "discrete uniform" distribution in the
- "half-open" interval [`low`, `high`). If `high` is None (the default),
- then results are from [0, `low`).
+ Return random integers from the "discrete uniform" distribution of
+ the specified dtype in the "half-open" interval [`low`, `high`). If
+ `high` is None (the default), then results are from [0, `low`).
Parameters
----------
@@ -903,6 +1214,13 @@ cdef class RandomState:
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
``m * n * k`` samples are drawn. Default is None, in which case a
single value is returned.
+ dtype : dtype, optional
+ Desired dtype of the result. All dtypes are determined by their
+ name, i.e., 'int64', 'int`, etc, so byteorder is not available
+ and a specific precision may have different C types depending
+ on the platform. The default value is 'l' (C long).
+
+ .. versionadded:: 1.11.0
Returns
-------
@@ -931,14 +1249,24 @@ cdef class RandomState:
[3, 2, 2, 0]])
"""
- if high is not None and low >= high:
- raise ValueError("low >= high")
-
if high is None:
high = low
low = 0
- return self.random_integers(low, high - 1, size)
+ key = np.dtype(dtype).name
+ if not key in _randint_type:
+ raise TypeError('Unsupported dtype "%s" for randint' % key)
+ lowbnd, highbnd, randfunc = _randint_type[key]
+
+ if low < lowbnd:
+ raise ValueError("low is out of bounds for %s" % (key,))
+ if high > highbnd:
+ raise ValueError("high is out of bounds for %s" % (key,))
+ if low >= high:
+ raise ValueError("low >= high")
+
+ with self.lock:
+ return randfunc(low, high - 1, size, self.state_address)
def bytes(self, npy_intp length):
"""
@@ -1351,11 +1679,13 @@ cdef class RandomState:
"""
random_integers(low, high=None, size=None)
- Return random integers between `low` and `high`, inclusive.
+ Random integers of type np.int between `low` and `high`, inclusive.
- Return random integers from the "discrete uniform" distribution in the
- closed interval [`low`, `high`]. If `high` is None (the default),
- then results are from [1, `low`].
+ Return random integers of type np.int from the "discrete uniform"
+ distribution in the closed interval [`low`, `high`]. If `high` is
+ None (the default), then results are from [1, `low`]. The np.int
+ type translates to the C long type used by Python 2 for "short"
+ integers and its precision is platform dependent.
Parameters
----------
@@ -1421,37 +1751,13 @@ cdef class RandomState:
>>> plt.show()
"""
- if high is not None and low > high:
- raise ValueError("low > high")
+ if high is None:
+ high = low
+ low = 1
- cdef long lo, hi, rv
- cdef unsigned long diff
- cdef long *array_data
- cdef ndarray array "arrayObject"
- cdef npy_intp length
- cdef npy_intp i
+ return self.randint(low, high + 1, size=size, dtype='l')
- if high is None:
- lo = 1
- hi = low
- else:
- lo = low
- hi = high
- diff = <unsigned long>hi - <unsigned long>lo
- if size is None:
- with self.lock:
- rv = lo + <long>rk_interval(diff, self. internal_state)
- return rv
- else:
- array = <ndarray>np.empty(size, int)
- length = PyArray_SIZE(array)
- array_data = <long *>PyArray_DATA(array)
- with self.lock, nogil:
- for i from 0 <= i < length:
- rv = lo + <long>rk_interval(diff, self. internal_state)
- array_data[i] = rv
- return array
# Complicated, continuous distributions:
def standard_normal(self, size=None):
diff --git a/numpy/random/mtrand/numpy.pxd b/numpy/random/mtrand/numpy.pxd
index c54f79c0a..488278d6c 100644
--- a/numpy/random/mtrand/numpy.pxd
+++ b/numpy/random/mtrand/numpy.pxd
@@ -2,6 +2,12 @@
cdef extern from "numpy/npy_no_deprecated_api.h": pass
+cdef extern from "mt_compat.h":
+
+ object NpyCapsule_FromVoidPtr(void *ptr, void (*dtor)(object o))
+ void * NpyCapsule_AsVoidPtr(object o)
+
+
cdef extern from "numpy/arrayobject.h":
cdef enum NPY_TYPES:
@@ -71,7 +77,17 @@ cdef extern from "numpy/arrayobject.h":
double real
double imag
+ ctypedef int npy_int
ctypedef int npy_intp
+ ctypedef int npy_int64
+ ctypedef int npy_uint64
+ ctypedef int npy_int32
+ ctypedef int npy_uint32
+ ctypedef int npy_int16
+ ctypedef int npy_uint16
+ ctypedef int npy_int8
+ ctypedef int npy_uint8
+ ctypedef int npy_bool
ctypedef extern class numpy.dtype [object PyArray_Descr]: pass
diff --git a/numpy/random/mtrand/randomkit.c b/numpy/random/mtrand/randomkit.c
index b18897e2c..3a95efeeb 100644
--- a/numpy/random/mtrand/randomkit.c
+++ b/numpy/random/mtrand/randomkit.c
@@ -70,6 +70,7 @@
#include <errno.h>
#include <limits.h>
#include <math.h>
+#include <assert.h>
#ifdef _WIN32
/*
@@ -115,6 +116,10 @@
#include <unistd.h>
#endif
+/*
+ * Do not move this include. randomkit.h must be included
+ * after windows timeb.h is included.
+ */
#include "randomkit.h"
#ifndef RK_DEV_URANDOM
@@ -207,7 +212,11 @@ rk_randomseed(rk_state *state)
#define UPPER_MASK 0x80000000UL
#define LOWER_MASK 0x7fffffffUL
-/* Slightly optimised reference implementation of the Mersenne Twister */
+/*
+ * Slightly optimised reference implementation of the Mersenne Twister
+ * Note that regardless of the precision of long, only 32 bit random
+ * integers are produced
+ */
unsigned long
rk_random(rk_state *state)
{
@@ -240,6 +249,219 @@ rk_random(rk_state *state)
return y;
}
+
+/*
+ * Returns an unsigned 64 bit random integer.
+ */
+NPY_INLINE static npy_uint64
+rk_uint64(rk_state *state)
+{
+ npy_uint64 upper = (npy_uint64)rk_random(state) << 32;
+ npy_uint64 lower = (npy_uint64)rk_random(state);
+ return upper | lower;
+}
+
+
+/*
+ * Returns an unsigned 32 bit random integer.
+ */
+NPY_INLINE static npy_uint32
+rk_uint32(rk_state *state)
+{
+ return (npy_uint32)rk_random(state);
+}
+
+
+/*
+ * Fills an array with cnt random npy_uint64 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+void
+rk_random_uint64(npy_uint64 off, npy_uint64 rng, npy_intp cnt,
+ npy_uint64 *out, rk_state *state)
+{
+ npy_uint64 val, mask = rng;
+ npy_intp i;
+
+ if (rng == 0) {
+ for (i = 0; i < cnt; i++) {
+ out[i] = off;
+ }
+ return;
+ }
+
+ /* Smallest bit mask >= max */
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+ mask |= mask >> 16;
+ mask |= mask >> 32;
+
+ for (i = 0; i < cnt; i++) {
+ if (rng <= 0xffffffffUL) {
+ while ((val = (rk_uint32(state) & mask)) > rng);
+ }
+ else {
+ while ((val = (rk_uint64(state) & mask)) > rng);
+ }
+ out[i] = off + val;
+ }
+}
+
+
+/*
+ * Fills an array with cnt random npy_uint32 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+void
+rk_random_uint32(npy_uint32 off, npy_uint32 rng, npy_intp cnt,
+ npy_uint32 *out, rk_state *state)
+{
+ npy_uint32 val, mask = rng;
+ npy_intp i;
+
+ if (rng == 0) {
+ for (i = 0; i < cnt; i++) {
+ out[i] = off;
+ }
+ return;
+ }
+
+ /* Smallest bit mask >= max */
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+ mask |= mask >> 16;
+
+ for (i = 0; i < cnt; i++) {
+ while ((val = (rk_uint32(state) & mask)) > rng);
+ out[i] = off + val;
+ }
+}
+
+
+/*
+ * Fills an array with cnt random npy_uint16 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+void
+rk_random_uint16(npy_uint16 off, npy_uint16 rng, npy_intp cnt,
+ npy_uint16 *out, rk_state *state)
+{
+ npy_uint16 val, mask = rng;
+ npy_intp i;
+ npy_uint32 buf;
+ int bcnt = 0;
+
+ if (rng == 0) {
+ for (i = 0; i < cnt; i++) {
+ out[i] = off;
+ }
+ return;
+ }
+
+ /* Smallest bit mask >= max */
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+
+ for (i = 0; i < cnt; i++) {
+ do {
+ if (!bcnt) {
+ buf = rk_uint32(state);
+ bcnt = 1;
+ }
+ else {
+ buf >>= 16;
+ bcnt--;
+ }
+ val = (npy_uint16)buf & mask;
+ } while (val > rng);
+ out[i] = off + val;
+ }
+}
+
+
+/*
+ * Fills an array with cnt random npy_uint8 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+void
+rk_random_uint8(npy_uint8 off, npy_uint8 rng, npy_intp cnt,
+ npy_uint8 *out, rk_state *state)
+{
+ npy_uint8 val, mask = rng;
+ npy_intp i;
+ npy_uint32 buf;
+ int bcnt = 0;
+
+ if (rng == 0) {
+ for (i = 0; i < cnt; i++) {
+ out[i] = off;
+ }
+ return;
+ }
+
+ /* Smallest bit mask >= max */
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+
+ for (i = 0; i < cnt; i++) {
+ do {
+ if (!bcnt) {
+ buf = rk_uint32(state);
+ bcnt = 3;
+ }
+ else {
+ buf >>= 8;
+ bcnt--;
+ }
+ val = (npy_uint8)buf & mask;
+ } while (val > rng);
+ out[i] = off + val;
+ }
+}
+
+
+/*
+ * Fills an array with cnt random npy_bool between off and off + rng
+ * inclusive.
+ */
+void
+rk_random_bool(npy_bool off, npy_bool rng, npy_intp cnt,
+ npy_bool *out, rk_state *state)
+{
+ npy_intp i;
+ npy_uint32 buf;
+ int bcnt = 0;
+
+ if (rng == 0) {
+ for (i = 0; i < cnt; i++) {
+ out[i] = off;
+ }
+ return;
+ }
+
+ /* If we reach here rng and mask are one and off is zero */
+ assert(rng == 1 && off == 0);
+ for (i = 0; i < cnt; i++) {
+ if (!bcnt) {
+ buf = rk_uint32(state);
+ bcnt = 31;
+ }
+ else {
+ buf >>= 1;
+ bcnt--;
+ }
+ out[i] = (buf & 0x00000001) != 0;
+ }
+}
+
+
long
rk_long(rk_state *state)
{
diff --git a/numpy/random/mtrand/randomkit.h b/numpy/random/mtrand/randomkit.h
index e049488ee..fcdd606a1 100644
--- a/numpy/random/mtrand/randomkit.h
+++ b/numpy/random/mtrand/randomkit.h
@@ -56,11 +56,13 @@
* defaults to "/dev/urandom"
*/
-#include <stddef.h>
-
#ifndef _RANDOMKIT_
#define _RANDOMKIT_
+#include <stddef.h>
+#include <numpy/npy_common.h>
+
+
#define RK_STATE_LEN 624
typedef struct rk_state_
@@ -149,6 +151,41 @@ extern unsigned long rk_ulong(rk_state *state);
extern unsigned long rk_interval(unsigned long max, rk_state *state);
/*
+ * Fills an array with cnt random npy_uint64 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+extern void rk_random_uint64(npy_uint64 off, npy_uint64 rng, npy_intp cnt,
+ npy_uint64 *out, rk_state *state);
+
+/*
+ * Fills an array with cnt random npy_uint32 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+extern void rk_random_uint32(npy_uint32 off, npy_uint32 rng, npy_intp cnt,
+ npy_uint32 *out, rk_state *state);
+
+/*
+ * Fills an array with cnt random npy_uint16 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+extern void rk_random_uint16(npy_uint16 off, npy_uint16 rng, npy_intp cnt,
+ npy_uint16 *out, rk_state *state);
+
+/*
+ * Fills an array with cnt random npy_uint8 between off and off + rng
+ * inclusive. The numbers wrap if rng is sufficiently large.
+ */
+extern void rk_random_uint8(npy_uint8 off, npy_uint8 rng, npy_intp cnt,
+ npy_uint8 *out, rk_state *state);
+
+/*
+ * Fills an array with cnt random npy_bool between off and off + rng
+ * inclusive. It is assumed tha npy_bool as the same size as npy_uint8.
+ */
+extern void rk_random_bool(npy_bool off, npy_bool rng, npy_intp cnt,
+ npy_bool *out, rk_state *state);
+
+/*
* Returns a random double between 0.0 and 1.0, 1.0 excluded.
*/
extern double rk_double(rk_state *state);
diff --git a/numpy/random/tests/test_random.py b/numpy/random/tests/test_random.py
index c3aa43f0e..a6783fe8f 100644
--- a/numpy/random/tests/test_random.py
+++ b/numpy/random/tests/test_random.py
@@ -128,6 +128,83 @@ class TestSetState(TestCase):
# arguments without truncation.
self.prng.negative_binomial(0.5, 0.5)
+class TestRandint(TestCase):
+
+ rfunc = np.random.randint
+
+ # valid integer/boolean types
+ itype = [np.bool, np.int8, np.uint8, np.int16, np.uint16,
+ np.int32, np.uint32, np.int64, np.uint64]
+
+ def test_unsupported_type(self):
+ assert_raises(TypeError, self.rfunc, 1, dtype=np.float)
+
+ def test_bounds_checking(self):
+ for dt in self.itype:
+ lbnd = 0 if dt is np.bool else np.iinfo(dt).min
+ ubnd = 2 if dt is np.bool else np.iinfo(dt).max + 1
+ assert_raises(ValueError, self.rfunc, lbnd - 1 , ubnd, dtype=dt)
+ assert_raises(ValueError, self.rfunc, lbnd , ubnd + 1, dtype=dt)
+ assert_raises(ValueError, self.rfunc, ubnd , lbnd, dtype=dt)
+ assert_raises(ValueError, self.rfunc, 1 , 0, dtype=dt)
+
+ def test_rng_zero_and_extremes(self):
+ for dt in self.itype:
+ lbnd = 0 if dt is np.bool else np.iinfo(dt).min
+ ubnd = 2 if dt is np.bool else np.iinfo(dt).max + 1
+ tgt = ubnd - 1
+ assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
+ tgt = lbnd
+ assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
+ tgt = (lbnd + ubnd)//2
+ assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
+
+ def test_in_bounds_fuzz(self):
+ # Don't use fixed seed
+ np.random.seed()
+ for dt in self.itype[1:]:
+ for ubnd in [4, 8, 16]:
+ vals = self.rfunc(2, ubnd, size=2**16, dtype=dt)
+ assert_(vals.max() < ubnd)
+ assert_(vals.min() >= 2)
+ vals = self.rfunc(0, 2, size=2**16, dtype=np.bool)
+ assert_(vals.max() < 2)
+ assert_(vals.min() >= 0)
+
+ def test_repeatability(self):
+ import hashlib
+ # We use a md5 hash of generated sequences of 1000 samples
+ # in the range [0, 6) for all but np.bool, where the range
+ # is [0, 2). Hashes are for little endian numbers.
+ tgt = {'bool': '7dd3170d7aa461d201a65f8bcf3944b0',
+ 'int16': '1b7741b80964bb190c50d541dca1cac1',
+ 'int32': '4dc9fcc2b395577ebb51793e58ed1a05',
+ 'int64': '17db902806f448331b5a758d7d2ee672',
+ 'int8': '27dd30c4e08a797063dffac2490b0be6',
+ 'uint16': '1b7741b80964bb190c50d541dca1cac1',
+ 'uint32': '4dc9fcc2b395577ebb51793e58ed1a05',
+ 'uint64': '17db902806f448331b5a758d7d2ee672',
+ 'uint8': '27dd30c4e08a797063dffac2490b0be6'}
+
+ for dt in self.itype[1:]:
+ np.random.seed(1234)
+
+ # view as little endian for hash
+ if sys.byteorder == 'little':
+ val = self.rfunc(0, 6, size=1000, dtype=dt)
+ else:
+ val = self.rfunc(0, 6, size=1000, dtype=dt).byteswap()
+
+ res = hashlib.md5(val.view(np.int8)).hexdigest()
+ assert_(tgt[np.dtype(dt).name] == res)
+
+ # bools do not depend on endianess
+ np.random.seed(1234)
+ val = self.rfunc(0, 2, size=1000, dtype=np.bool).view(np.int8)
+ res = hashlib.md5(val).hexdigest()
+ assert_(tgt[np.dtype(np.bool).name] == res)
+
+
class TestRandomDist(TestCase):
# Make sure the random distribution returns the correct value for a
# given seed
diff --git a/tools/travis-test.sh b/tools/travis-test.sh
index 939594d8c..40e266b26 100755
--- a/tools/travis-test.sh
+++ b/tools/travis-test.sh
@@ -50,8 +50,10 @@ setup_base()
| grep -E "warning\>" \
| tee warnings
# Check for an acceptable number of warnings. Some warnings are out of
- # our control, so adjust the number as needed.
- [[ $(wc -l < warnings) -lt 1 ]]
+ # our control, so adjust the number as needed. At the moment a
+ # cython generated code produces a warning about '-2147483648L', but
+ # the code seems to compile OK.
+ [[ $(wc -l < warnings) -lt 2 ]]
fi
else
sysflags="$($PYTHON -c "from distutils import sysconfig; \