8000 ENH: Allow size=0 in numpy.random.choice, regardless of array · Pull Request #8717 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Allow size=0 in numpy.random.choice, regardless of array #8717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release/1.13.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ array, in the same way that ``sort`` already did. Additionally, the
Note that this argument is not added at the end, so breaks any code that
passed ``fill_value`` as a positional argument.

``randint`` and ``choice`` now work on empty distributions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Even when no elements needed to be drawn, ``np.random.randint`` and
``np.random.choice`` raised an error when the arguments described an empty
distribution. This has been fixed so that e.g.
``np.random.choice([],0) == np.array([],dtype=float64)``.
Bundled version of LAPACK is now 3.2.2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline needed here

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NumPy comes bundled with a minimal implementation of lapack for systems without
Expand Down
14 changes: 7 additions & 7 deletions numpy/random/mtrand/mtrand.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

include "Python.pxi"
include "randint_helpers.pxi"
include "numpy.pxd"
include "randint_helpers.pxi"
Copy link
Member
@eric-wieser eric-wieser Mar 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be after include "numpy.pxd" to have access to the numpy types

include "cpython/pycapsule.pxd"

from libc cimport string
Expand Down Expand Up @@ -973,8 +973,8 @@ cdef class RandomState:
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")
if low >= high and np.prod(size) != 0:
raise ValueError("Range cannot be empty (low >= high) unless no samples are taken")

with self.lock:
ret = randfunc(low, high - 1, size, self.state_address)
Expand Down Expand Up @@ -1100,14 +1100,14 @@ cdef class RandomState:
pop_size = operator.index(a.item())
except TypeError:
raise ValueError("a must be 1-dimensional or an integer")
if pop_size <= 0:
raise ValueError("a must be greater than 0")
if pop_size <= 0 and np.prod(size) != 0:
raise ValueError("a must be greater than 0 unless no samples are taken")
elif a.ndim != 1:
raise ValueError("a must be 1-dimensional")
else:
pop_size = a.shape[0]
if pop_size is 0:
raise ValueError("a must be non-empty")
if pop_size is 0 and np.prod(size) != 0:
raise ValueError("a cannot be empty unless no samples are taken")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps "a cannot be empty unless no samples are taken"


if p is not None:
d = len(p)
Expand Down
6 changes: 3 additions & 3 deletions numpy/random/mtrand/randint_helpers.pxi.in
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def get_dispatch(dtypes):

{{for npy_dt, npy_udt, np_dt in get_dispatch(dtypes)}}

def _rand_{{npy_dt}}(low, high, size, rngstate):
def _rand_{{npy_dt}}(npy_{{npy_dt}} low, npy_{{npy_dt}} high, size, rngstate):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well enforce casting here

"""
_rand_{{npy_dt}}(low, high, size, rngstate)

Expand Down Expand Up @@ -60,8 +60,8 @@ def _rand_{{npy_dt}}(low, high, size, rngstate):
cdef npy_intp cnt
cdef rk_state *state = <rk_state *>PyCapsule_GetPointer(rngstate, NULL)

rng = <npy_{{npy_udt}}>(high - low)
off = <npy_{{npy_udt}}>(<npy_{{npy_dt}}>low)
off = <npy_{{npy_udt}}>(low)
rng = <npy_{{npy_udt}}>(high) - <npy_{{npy_udt}}>(low)
Copy link
Member
@eric-wieser eric-wieser Mar 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high - low can produce a result that doesn't fit in npy_dt, and cause signed overflow (which is undefined in C - not sure about Cython). Unsigned overflow, however, is well-defined, and does the right thing.


if size is None:
rk_random_{{npy_udt}}(off, rng, 1, &buf, state)
Expand Down
8 changes: 8 additions & 0 deletions numpy/random/tests/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,14 @@ def test_choice_return_shape(self):
assert_equal(np.random.choice(6, s, replace=False, p=p).shape, s)
assert_equal(np.random.choice(np.arange(6), s, replace=True).shape, s)

# Check zero-size
assert_equal(np.random.randint(0,0,(3,0,4)).shape, (3,0,4))
assert_equal(np.random.randint(0,-10,0).shape, (0,))
assert_equal(np.random.choice(0,0).shape, (0,))
assert_equal(np.random.choice([],(0,)).shape, (0,))
assert_equal(np.random.choice(['a', 'b'], size=(3, 0, 4)).shape, (3, 0, 4))
assert_raises(ValueError, np.random.choice, [], 10)

def test_bytes(self):
np.random.seed(self.seed)
actual = np.random.bytes(10)
Expand Down
0