-
Notifications
You must be signed in to change notification settings - Fork 79
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
cunumeric.random - sort and shuffle-based operations #559
base: branch-24.03
Are you sure you want to change the base?
Changes from 6 commits
1a6a5fb
e057a8d
4ab65e6
036efd5
20d3364
b3ed39b
ebf4590
1931d6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -20,6 +20,7 @@ | |||||
from cunumeric.array import ndarray | ||||||
from cunumeric.runtime import runtime | ||||||
|
||||||
import cunumeric.sort | ||||||
from cunumeric.random import generator | ||||||
|
||||||
if TYPE_CHECKING: | ||||||
|
@@ -216,6 +217,96 @@ def chisquare( | |||||
return generator.get_static_generator().chisquare(df, size, dtype) | ||||||
|
||||||
|
||||||
def choice( | ||||||
a: Union[int, ndarray], | ||||||
size: Union[NdShapeLike, None] = None, | ||||||
replace: bool = True, | ||||||
p: Union[None, ndarray] = None, | ||||||
) -> Union[int, ndarray, npt.NDArray[Any]]: | ||||||
""" | ||||||
Returns an array of random values from a given 1-D array. | ||||||
Each element of the returned array is independently sampled | ||||||
from ``a`` according to ``p`` or uniformly. | ||||||
|
||||||
Note | ||||||
---- | ||||||
Currently ``p`` is not supported when ``replace=False``. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
a : ndarray or int : If an array-like, | ||||||
a random sample is generated from its elements. | ||||||
If an int, the random sample is generated as if ``a`` was | ||||||
``np.arange(n)`` | ||||||
size : int or tuple of ints : The shape of the array. | ||||||
replace (boolean): Whether the sample is with or without replacement. | ||||||
p : 1-D array-like : | ||||||
The probabilities associated with each entry in ``a``. | ||||||
If not given the sample assumes a uniform distribution over all | ||||||
entries in ``a``. | ||||||
|
||||||
Returns | ||||||
------- | ||||||
out : ndarray : The generated random samples. | ||||||
|
||||||
|
||||||
Raises | ||||||
------ | ||||||
ValueError | ||||||
If a is an int and less than zero, if a or p are not 1-dimensional, | ||||||
if a is an array-like of size 0, if p is not a vector of | ||||||
probabilities, if a and p have different lengths, or if | ||||||
replace=False and the sample size is greater than the population | ||||||
size | ||||||
|
||||||
See Also | ||||||
-------- | ||||||
numpy.random.choice | ||||||
|
||||||
Availability | ||||||
-------- | ||||||
Multiple GPUs, Multiple CPUs | ||||||
""" | ||||||
|
||||||
if isinstance(a, int) and a <= 0: | ||||||
raise ValueError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Numpy includes a specific message for the exception Input In [3], in <cell line: 1>()
----> 1 np.random.choice(a, -1)
File mtrand.pyx:962, in numpy.random.mtrand.RandomState.choice()
File mtrand.pyx:748, in numpy.random.mtrand.RandomState.randint()
File _bounded_integers.pyx:1256, in numpy.random._bounded_integers._rand_int64()
ValueError: negative dimensions are not allowed |
||||||
if not isinstance(size, int): | ||||||
raise ValueError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Numpy lets a In [4]: np.random.choice(a, 2, p=3.4)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 np.random.choice(a, 2, p=3.4)
File mtrand.pyx:918, in numpy.random.mtrand.RandomState.choice()
TypeError: object of type 'float' has no len() we could explicitly raise our own TypeError here if it doesn't arise naturally in our codepaths, but we should match exception type with Numpy in any case. |
||||||
if p is None: | ||||||
if isinstance(a, int): | ||||||
if replace: | ||||||
return cunumeric.random.randint(0, a, size) | ||||||
else: | ||||||
if a < size: | ||||||
raise ValueError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Numpy has a descriptive error message In [9]: np.random.choice(5, size=8, replace=False)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [9], in <cell line: 1>()
----> 1 np.random.choice(5, size=8, replace=False)
File mtrand.pyx:965, in numpy.random.mtrand.RandomState.choice()
ValueError: Cannot take a larger sample than population when 'replace=False' |
||||||
return cunumeric.random.permutation(a)[:size] | ||||||
else: | ||||||
if replace: | ||||||
indices = cunumeric.random.randint(0, len(a), size) | ||||||
return a[indices] | ||||||
else: | ||||||
if len(a) < size: | ||||||
raise ValueError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above:
|
||||||
return cunumeric.random.permutation(a)[:size] | ||||||
else: | ||||||
if not replace: | ||||||
raise ValueError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
cump = p.cumsum() | ||||||
# check if p is a probability distribution | ||||||
if abs(cump[-1] - 1.0) > len(p) * np.finfo(p.dtype).eps: | ||||||
# does not sum up to 1 | ||||||
raise ValueError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From Numpy:
|
||||||
cump[-1] = 1.0 # fix rounding issues | ||||||
# draw uniforms | ||||||
uni = cunumeric.random.uniform(0.0, 1.0, size) | ||||||
# inverse distribution | ||||||
idx = cunumeric.searchsorted(cump, uni, "right") | ||||||
# return samples | ||||||
if isinstance(a, int): | ||||||
return cunumeric.arange(a)[idx] | ||||||
return a[idx] | ||||||
|
||||||
|
||||||
def exponential( | ||||||
scale: float = 1.0, | ||||||
size: Union[NdShapeLike, None] = None, | ||||||
|
@@ -924,6 +1015,40 @@ def pareto( | |||||
return generator.get_static_generator().pareto(a, size, dtype) | ||||||
|
||||||
|
||||||
def permutation(x: Union[int, ndarray]) -> ndarray: | ||||||
""" | ||||||
Returns a permuted range or a permutation of an array. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
a : (int or ndarray): The range or the array to be shuffled. | ||||||
|
||||||
Returns | ||||||
------- | ||||||
out: If `a` is an integer, it is permutation range between 0 | ||||||
and `a` - 1. | ||||||
Otherwise, it is a permutation of `a`. | ||||||
|
||||||
See Also | ||||||
-------- | ||||||
numpy.random.permutation | ||||||
|
||||||
Availability | ||||||
-------- | ||||||
Multiple GPUs, Multiple CPUs | ||||||
""" | ||||||
if isinstance(x, int): | ||||||
count = x | ||||||
else: | ||||||
count = len(x) | ||||||
key = uniform(0.0, 1.0, count) | ||||||
indices = cunumeric.argsort(key) | ||||||
if isinstance(x, int): | ||||||
return indices | ||||||
else: | ||||||
return x[indices] | ||||||
bryevdv marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
|
||||||
def poisson( | ||||||
lam: float = 1.0, size: Union[NdShapeLike, None] = None | ||||||
) -> ndarray: | ||||||
|
@@ -1277,6 +1402,26 @@ def rayleigh( | |||||
sample = random_sample | ||||||
|
||||||
|
||||||
def shuffle(a: ndarray) -> None: | ||||||
""" | ||||||
Shuffles an array. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
a : (ndarray): The array to be shuffled. | ||||||
|
||||||
See Also | ||||||
-------- | ||||||
numpy.random.shuffle | ||||||
|
||||||
Availability | ||||||
-------- | ||||||
Multiple GPUs, Multiple CPUs | ||||||
""" | ||||||
b = cunumeric.random.permutation(a) | ||||||
a[:] = b | ||||||
|
||||||
|
||||||
def standard_cauchy( | ||||||
size: Union[NdShapeLike, None] = None, | ||||||
dtype: npt.DTypeLike = np.float64, | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# Copyright 2022 NVIDIA Corporation | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
import numpy as np | ||
import pytest | ||
|
||
import cunumeric as num | ||
|
||
|
||
def test_permutation_int(): | ||
count = 1024 | ||
p = num.random.permutation(count) | ||
p.sort() | ||
assert num.linalg.norm(p - np.arange(count)) == 0.0 | ||
|
||
|
||
def test_permutation_array(): | ||
count = 1024 | ||
x = num.arange(count) | ||
p = num.random.permutation(x) | ||
assert num.linalg.norm(x - p) != 0.0 | ||
p.sort() | ||
assert num.linalg.norm(x - p) == 0.0 | ||
|
||
|
||
def test_shuffle(): | ||
count = 16 | ||
p = num.arange(count) | ||
x = num.arange(count) | ||
num.random.shuffle(x) | ||
assert num.linalg.norm(x - p) != 0.0 | ||
x.sort() | ||
assert num.linalg.norm(x - p) == 0.0 | ||
|
||
|
||
def test_choice_1(maxvalue=1024, count=42): | ||
a = num.random.choice(maxvalue, count) | ||
assert len(a) == count | ||
assert num.amax(a) <= maxvalue | ||
assert num.amin(a) >= 0 | ||
|
||
|
||
def test_choice_2(maxvalue=1024, count=42): | ||
bryevdv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
a = num.random.choice(maxvalue, count, False) | ||
assert len(a) == count | ||
assert num.amax(a) <= maxvalue | ||
assert num.amin(a) >= 0 | ||
for i in range(count): | ||
for j in range(count): | ||
if i == j: | ||
continue | ||
assert a[i] != a[j] | ||
|
||
|
||
def test_choice_3(maxvalue=1024, count=42): | ||
values = num.random.random_integers(0, maxvalue, maxvalue) | ||
|
||
a = num.random.choice(values, count) | ||
assert len(a) == count | ||
assert num.amax(a) <= num.amax(values) | ||
assert num.amin(a) >= num.amin(values) | ||
|
||
|
||
def test_choice_4(maxvalue=1024, count=42): | ||
values = num.arange(maxvalue) | ||
|
||
a = num.random.choice(values, count, False) | ||
assert len(a) == count | ||
assert num.amax(a) <= num.amax(values) | ||
assert num.amin(a) >= num.amin(values) | ||
for i in range(count): | ||
for j in range(count): | ||
if i == j: | ||
continue | ||
assert a[i] != a[j] | ||
|
||
|
||
def test_choice_5(maxvalue=1024, count=42): | ||
values = num.arange(maxvalue) | ||
|
||
p = num.random.uniform(0, 1, maxvalue) | ||
p /= p.sum() | ||
|
||
a = num.random.choice(values, count, True, p) | ||
assert len(a) == count | ||
assert num.amax(a) <= num.amax(values) | ||
assert num.amin(a) >= num.amin(values) | ||
|
||
|
||
def test_choice_6(maxvalue=1024, count=42): | ||
values = num.random.random_integers(0, maxvalue, maxvalue) | ||
|
||
p = num.random.uniform(0, 1, maxvalue) | ||
p /= p.sum() | ||
|
||
a = num.random.choice(values, count, True, p) | ||
assert len(a) == count | ||
assert num.amax(a) <= num.amax(values) | ||
assert num.amin(a) >= num.amin(values) | ||
|
||
|
||
if __name__ == "__main__": | ||
import sys | ||
|
||
sys.exit(pytest.main(sys.argv)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional params need "optional" and also to state the default values. c.f. numpy docstring:
https://numpy.org/doc/stable/reference/random/generated/numpy.random.choice.html