Skip to content

Commit e3b51e9

Browse files
committed
Add new test scope for binary ufuncs with two output arrays
1 parent 95c6f0b commit e3b51e9

File tree

1 file changed

+271
-0
lines changed

1 file changed

+271
-0
lines changed
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
import itertools
2+
3+
import numpy
4+
import pytest
5+
from numpy.testing import (
6+
assert_array_equal,
7+
)
8+
9+
import dpnp
10+
11+
from .helper import (
12+
generate_random_numpy_array,
13+
get_all_dtypes,
14+
get_complex_dtypes,
15+
get_float_dtypes,
16+
get_integer_dtypes,
17+
)
18+
19+
"""
20+
The scope includes tests with only functions which are instances of
21+
`DPNPBinaryTwoOutputsFunc` class.
22+
23+
"""
24+
25+
26+
@pytest.mark.parametrize("func", ["divmod"])
27+
class TestBinaryTwoOutputs:
28+
ALL_DTYPES = get_all_dtypes(no_none=True)
29+
ALL_DTYPES_NO_COMPLEX = get_all_dtypes(
30+
no_none=True, no_float16=False, no_complex=True
31+
)
32+
ALL_FLOAT_DTYPES = get_float_dtypes(no_float16=False)
33+
34+
def _signs(self, dtype):
35+
if numpy.issubdtype(dtype, numpy.unsignedinteger):
36+
return (+1,)
37+
else:
38+
return (+1, -1)
39+
40+
@pytest.mark.usefixtures("suppress_divide_numpy_warnings")
41+
@pytest.mark.parametrize("dt", ALL_DTYPES_NO_COMPLEX)
42+
def test_basic(self, func, dt):
43+
a = generate_random_numpy_array((2, 5), dtype=dt)
44+
b = generate_random_numpy_array((2, 5), dtype=dt)
45+
ia, ib = dpnp.array(a), dpnp.array(b)
46+
47+
res1, res2 = getattr(dpnp, func)(ia, ib)
48+
exp1, exp2 = getattr(numpy, func)(a, b)
49+
assert_array_equal(res1, exp1)
50+
assert_array_equal(res2, exp2)
51+
52+
@pytest.mark.parametrize("dt1", ALL_DTYPES_NO_COMPLEX)
53+
@pytest.mark.parametrize("dt2", ALL_DTYPES_NO_COMPLEX)
54+
def test_signs(self, func, dt1, dt2):
55+
for sign1, sign2 in itertools.product(
56+
self._signs(dt1), self._signs(dt2)
57+
):
58+
a = numpy.array(sign1 * 71, dtype=dt1)
59+
b = numpy.array(sign2 * 19, dtype=dt2)
60+
ia, ib = dpnp.array(a), dpnp.array(b)
61+
62+
res1, res2 = getattr(dpnp, func)(ia, ib)
63+
exp1, exp2 = getattr(numpy, func)(a, b)
64+
assert_array_equal(res1, exp1)
65+
assert_array_equal(res2, exp2)
66+
67+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
68+
def test_float_exact(self, func, dt):
69+
# test that float results are exact for small integers
70+
nlst = list(range(-127, 0))
71+
plst = list(range(1, 128))
72+
dividend = nlst + [0] + plst
73+
divisor = nlst + plst
74+
arg = list(itertools.product(dividend, divisor))
75+
76+
a, b = numpy.array(arg, dtype=dt).T
77+
ia, ib = dpnp.array(a), dpnp.array(b)
78+
79+
res1, res2 = getattr(dpnp, func)(ia, ib)
80+
exp1, exp2 = getattr(numpy, func)(a, b)
81+
assert_array_equal(res1, exp1)
82+
assert_array_equal(res2, exp2)
83+
84+
@pytest.mark.parametrize("dt1", get_float_dtypes())
85+
@pytest.mark.parametrize("dt2", get_float_dtypes())
86+
@pytest.mark.parametrize(
87+
"sign1, sign2", [(+1, +1), (+1, -1), (-1, +1), (-1, -1)]
88+
)
89+
def test_float_roundoff(self, func, dt1, dt2, sign1, sign2):
90+
a = numpy.array(sign1 * 78 * 6e-8, dtype=dt1)
91+
b = numpy.array(sign2 * 6e-8, dtype=dt2)
92+
ia, ib = dpnp.array(a), dpnp.array(b)
93+
94+
res1, res2 = getattr(dpnp, func)(ia, ib)
95+
exp1, exp2 = getattr(numpy, func)(a, b)
96+
assert_array_equal(res1, exp1)
97+
assert_array_equal(res2, exp2)
98+
99+
@pytest.mark.usefixtures("suppress_divide_invalid_numpy_warnings")
100+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
101+
@pytest.mark.parametrize(
102+
"val1", [0.0, 1.0, numpy.inf, -numpy.inf, numpy.nan]
103+
)
104+
@pytest.mark.parametrize(
105+
"val2", [0.0, 1.0, numpy.inf, -numpy.inf, numpy.nan]
106+
)
107+
def test_special_float_values(self, func, dt, val1, val2):
108+
a = numpy.array(val1, dtype=dt)
109+
b = numpy.array(val2, dtype=dt)
110+
ia, ib = dpnp.array(a), dpnp.array(b)
111+
112+
res1, res2 = getattr(dpnp, func)(ia, ib)
113+
exp1, exp2 = getattr(numpy, func)(a, b)
114+
assert_array_equal(res1, exp1)
115+
assert_array_equal(res2, exp2)
116+
117+
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
118+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
119+
def test_float_overflow(self, func, dt):
120+
a = numpy.finfo(dt).tiny
121+
a = numpy.array(a, dtype=dt)
122+
ia = dpnp.array(a, dtype=dt)
123+
124+
res1, res2 = getattr(dpnp, func)(4, ia)
125+
exp1, exp2 = getattr(numpy, func)(4, a)
126+
assert_array_equal(res1, exp1)
127+
assert_array_equal(res2, exp2)
128+
129+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
130+
def test_out(self, func, dt):
131+
a = numpy.array(5.7, dtype=dt)
132+
ia = dpnp.array(a)
133+
134+
out1 = numpy.empty((), dtype=dt)
135+
out2 = numpy.empty((), dtype=dt)
136+
iout1, iout2 = dpnp.array(out1), dpnp.array(out2)
137+
138+
res1, res2 = getattr(dpnp, func)(ia, 2, iout1)
139+
exp1, exp2 = getattr(numpy, func)(a, 2, out1)
140+
assert_array_equal(res1, exp1)
141+
assert_array_equal(res2, exp2)
142+
assert res1 is iout1
143+
144+
res1, res2 = getattr(dpnp, func)(ia, 2, None, iout2)
145+
exp1, exp2 = getattr(numpy, func)(a, 2, None, out2)
146+
assert_array_equal(res1, exp1)
147+
assert_array_equal(res2, exp2)
148+
assert res2 is iout2
149+
150+
res1, res2 = getattr(dpnp, func)(ia, 2, iout1, iout2)
151+
exp1, exp2 = getattr(numpy, func)(a, 2, out1, out2)
152+
assert_array_equal(res1, exp1)
153+
assert_array_equal(res2, exp2)
154+
assert res1 is iout1
155+
assert res2 is iout2
156+
157+
@pytest.mark.parametrize("dt1", ALL_DTYPES_NO_COMPLEX)
158+
@pytest.mark.parametrize("dt2", ALL_DTYPES_NO_COMPLEX)
159+
@pytest.mark.parametrize("out1_dt", ALL_DTYPES)
160+
@pytest.mark.parametrize("out2_dt", ALL_DTYPES)
161+
def test_2out_all_dtypes(self, func, dt1, dt2, out1_dt, out2_dt):
162+
a = numpy.ones((3, 1), dtype=dt1)
163+
b = numpy.ones((3, 4), dtype=dt2)
164+
ia, ib = dpnp.array(a), dpnp.array(b)
165+
166+
out1 = numpy.zeros_like(b, dtype=out1_dt)
167+
out2 = numpy.zeros_like(b, dtype=out2_dt)
168+
iout1, iout2 = dpnp.array(out1), dpnp.array(out2)
169+
170+
try:
171+
res1, res2 = getattr(dpnp, func)(ia, ib, out=(iout1, iout2))
172+
except TypeError:
173+
# expect numpy to fail with the same reason
174+
with pytest.raises(TypeError):
175+
_ = getattr(numpy, func)(a, b, out=(out1, out2))
176+
else:
177+
exp1, exp2 = getattr(numpy, func)(a, b, out=(out1, out2))
178+
assert_array_equal(res1, exp1)
179+
assert_array_equal(res2, exp2)
180+
assert res1 is iout1
181+
assert res2 is iout2
182+
183+
@pytest.mark.usefixtures("suppress_invalid_numpy_warnings")
184+
@pytest.mark.parametrize("stride", [-4, -2, -1, 1, 2, 4])
185+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
186+
def test_strides_out(self, func, stride, dt):
187+
a = numpy.array(
188+
[numpy.nan, numpy.nan, numpy.inf, -numpy.inf, 0.0, -0.0, 1.0, -1.0],
189+
dtype=dt,
190+
)
191+
ia = dpnp.array(a)
192+
193+
out1 = numpy.ones_like(a, dtype=dt)
194+
out2 = 2 * numpy.ones_like(a, dtype=dt)
195+
iout_mant, iout_exp = dpnp.array(out1), dpnp.array(out2)
196+
197+
res1, res2 = getattr(dpnp, func)(
198+
ia[::stride], 2, out=(iout_mant[::stride], iout_exp[::stride])
199+
)
200+
exp1, exp2 = getattr(numpy, func)(
201+
a[::stride], 2, out=(out1[::stride], out2[::stride])
202+
)
203+
assert_array_equal(res1, exp1)
204+
assert_array_equal(res2, exp2)
205+
206+
assert_array_equal(iout_mant, out1)
207+
assert_array_equal(iout_exp, out2)
208+
209+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
210+
def test_out1_overlap(self, func, dt):
211+
size = 15
212+
a = numpy.ones(2 * size, dtype=dt)
213+
ia = dpnp.array(a)
214+
215+
# out1 overlaps memory of input array
216+
_ = getattr(dpnp, func)(ia[size::], 1, ia[::2])
217+
_ = getattr(numpy, func)(a[size::], 1, a[::2])
218+
assert_array_equal(ia, a)
219+
220+
@pytest.mark.parametrize("dt", ALL_FLOAT_DTYPES)
221+
def test_empty(self, func, dt):
222+
a = numpy.empty(0, dtype=dt)
223+
ia = dpnp.array(a)
224+
225+
res1, res2 = getattr(dpnp, func)(ia, ia)
226+
exp1, exp2 = getattr(numpy, func)(a, a)
227+
assert_array_equal(res1, exp1, strict=True)
228+
assert_array_equal(res2, exp2, strict=True)
229+
230+
@pytest.mark.parametrize("xp", [numpy, dpnp])
231+
@pytest.mark.parametrize("dt", get_complex_dtypes())
232+
def test_complex_dtype(self, func, xp, dt):
233+
a = xp.array(
234+
[0.9 + 1j, -0.1 + 1j, 0.9 + 0.5 * 1j, 0.9 + 2.0 * 1j], dtype=dt
235+
)
236+
with pytest.raises((TypeError, ValueError)):
237+
_ = getattr(xp, func)(a, 7)
238+
239+
240+
class TestDivmod:
241+
@pytest.mark.usefixtures("suppress_divide_numpy_warnings")
242+
@pytest.mark.parametrize("dt", get_integer_dtypes())
243+
def test_int_zero(self, dt):
244+
a = numpy.array(0, dtype=dt)
245+
ia = dpnp.array(a)
246+
247+
res1, res2 = dpnp.divmod(ia, 0)
248+
exp1, exp2 = numpy.divmod(a, 0)
249+
assert_array_equal(res1, exp1)
250+
assert_array_equal(res2, exp2)
251+
252+
@pytest.mark.parametrize("dt", get_integer_dtypes(no_unsigned=True))
253+
def test_min_int(self, dt):
254+
a = numpy.array(numpy.iinfo(dt).min, dtype=dt)
255+
ia = dpnp.array(a)
256+
257+
res1, res2 = dpnp.divmod(ia, -1)
258+
exp1, exp2 = numpy.divmod(a, -1)
259+
assert_array_equal(res1, exp1)
260+
assert_array_equal(res2, exp2)
261+
262+
@pytest.mark.parametrize("dt", get_integer_dtypes(no_unsigned=True))
263+
def test_special_int(self, dt):
264+
# a and b have different sign and mod != 0
265+
a, b = numpy.array(-1, dtype=dt), numpy.array(3, dtype=dt)
266+
ia, ib = dpnp.array(a), dpnp.array(b)
267+
268+
res1, res2 = dpnp.divmod(ia, ib)
269+
exp1, exp2 = numpy.divmod(a, b)
270+
assert_array_equal(res1, exp1)
271+
assert_array_equal(res2, exp2)

0 commit comments

Comments
 (0)