Skip to content

Commit 8d54d7c

Browse files
authored
Raise not-implemented exception on numpy fallback (#1201)
* Raise not-implemented exception on numpy fallback * Update dpnp/dpnp_utils/dpnp_algo_utils.pyx * Pass tests with DPNP_RAISE_EXCEPION_ON_NUMPY_FALLBACK=0 where it's needed * Wrap numpy.isfinite() and numpy.signbit() with TODO note
1 parent 8ee4744 commit 8d54d7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+413
-37
lines changed

dpnp/config.py

+5
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@
4747
'''
4848
Explicitly use SYCL shared memory parameter in DPCtl array constructor for creation functions
4949
'''
50+
51+
__DPNP_RAISE_EXCEPION_ON_NUMPY_FALLBACK__ = int(os.getenv('DPNP_RAISE_EXCEPION_ON_NUMPY_FALLBACK', 1))
52+
'''
53+
Trigger non-implemented exception when DPNP fallbacks on NumPy implementation
54+
'''

dpnp/dpnp_utils/dpnp_algo_utils.pyx

+8
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ def call_origin(function, *args, **kwargs):
114114
Call fallback function for unsupported cases
115115
"""
116116

117+
allow_fallback = kwargs.pop("allow_fallback", False)
118+
119+
if not allow_fallback and config.__DPNP_RAISE_EXCEPION_ON_NUMPY_FALLBACK__ == 1:
120+
raise NotImplementedError(f"Requested funtion={function.__name__} with args={args} and kwargs={kwargs} "
121+
"isn't currently supported and would fall back on NumPy implementation. "
122+
"Define enviroment variable `DPNP_RAISE_EXCEPION_ON_NUMPY_FALLBACK` to `0` "
123+
"if the fall back is required to be supported without rasing an exception.")
124+
117125
dpnp_inplace = kwargs.pop("dpnp_inplace", False)
118126
sycl_queue = kwargs.pop("sycl_queue", None)
119127
# print(f"DPNP call_origin(): Fallback called. \n\t function={function}, \n\t args={args}, \n\t kwargs={kwargs}, \n\t dpnp_inplace={dpnp_inplace}")

dpnp/random/dpnp_iface_random.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,7 @@ def seed(seed=None):
13141314
dpnp_rng_srand(seed)
13151315

13161316
# always reseed numpy engine also
1317-
return call_origin(numpy.random.seed, seed)
1317+
return call_origin(numpy.random.seed, seed, allow_fallback=True)
13181318

13191319

13201320
def standard_cauchy(size=None):

dpnp/random/dpnp_random_state.py

+52-10
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,45 @@ def __init__(self, seed=None, device=None, sycl_queue=None):
6767
self._def_float_type = dpnp.float64
6868

6969
self._random_state = MT19937(self._seed, self._sycl_queue)
70-
self._fallback_random_state = call_origin(numpy.random.RandomState, seed)
70+
self._fallback_random_state = call_origin(numpy.random.RandomState, seed, allow_fallback=True)
71+
72+
73+
def _is_finite_scalar(self, x):
74+
"""
75+
Test a scalar for finiteness (not infinity and not Not a Number).
76+
77+
Parameters
78+
-----------
79+
x : input value for test, must be a scalar.
80+
81+
Returns
82+
-------
83+
True where ``x`` is not positive infinity, negative infinity, or NaN;
84+
false otherwise.
85+
"""
86+
87+
# TODO: replace with dpnp.isfinite() once function is available in DPNP,
88+
# but for now use direct numpy calls without call_origin() wrapper, since data is a scalar
89+
return numpy.isfinite(x)
90+
91+
92+
def _is_signbit_scalar(self, x):
93+
"""
94+
Test a scalar if sign bit is set for it (less than zero).
95+
96+
Parameters
97+
-----------
98+
x : input value for test, must be a scalar.
99+
100+
Returns
101+
-------
102+
True where sign bit is set for ``x`` (that is ``x`` is less than zero);
103+
false otherwise.
104+
"""
105+
106+
# TODO: replace with dpnp.signbit() once function is available in DPNP,
107+
# but for now use direct numpy calls without call_origin() wrapper, since data is a scalar
108+
return numpy.signbit(x)
71109

72110

73111
def get_state(self):
@@ -125,13 +163,14 @@ def normal(self, loc=0.0, scale=1.0, size=None, dtype=None, usm_type="device"):
125163
else:
126164
min_double = numpy.finfo('double').min
127165
max_double = numpy.finfo('double').max
128-
if (loc >= max_double or loc <= min_double) and dpnp.isfinite(loc):
166+
167+
if (loc >= max_double or loc <= min_double) and self._is_finite_scalar(loc):
129168
raise OverflowError(f"Range of loc={loc} exceeds valid bounds")
130169

131-
if (scale >= max_double) and dpnp.isfinite(scale):
170+
if (scale >= max_double) and self._is_finite_scalar(scale):
132171
raise OverflowError(f"Range of scale={scale} exceeds valid bounds")
133-
# # scale = -0.0 is cosidered as negative
134-
elif scale < 0 or scale == 0 and numpy.signbit(scale):
172+
# scale = -0.0 is cosidered as negative
173+
elif scale < 0 or scale == 0 and self._is_signbit_scalar(scale):
135174
raise ValueError(f"scale={scale}, but must be non-negative.")
136175

137176
if dtype is None:
@@ -198,7 +237,8 @@ def randint(self, low, high=None, size=None, dtype=int, usm_type="device"):
198237
Limitations
199238
-----------
200239
Parameters ``low`` and ``high`` are supported only as scalar.
201-
Parameter ``dtype`` is supported only as `int`.
240+
Parameter ``dtype`` is supported only as :obj:`dpnp.int32` or `int`,
241+
but `int` value is considered to be exactly equivalent to :obj:`dpnp.int32`.
202242
Otherwise, :obj:`numpy.random.randint(low, high, size, dtype)` samples are drawn.
203243
204244
Examples
@@ -230,9 +270,10 @@ def randint(self, low, high=None, size=None, dtype=int, usm_type="device"):
230270

231271
min_int = numpy.iinfo('int32').min
232272
max_int = numpy.iinfo('int32').max
233-
if not dpnp.isfinite(low) or low > max_int or low < min_int:
273+
274+
if not self._is_finite_scalar(low) or low > max_int or low < min_int:
234275
raise OverflowError(f"Range of low={low} exceeds valid bounds")
235-
elif not dpnp.isfinite(high) or high > max_int or high < min_int:
276+
elif not self._is_finite_scalar(high) or high > max_int or high < min_int:
236277
raise OverflowError(f"Range of high={high} exceeds valid bounds")
237278

238279
low = int(low)
@@ -400,9 +441,10 @@ def uniform(self, low=0.0, high=1.0, size=None, dtype=None, usm_type="device"):
400441
else:
401442
min_double = numpy.finfo('double').min
402443
max_double = numpy.finfo('double').max
403-
if not dpnp.isfinite(low) or low >= max_double or low <= min_double:
444+
445+
if not self._is_finite_scalar(low) or low >= max_double or low <= min_double:
404446
raise OverflowError(f"Range of low={low} exceeds valid bounds")
405-
elif not dpnp.isfinite(high) or high >= max_double or high <= min_double:
447+
elif not self._is_finite_scalar(high) or high >= max_double or high <= min_double:
406448
raise OverflowError(f"Range of high={high} exceeds valid bounds")
407449

408450
if low > high:

tests/conftest.py

+4
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,7 @@ def pytest_collection_modifyitems(config, items):
7373
# exact match of the test name with items from excluded_list
7474
if test_name == item_tbl_str:
7575
item.add_marker(skip_mark)
76+
77+
@pytest.fixture
78+
def allow_fall_back_on_numpy(monkeypatch):
79+
monkeypatch.setattr(dpnp.config, '__DPNP_RAISE_EXCEPION_ON_NUMPY_FALLBACK__', 0)

tests/test_amin_amax.py

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def _get_min_max_input(type, shape):
6767
return a.reshape(shape)
6868

6969

70+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
7071
@pytest.mark.parametrize("type",
7172
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
7273
ids=['float64', 'float32', 'int64', 'int32'])
@@ -87,6 +88,7 @@ def test_amax(type, shape):
8788
numpy.testing.assert_array_equal(dpnp_res, np_res)
8889

8990

91+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
9092
@pytest.mark.parametrize("type",
9193
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
9294
ids=['float64', 'float32', 'int64', 'int32'])

tests/test_arithmetic.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
import pytest
23

34
from tests.third_party.cupy import testing
45

@@ -21,12 +22,14 @@ def test_modf_part2(self, xp, dtype):
2122

2223
return c
2324

25+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
2426
@testing.for_float_dtypes()
2527
@testing.numpy_cupy_allclose()
2628
def test_nanprod(self, xp, dtype):
2729
a = xp.array([-2.5, -1.5, xp.nan, 10.5, 1.5, xp.nan], dtype=dtype)
2830
return xp.nanprod(a)
2931

32+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
3033
@testing.for_float_dtypes()
3134
@testing.numpy_cupy_allclose()
3235
def test_nansum(self, xp, dtype):

tests/test_arraycreation.py

+7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def test_eye(N, M, k, dtype):
8181
numpy.testing.assert_array_equal(expected, result)
8282

8383

84+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
8485
@pytest.mark.parametrize("type",
8586
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
8687
ids=['float64', 'float32', 'int64', 'int32'])
@@ -93,6 +94,7 @@ def test_frombuffer(type):
9394
numpy.testing.assert_array_equal(dpnp_res, np_res)
9495

9596

97+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
9698
@pytest.mark.parametrize("type",
9799
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
98100
ids=['float64', 'float32', 'int64', 'int32'])
@@ -109,6 +111,7 @@ def test_fromfile(type):
109111
numpy.testing.assert_array_equal(dpnp_res, np_res)
110112

111113

114+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
112115
@pytest.mark.parametrize("type",
113116
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
114117
ids=['float64', 'float32', 'int64', 'int32'])
@@ -124,6 +127,7 @@ def func(x, y):
124127
numpy.testing.assert_array_equal(dpnp_res, np_res)
125128

126129

130+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
127131
@pytest.mark.parametrize("type",
128132
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
129133
ids=['float64', 'float32', 'int64', 'int32'])
@@ -136,6 +140,7 @@ def test_fromiter(type):
136140
numpy.testing.assert_array_equal(dpnp_res, np_res)
137141

138142

143+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
139144
@pytest.mark.parametrize("type",
140145
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
141146
ids=['float64', 'float32', 'int64', 'int32'])
@@ -148,6 +153,7 @@ def test_fromstring(type):
148153
numpy.testing.assert_array_equal(dpnp_res, np_res)
149154

150155

156+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
151157
@pytest.mark.parametrize("type",
152158
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
153159
ids=['float64', 'float32', 'int64', 'int32'])
@@ -183,6 +189,7 @@ def test_identity(n, type):
183189
numpy.testing.assert_array_equal(expected, result)
184190

185191

192+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
186193
@pytest.mark.parametrize("type",
187194
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
188195
ids=['float64', 'float32', 'int64', 'int32'])

tests/test_arraymanipulation.py

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import numpy
55

66

7+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
78
@pytest.mark.parametrize("dtype",
89
[numpy.float64, numpy.float32, numpy.int64, numpy.int32],
910
ids=["float64", "float32", "int64", "int32"])
@@ -30,6 +31,7 @@ def test_asfarray2(dtype, data):
3031
numpy.testing.assert_array_equal(result, expected)
3132

3233

34+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
3335
class TestConcatenate:
3436
def test_returns_copy(self):
3537
a = dpnp.array(numpy.eye(3))
@@ -91,23 +93,27 @@ class TestHstack:
9193
def test_non_iterable(self):
9294
numpy.testing.assert_raises(TypeError, dpnp.hstack, 1)
9395

96+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
9497
def test_empty_input(self):
9598
numpy.testing.assert_raises(ValueError, dpnp.hstack, ())
9699

100+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
97101
def test_0D_array(self):
98102
b = dpnp.array(2)
99103
a = dpnp.array(1)
100104
res = dpnp.hstack([a, b])
101105
desired = dpnp.array([1, 2])
102106
numpy.testing.assert_array_equal(res, desired)
103107

108+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
104109
def test_1D_array(self):
105110
a = dpnp.array([1])
106111
b = dpnp.array([2])
107112
res = dpnp.hstack([a, b])
108113
desired = dpnp.array([1, 2])
109114
numpy.testing.assert_array_equal(res, desired)
110115

116+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
111117
def test_2D_array(self):
112118
a = dpnp.array([[1], [2]])
113119
b = dpnp.array([[1], [2]])
@@ -126,30 +132,35 @@ class TestVstack:
126132
def test_non_iterable(self):
127133
numpy.testing.assert_raises(TypeError, dpnp.vstack, 1)
128134

135+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
129136
def test_empty_input(self):
130137
numpy.testing.assert_raises(ValueError, dpnp.vstack, ())
131138

139+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
132140
def test_0D_array(self):
133141
a = dpnp.array(1)
134142
b = dpnp.array(2)
135143
res = dpnp.vstack([a, b])
136144
desired = dpnp.array([[1], [2]])
137145
numpy.testing.assert_array_equal(res, desired)
138146

147+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
139148
def test_1D_array(self):
140149
a = dpnp.array([1])
141150
b = dpnp.array([2])
142151
res = dpnp.vstack([a, b])
143152
desired = dpnp.array([[1], [2]])
144153
numpy.testing.assert_array_equal(res, desired)
145154

155+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
146156
def test_2D_array(self):
147157
a = dpnp.array([[1], [2]])
148158
b = dpnp.array([[1], [2]])
149159
res = dpnp.vstack([a, b])
150160
desired = dpnp.array([[1], [2], [1], [2]])
151161
numpy.testing.assert_array_equal(res, desired)
152162

163+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
153164
def test_2D_array2(self):
154165
a = dpnp.array([1, 2])
155166
b = dpnp.array([1, 2])

tests/test_bitwise.py

+6
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,26 @@ def _test_binary_int(self, name, lhs, rhs, dtype):
3737

3838
numpy.testing.assert_array_equal(result, expected)
3939

40+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
4041
def test_bitwise_and(self, lhs, rhs, dtype):
4142
self._test_binary_int('bitwise_and', lhs, rhs, dtype)
4243

44+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
4345
def test_bitwise_or(self, lhs, rhs, dtype):
4446
self._test_binary_int('bitwise_or', lhs, rhs, dtype)
4547

48+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
4649
def test_bitwise_xor(self, lhs, rhs, dtype):
4750
self._test_binary_int('bitwise_xor', lhs, rhs, dtype)
4851

52+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
4953
def test_invert(self, lhs, rhs, dtype):
5054
self._test_unary_int('invert', lhs, dtype)
5155

56+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
5257
def test_left_shift(self, lhs, rhs, dtype):
5358
self._test_binary_int('left_shift', lhs, rhs, dtype)
5459

60+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
5561
def test_right_shift(self, lhs, rhs, dtype):
5662
self._test_binary_int('right_shift', lhs, rhs, dtype)

tests/test_histograms.py

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def setup(self):
1313
def teardown(self):
1414
pass
1515

16+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
1617
def test_simple(self):
1718
n = 100
1819
v = dpnp.random.rand(n)
@@ -24,6 +25,7 @@ def test_simple(self):
2425
(a, b) = dpnp.histogram(numpy.linspace(0, 10, 100))
2526
numpy.testing.assert_array_equal(a, 10)
2627

28+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
2729
def test_one_bin(self):
2830
# Ticket 632
2931
hist, edges = dpnp.histogram([1, 2, 3, 4], [1, 2])
@@ -66,6 +68,8 @@ def test_density(self):
6668
[1, 2, 3, 4], [0.5, 1.5, numpy.inf], density=True)
6769
numpy.testing.assert_equal(counts, [.25, 0])
6870

71+
72+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
6973
def test_arr_weights_mismatch(self):
7074
a = dpnp.arange(10) + .5
7175
w = dpnp.arange(11) + .5

0 commit comments

Comments
 (0)