From bba1ad73c187a951beb299409576b94b5450a510 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 28 Jul 2023 13:08:06 +0200 Subject: [PATCH 1/2] Leveraged dpctl.tensor.concat() implementation --- dpnp/dpnp_iface_manipulation.py | 74 ++- dpnp/dpnp_utils/dpnp_utils_statistics.py | 35 +- tests/test_arraymanipulation.py | 220 +++++-- .../cupy/manipulation_tests/test_join.py | 586 ++++++++++++++++++ 4 files changed, 822 insertions(+), 93 deletions(-) create mode 100644 tests/third_party/cupy/manipulation_tests/test_join.py diff --git a/dpnp/dpnp_iface_manipulation.py b/dpnp/dpnp_iface_manipulation.py index be2c427cf0e3..b5bd7e423eee 100644 --- a/dpnp/dpnp_iface_manipulation.py +++ b/dpnp/dpnp_iface_manipulation.py @@ -237,38 +237,76 @@ def broadcast_to(x, /, shape, subok=False): return call_origin(numpy.broadcast_to, x, shape=shape, subok=subok) -def concatenate(arrs, axis=0, out=None, dtype=None, casting="same_kind"): +def concatenate(arrays, axis=0, out=None, *, dtype=None, **kwargs): """ Join a sequence of arrays along an existing axis. For full documentation refer to :obj:`numpy.concatenate`. + Returns + ------- + out : dpnp.ndarray + The concatenated array. + + Limitations + ----------- + Each array in `arrays` is supported as either :class:`dpnp.ndarray` + or :class:`dpctl.tensor.usm_ndarray`. Otherwise ``TypeError`` exeption + will be raised. + Parameter `out` is supported with default value. + Parameter `dtype` is supported with default value. + Keyword argument ``kwargs`` is currently unsupported. + Otherwise the function will be executed sequentially on CPU. + + See Also + -------- + :obj:`dpnp.array_split` : Split an array into multiple sub-arrays of equal or near-equal size. + :obj:`dpnp.split` : Split array into a list of multiple sub-arrays of equal size. + :obj:`dpnp.hsplit` : Split array into multiple sub-arrays horizontally (column wise). + :obj:`dpnp.vsplit` : Split array into multiple sub-arrays vertically (row wise). + :obj:`dpnp.dsplit` : Split array into multiple sub-arrays along the 3rd axis (depth). + :obj:`dpnp.stack` : Stack a sequence of arrays along a new axis. + :obj:`dpnp.block` : Assemble arrays from blocks. + :obj:`dpnp.hstack` : Stack arrays in sequence horizontally (column wise). + :obj:`dpnp.vstack` : Stack arrays in sequence vertically (row wise). + :obj:`dpnp.dstack` : Stack arrays in sequence depth wise (along third dimension). + :obj:`dpnp.column_stack` : Stack 1-D arrays as columns into a 2-D array. + Examples -------- - >>> import dpnp - >>> a = dpnp.array([[1, 2], [3, 4]]) - >>> b = dpnp.array([[5, 6]]) - >>> res = dpnp.concatenate((a, b), axis=0) - >>> print(res) - [[1 2] - [3 4] - [5 6]] - >>> res = dpnp.concatenate((a, b.T), axis=1) - >>> print(res) - [[1 2 5] - [3 4 6]] - >>> res = dpnp.concatenate((a, b), axis=None) - >>> print(res) - [1 2 3 4 5 6] + >>> import dpnp as np + >>> a = np.array([[1, 2], [3, 4]]) + >>> b = np.array([[5, 6]]) + >>> np.concatenate((a, b), axis=0) + array([[1, 2], + [3, 4], + [5, 6]]) + >>> np.concatenate((a, b.T), axis=1) + array([[1, 2, 5], + [3, 4, 6]]) + >>> np.concatenate((a, b), axis=None) + array([1, 2, 3, 4, 5, 6]) """ + + if kwargs: + pass + elif out is not None: + pass + elif dtype is not None: + pass + else: + usm_arrays = [dpnp.get_usm_ndarray(x) for x in arrays] + usm_res = dpt.concat(usm_arrays, axis=axis) + return dpnp_array._create_from_usm_ndarray(usm_res) + return call_origin( numpy.concatenate, - arrs, + arrays, axis=axis, out=out, dtype=dtype, - casting=casting, + **kwargs, ) diff --git a/dpnp/dpnp_utils/dpnp_utils_statistics.py b/dpnp/dpnp_utils/dpnp_utils_statistics.py index 9d69dbaa3684..7ed829535412 100644 --- a/dpnp/dpnp_utils/dpnp_utils_statistics.py +++ b/dpnp/dpnp_utils/dpnp_utils_statistics.py @@ -27,12 +27,7 @@ # ***************************************************************************** -import dpctl -import dpctl.tensor as dpt -import dpctl.tensor._tensor_impl as ti - import dpnp -from dpnp.dpnp_array import dpnp_array from dpnp.dpnp_utils import get_usm_allocations __all__ = ["dpnp_cov"] @@ -92,35 +87,7 @@ def _get_2dmin_array(x, dtype): if y is not None: y = _get_2dmin_array(y, dtype) - # TODO: replace with dpnp.concatenate((X, y), axis=0) once dpctl implementation is ready - if X.ndim != y.ndim: - raise ValueError( - "all the input arrays must have same number of dimensions" - ) - - if X.shape[1:] != y.shape[1:]: - raise ValueError( - "all the input array dimensions for the concatenation axis must match exactly" - ) - - res_shape = tuple( - X.shape[i] if i > 0 else (X.shape[i] + y.shape[i]) - for i in range(X.ndim) - ) - res_usm = dpt.empty( - res_shape, dtype=dtype, usm_type=usm_type, sycl_queue=queue - ) - - # concatenate input arrays 'm' and 'y' into single array among 0-axis - hev1, _ = ti._copy_usm_ndarray_into_usm_ndarray( - src=X.get_array(), dst=res_usm[: X.shape[0]], sycl_queue=queue - ) - hev2, _ = ti._copy_usm_ndarray_into_usm_ndarray( - src=y.get_array(), dst=res_usm[X.shape[0] :], sycl_queue=queue - ) - dpctl.SyclEvent.wait_for([hev1, hev2]) - - X = dpnp_array._create_from_usm_ndarray(res_usm) + X = dpnp.concatenate((X, y), axis=0) avg = X.mean(axis=1) diff --git a/tests/test_arraymanipulation.py b/tests/test_arraymanipulation.py index 8d75296cad0a..0be647765c0e 100644 --- a/tests/test_arraymanipulation.py +++ b/tests/test_arraymanipulation.py @@ -128,62 +128,200 @@ def test_broadcast_raise(self, sh1, sh2): func(dpnp, dp_a) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestConcatenate: def test_returns_copy(self): - a = dpnp.array(numpy.eye(3)) + a = dpnp.eye(3) b = dpnp.concatenate([a]) b[0, 0] = 2 assert b[0, 0] != a[0, 0] - def test_large_concatenate_axis_None(self): - x = dpnp.arange(1, 100) - r = dpnp.concatenate(x, None) - assert_array_equal(x, r) - r = dpnp.concatenate(x, 100) - assert_array_equal(x, r) + @pytest.mark.parametrize("ndim", [1, 2, 3]) + def test_axis_exceptions(self, ndim): + dp_a = dpnp.ones((1,) * ndim) + np_a = numpy.ones((1,) * ndim) + + dp_res = dpnp.concatenate((dp_a, dp_a), axis=0) + np_res = numpy.concatenate((np_a, np_a), axis=0) + assert_equal(dp_res.asnumpy(), np_res) + + for axis in [ndim, -(ndim + 1)]: + with pytest.raises(numpy.AxisError): + dpnp.concatenate((dp_a, dp_a), axis=axis) + numpy.concatenate((np_a, np_a), axis=axis) + + def test_scalar_exceptions(self): + assert_raises(TypeError, dpnp.concatenate, (0,)) + assert_raises(ValueError, numpy.concatenate, (0,)) + + for xp in [dpnp, numpy]: + with pytest.raises(ValueError): + xp.concatenate((xp.array(0),)) + + def test_dims_exception(self): + for xp in [dpnp, numpy]: + with pytest.raises(ValueError): + xp.concatenate((xp.zeros(1), xp.zeros((1, 1)))) + + def test_shapes_match_exception(self): + axis = list(range(3)) + np_a = numpy.ones((1, 2, 3)) + np_b = numpy.ones((2, 2, 3)) + + dp_a = dpnp.array(np_a) + dp_b = dpnp.array(np_b) + + for _ in range(3): + # shapes must match except for concatenation axis + np_res = numpy.concatenate((np_a, np_b), axis=axis[0]) + dp_res = dpnp.concatenate((dp_a, dp_b), axis=axis[0]) + assert_equal(dp_res.asnumpy(), np_res) + + for i in range(1, 3): + with pytest.raises(ValueError): + numpy.concatenate((np_a, np_b), axis=axis[i]) + dpnp.concatenate((dp_a, dp_b), axis=axis[i]) + + np_a = numpy.moveaxis(np_a, -1, 0) + dp_a = dpnp.moveaxis(dp_a, -1, 0) + + np_b = numpy.moveaxis(np_b, -1, 0) + dp_b = dpnp.moveaxis(dp_b, -1, 0) + axis.append(axis.pop(0)) + + def test_no_array_exception(self): + with pytest.raises(ValueError): + numpy.concatenate(()) + dpnp.concatenate(()) + + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_concatenate_axis_None(self, dtype): + stop, sh = (4, (2, 2)) if dtype is not dpnp.bool else (2, (2, 1)) + np_a = numpy.arange(stop, dtype=dtype).reshape(sh) + dp_a = dpnp.arange(stop, dtype=dtype).reshape(sh) + + np_res = numpy.concatenate((np_a, np_a), axis=None) + dp_res = dpnp.concatenate((dp_a, dp_a), axis=None) + assert_equal(dp_res.asnumpy(), np_res) + + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_bool=True, no_none=True) + ) + def test_large_concatenate_axis_None(self, dtype): + start, stop = (1, 100) + np_a = numpy.arange(start, stop, dtype=dtype) + dp_a = dpnp.arange(start, stop, dtype=dtype) + + np_res = numpy.concatenate(np_a, None) + dp_res = dpnp.concatenate(dp_a, None) + assert_array_equal(dp_res.asnumpy(), np_res) + + # numpy doesn't raise an exception here but probably should + with pytest.raises(numpy.AxisError): + dpnp.concatenate(dp_a, 100) - def test_concatenate(self): + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_concatenate(self, dtype): # Test concatenate function # One sequence returns unmodified (but as array) r4 = list(range(4)) - assert_array_equal(dpnp.concatenate((r4,)), r4) - # Any sequence - assert_array_equal(dpnp.concatenate((tuple(r4),)), r4) - assert_array_equal(dpnp.concatenate((dpnp.array(r4),)), r4) + np_r4 = numpy.array(r4, dtype=dtype) + dp_r4 = dpnp.array(r4, dtype=dtype) + + np_res = numpy.concatenate((np_r4,)) + dp_res = dpnp.concatenate((dp_r4,)) + assert_array_equal(dp_res.asnumpy(), np_res) + # 1D default concatenation r3 = list(range(3)) - assert_array_equal(dpnp.concatenate((r4, r3)), r4 + r3) - # Mixed sequence types - assert_array_equal(dpnp.concatenate((tuple(r4), r3)), r4 + r3) - assert_array_equal(dpnp.concatenate((dpnp.array(r4), r3)), r4 + r3) + np_r3 = numpy.array(r3, dtype=dtype) + dp_r3 = dpnp.array(r3, dtype=dtype) + + np_res = numpy.concatenate((np_r4, np_r3)) + dp_res = dpnp.concatenate((dp_r4, dp_r3)) + assert_array_equal(dp_res.asnumpy(), np_res) + # Explicit axis specification - assert_array_equal(dpnp.concatenate((r4, r3), 0), r4 + r3) + np_res = numpy.concatenate((np_r4, np_r3), 0) + dp_res = dpnp.concatenate((dp_r4, dp_r3), 0) + assert_array_equal(dp_res.asnumpy(), np_res) + # Including negative - assert_array_equal(dpnp.concatenate((r4, r3), -1), r4 + r3) - # 2D - a23 = dpnp.array([[10, 11, 12], [13, 14, 15]]) - a13 = dpnp.array([[0, 1, 2]]) - res = dpnp.array([[10, 11, 12], [13, 14, 15], [0, 1, 2]]) - assert_array_equal(dpnp.concatenate((a23, a13)), res) - assert_array_equal(dpnp.concatenate((a23, a13), 0), res) - assert_array_equal(dpnp.concatenate((a23.T, a13.T), 1), res.T) - assert_array_equal(dpnp.concatenate((a23.T, a13.T), -1), res.T) + np_res = numpy.concatenate((np_r4, np_r3), -1) + dp_res = dpnp.concatenate((dp_r4, dp_r3), -1) + assert_array_equal(dp_res.asnumpy(), np_res) + + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_concatenate_2d(self, dtype): + np_a23 = numpy.array([[10, 11, 12], [13, 14, 15]], dtype=dtype) + np_a13 = numpy.array([[0, 1, 2]], dtype=dtype) + + dp_a23 = dpnp.array([[10, 11, 12], [13, 14, 15]], dtype=dtype) + dp_a13 = dpnp.array([[0, 1, 2]], dtype=dtype) + + np_res = numpy.concatenate((np_a23, np_a13)) + dp_res = dpnp.concatenate((dp_a23, dp_a13)) + assert_array_equal(dp_res.asnumpy(), np_res) + + np_res = numpy.concatenate((np_a23, np_a13), 0) + dp_res = dpnp.concatenate((dp_a23, dp_a13), 0) + assert_array_equal(dp_res.asnumpy(), np_res) + + for axis in [1, -1]: + np_res = numpy.concatenate((np_a23.T, np_a13.T), axis) + dp_res = dpnp.concatenate((dp_a23.T, dp_a13.T), axis) + assert_array_equal(dp_res.asnumpy(), np_res) + # Arrays much match shape - assert_raises(ValueError, dpnp.concatenate, (a23.T, a13.T), 0) - # 3D - res = dpnp.reshape(dpnp.arange(2 * 3 * 7), (2, 3, 7)) - a0 = res[..., :4] - a1 = res[..., 4:6] - a2 = res[..., 6:] - assert_array_equal(dpnp.concatenate((a0, a1, a2), 2), res) - assert_array_equal(dpnp.concatenate((a0, a1, a2), -1), res) - assert_array_equal(dpnp.concatenate((a0.T, a1.T, a2.T), 0), res.T) - - out = dpnp.copy(res) - rout = dpnp.concatenate((a0, a1, a2), 2, out=out) - assert_(out is rout) - assert_equal(res, rout) + with pytest.raises(ValueError): + numpy.concatenate((np_a23.T, np_a13.T), 0) + dpnp.concatenate((dp_a23.T, dp_a13.T), 0) + + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_bool=True, no_none=True) + ) + def test_concatenate_3d(self, dtype): + np_a = numpy.arange(2 * 3 * 7, dtype=dtype).reshape((2, 3, 7)) + np_a0 = np_a[..., :4] + np_a1 = np_a[..., 4:6] + np_a2 = np_a[..., 6:] + + dp_a = dpnp.arange(2 * 3 * 7, dtype=dtype).reshape((2, 3, 7)) + dp_a0 = dp_a[..., :4] + dp_a1 = dp_a[..., 4:6] + dp_a2 = dp_a[..., 6:] + + for axis in [2, -1]: + np_res = numpy.concatenate((np_a0, np_a1, np_a2), axis) + dp_res = dpnp.concatenate((dp_a0, dp_a1, dp_a2), axis) + assert_array_equal(dp_res.asnumpy(), np_res) + + np_res = numpy.concatenate((np_a0.T, np_a1.T, np_a2.T), 0) + dp_res = dpnp.concatenate((dp_a0.T, dp_a1.T, dp_a2.T), 0) + assert_array_equal(dp_res.asnumpy(), np_res) + + @pytest.mark.skip("out keyword is currently unsupported") + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_bool=True, no_none=True) + ) + def test_concatenate_out(self, dtype): + np_a = numpy.arange(2 * 3 * 7, dtype=dtype).reshape((2, 3, 7)) + np_a0 = np_a[..., :4] + np_a1 = np_a[..., 4:6] + np_a2 = np_a[..., 6:] + np_out = numpy.empty_like(np_a) + + dp_a = dpnp.arange(2 * 3 * 7, dtype=dtype).reshape((2, 3, 7)) + dp_a0 = dp_a[..., :4] + dp_a1 = dp_a[..., 4:6] + dp_a2 = dp_a[..., 6:] + dp_out = dpnp.empty_like(dp_a) + + np_res = numpy.concatenate((np_a0, np_a1, np_a2), axis=2, out=np_out) + dp_res = dpnp.concatenate((dp_a0, dp_a1, dp_a2), axis=2, out=dp_out) + + assert dp_out is dp_res + assert_array_equal(dp_out.asnumpy(), np_out) + assert_array_equal(dp_res.asnumpy(), np_res) class TestHstack: diff --git a/tests/third_party/cupy/manipulation_tests/test_join.py b/tests/third_party/cupy/manipulation_tests/test_join.py new file mode 100644 index 000000000000..b90840c88548 --- /dev/null +++ b/tests/third_party/cupy/manipulation_tests/test_join.py @@ -0,0 +1,586 @@ +import unittest + +import numpy +import pytest + +import dpnp as cupy +from tests.third_party.cupy import testing + + +class TestJoin(unittest.TestCase): + @pytest.mark.skip("dpnp.column_stack() is not implemented yet") + @testing.for_all_dtypes(name="dtype1") + @testing.for_all_dtypes(name="dtype2") + @testing.numpy_cupy_array_equal() + def test_column_stack(self, xp, dtype1, dtype2): + a = testing.shaped_arange((4, 3), xp, dtype1) + b = testing.shaped_arange((4,), xp, dtype2) + c = testing.shaped_arange((4, 2), xp, dtype1) + return xp.column_stack((a, b, c)) + + @pytest.mark.skip("dpnp.column_stack() is not implemented yet") + def test_column_stack_wrong_ndim1(self): + a = cupy.zeros(()) + b = cupy.zeros((3,)) + with pytest.raises(ValueError): + cupy.column_stack((a, b)) + + @pytest.mark.skip("dpnp.column_stack() is not implemented yet") + def test_column_stack_wrong_ndim2(self): + a = cupy.zeros((3, 2, 3)) + b = cupy.zeros((3, 2)) + with pytest.raises(ValueError): + cupy.column_stack((a, b)) + + @pytest.mark.skip("dpnp.column_stack() is not implemented yet") + def test_column_stack_wrong_shape(self): + a = cupy.zeros((3, 2)) + b = cupy.zeros((4, 3)) + with pytest.raises(ValueError): + cupy.column_stack((a, b)) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate1(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_reverse_arange((2, 3, 2), xp, dtype) + c = testing.shaped_arange((2, 3, 3), xp, dtype) + return xp.concatenate((a, b, c), axis=2) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate2(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_reverse_arange((2, 3, 2), xp, dtype) + c = testing.shaped_arange((2, 3, 3), xp, dtype) + return xp.concatenate((a, b, c), axis=-1) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_axis_none(self, xp, dtype): + a = testing.shaped_arange((2, 3), xp, dtype) + b = testing.shaped_reverse_arange((3, 5, 2), xp, dtype) + c = testing.shaped_arange((7,), xp, dtype) + return xp.concatenate((a, b, c), axis=None) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_large_2(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_reverse_arange((2, 3, 2), xp, dtype) + c = testing.shaped_arange((2, 3, 3), xp, dtype) + d = testing.shaped_arange((2, 3, 5), xp, dtype) + e = testing.shaped_arange((2, 3, 2), xp, dtype) + return xp.concatenate((a, b, c, d, e) * 2, axis=-1) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_large_3(self, xp, dtype): + a = testing.shaped_arange((2, 3, 1), xp, dtype) + b = testing.shaped_reverse_arange((2, 3, 1), xp, dtype) + return xp.concatenate((a, b) * 10, axis=-1) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_large_4(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_reverse_arange((2, 3, 4), xp, dtype) + return xp.concatenate((a, b) * 10, axis=-1) + + @pytest.mark.skip("TODO: remove once dpctl #1325 is resolved") + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_large_5(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_reverse_arange((2, 3, 4), xp, "i") + return xp.concatenate((a, b) * 10, axis=-1) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_f_contiguous(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_arange((2, 3, 2), xp, dtype).T + c = testing.shaped_arange((2, 3, 3), xp, dtype) + return xp.concatenate((a, b, c), axis=-1) + + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_large_f_contiguous(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = testing.shaped_arange((2, 3, 2), xp, dtype).T + c = testing.shaped_arange((2, 3, 3), xp, dtype) + d = testing.shaped_arange((2, 3, 2), xp, dtype).T + e = testing.shaped_arange((2, 3, 2), xp, dtype) + return xp.concatenate((a, b, c, d, e) * 2, axis=-1) + + @pytest.mark.skip("TODO: remove once dpctl #1325 is resolved") + @testing.numpy_cupy_array_equal() + def test_concatenate_many_multi_dtype(self, xp): + a = testing.shaped_arange((2, 1), xp, "i") + b = testing.shaped_arange((2, 1), xp, "f") + return xp.concatenate((a, b) * 1024, axis=1) + + @pytest.mark.skip("dpnp.int8 is not supported yet") + @testing.slow + def test_concatenate_32bit_boundary(self): + a = cupy.zeros((2**30,), dtype=cupy.int8) + b = cupy.zeros((2**30,), dtype=cupy.int8) + ret = cupy.concatenate([a, b]) + del a + del b + del ret + # Free huge memory for slow test + cupy.get_default_memory_pool().free_all_blocks() + + def test_concatenate_wrong_ndim(self): + a = cupy.empty((2, 3)) + b = cupy.empty((2,)) + with pytest.raises(ValueError): + cupy.concatenate((a, b)) + + def test_concatenate_wrong_shape(self): + a = cupy.empty((2, 3, 4)) + b = cupy.empty((3, 3, 4)) + c = cupy.empty((4, 4, 4)) + with pytest.raises(ValueError): + cupy.concatenate((a, b, c)) + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_concatenate_out(self, xp, dtype): + a = testing.shaped_arange((3, 4), xp, dtype) + b = testing.shaped_reverse_arange((3, 4), xp, dtype) + c = testing.shaped_arange((3, 4), xp, dtype) + out = xp.zeros((3, 12), dtype=dtype) + xp.concatenate((a, b, c), axis=1, out=out) + return out + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @testing.numpy_cupy_array_equal() + def test_concatenate_out_same_kind(self, xp): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((3, 12), dtype=xp.float32) + xp.concatenate((a, b, c), axis=1, out=out) + return out + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + def test_concatenate_out_invalid_shape(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((4, 10), dtype=xp.float64) + with pytest.raises(ValueError): + xp.concatenate((a, b, c), axis=1, out=out) + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + def test_concatenate_out_invalid_shape_2(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((2, 2, 10), dtype=xp.float64) + with pytest.raises(ValueError): + xp.concatenate((a, b, c), axis=1, out=out) + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + def test_concatenate_out_invalid_dtype(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((3, 12), dtype=xp.int64) + with pytest.raises(TypeError): + xp.concatenate((a, b, c), axis=1, out=out) + + @pytest.mark.skip("TODO: remove once dpctl #1325 is resolved") + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal() + def test_concatenate_different_dtype(self, xp, dtype1, dtype2): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype2) + return xp.concatenate((a, b)) + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal(accept_error=TypeError) + def test_concatenate_out_different_dtype(self, xp, dtype1, dtype2): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + out = xp.zeros((6, 4), dtype=dtype2) + return xp.concatenate((a, b), out=out) + + @pytest.mark.skip( + "`dtype` parameter is not supported by dpnp.concatenate()" + ) + @testing.with_requires("numpy>=1.20.0") + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal(accept_error=TypeError) + def test_concatenate_dtype(self, xp, dtype1, dtype2): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + return xp.concatenate((a, b), dtype=dtype2) + + @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @testing.with_requires("numpy>=1.20.0") + def test_concatenate_dtype_invalid_out(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((6, 4), dtype=xp.int64) + with pytest.raises(TypeError): + xp.concatenate((a, b), out=out, dtype=xp.int64) + + @pytest.mark.skip( + "`casting` parameter is not supported by dpnp.concatenate()" + ) + @testing.with_requires("numpy>=1.20.0") + @pytest.mark.parametrize( + "casting", + [ + "no", + "equiv", + "safe", + "same_kind", + "unsafe", + ], + ) + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal( + accept_error=(TypeError, numpy.ComplexWarning) + ) + def test_concatenate_casting(self, xp, dtype1, dtype2, casting): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + # may raise TypeError or ComplexWarning + return xp.concatenate((a, b), dtype=dtype2, casting=casting) + + @pytest.mark.skip("dpnp.dstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_dstack(self, xp): + a = testing.shaped_arange((1, 3, 2), xp) + b = testing.shaped_arange((3,), xp) + c = testing.shaped_arange((1, 3), xp) + return xp.dstack((a, b, c)) + + @pytest.mark.skip("dpnp.dstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_dstack_single_element(self, xp): + a = testing.shaped_arange((1, 2, 3), xp) + return xp.dstack((a,)) + + @pytest.mark.skip("dpnp.dstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_dstack_single_element_2(self, xp): + a = testing.shaped_arange((1, 2), xp) + return xp.dstack((a,)) + + @pytest.mark.skip("dpnp.dstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_dstack_single_element_3(self, xp): + a = testing.shaped_arange((1,), xp) + return xp.dstack((a,)) + + @pytest.mark.skip("dpnp.hstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_hstack_vectors(self, xp): + a = xp.arange(3) + b = xp.arange(2, -1, -1) + return xp.hstack((a, b)) + + @pytest.mark.skip("dpnp.hstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_hstack_scalars(self, xp): + a = testing.shaped_arange((), xp) + b = testing.shaped_arange((), xp) + c = testing.shaped_arange((), xp) + return xp.hstack((a, b, c)) + + @pytest.mark.skip("dpnp.hstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_hstack(self, xp): + a = testing.shaped_arange((2, 1), xp) + b = testing.shaped_arange((2, 2), xp) + c = testing.shaped_arange((2, 3), xp) + return xp.hstack((a, b, c)) + + @pytest.mark.skip("dpnp.hstack() is not implemented yet") + @testing.with_requires("numpy>=1.24.0") + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal(accept_error=TypeError) + def test_hstack_dtype(self, xp, dtype1, dtype2): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + return xp.hstack((a, b), dtype=dtype2) + + @pytest.mark.skip("dpnp.hstack() is not implemented yet") + @testing.with_requires("numpy>=1.24.0") + @pytest.mark.parametrize( + "casting", + [ + "no", + "equiv", + "safe", + "same_kind", + "unsafe", + ], + ) + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal( + accept_error=(TypeError, numpy.ComplexWarning) + ) + def test_hstack_casting(self, xp, dtype1, dtype2, casting): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + # may raise TypeError or ComplexWarning + return xp.hstack((a, b), dtype=dtype2, casting=casting) + + @pytest.mark.skip("dpnp.vstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_vstack_vectors(self, xp): + a = xp.arange(3) + b = xp.arange(2, -1, -1) + return xp.vstack((a, b)) + + @pytest.mark.skip("dpnp.vstack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_vstack_single_element(self, xp): + a = xp.arange(3) + return xp.vstack((a,)) + + @pytest.mark.skip("dpnp.vstack() is not implemented yet") + def test_vstack_wrong_ndim(self): + a = cupy.empty((3,)) + b = cupy.empty((3, 1)) + with pytest.raises(ValueError): + cupy.vstack((a, b)) + + @pytest.mark.skip("dpnp.vstack() is not implemented yet") + @testing.with_requires("numpy>=1.24.0") + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal(accept_error=TypeError) + def test_vstack_dtype(self, xp, dtype1, dtype2): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + return xp.vstack((a, b), dtype=dtype2) + + @pytest.mark.skip("dpnp.vstack() is not implemented yet") + @testing.with_requires("numpy>=1.24.0") + @pytest.mark.parametrize( + "casting", + [ + "no", + "equiv", + "safe", + "same_kind", + "unsafe", + ], + ) + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal( + accept_error=(TypeError, numpy.ComplexWarning) + ) + def test_vstack_casting(self, xp, dtype1, dtype2, casting): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + # may raise TypeError or ComplexWarning + return xp.vstack((a, b), dtype=dtype2, casting=casting) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_stack(self, xp): + a = testing.shaped_arange((2, 3), xp) + b = testing.shaped_arange((2, 3), xp) + c = testing.shaped_arange((2, 3), xp) + return xp.stack((a, b, c)) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_value(self): + a = testing.shaped_arange((2, 3), cupy) + b = testing.shaped_arange((2, 3), cupy) + c = testing.shaped_arange((2, 3), cupy) + s = cupy.stack((a, b, c)) + assert s.shape == (3, 2, 3) + cupy.testing.assert_array_equal(s[0], a) + cupy.testing.assert_array_equal(s[1], b) + cupy.testing.assert_array_equal(s[2], c) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_stack_with_axis1(self, xp): + a = testing.shaped_arange((2, 3), xp) + return xp.stack((a, a), axis=1) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_stack_with_axis2(self, xp): + a = testing.shaped_arange((2, 3), xp) + return xp.stack((a, a), axis=2) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_with_axis_over(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((2, 3), xp) + with pytest.raises(ValueError): + xp.stack((a, a), axis=3) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_with_axis_value(self): + a = testing.shaped_arange((2, 3), cupy) + s = cupy.stack((a, a), axis=1) + + assert s.shape == (2, 2, 3) + cupy.testing.assert_array_equal(s[:, 0, :], a) + cupy.testing.assert_array_equal(s[:, 1, :], a) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_stack_with_negative_axis(self, xp): + a = testing.shaped_arange((2, 3), xp) + return xp.stack((a, a), axis=-1) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_with_negative_axis_value(self): + a = testing.shaped_arange((2, 3), cupy) + s = cupy.stack((a, a), axis=-1) + + assert s.shape == (2, 3, 2) + cupy.testing.assert_array_equal(s[:, :, 0], a) + cupy.testing.assert_array_equal(s[:, :, 1], a) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_different_shape(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((2, 3), xp) + b = testing.shaped_arange((2, 4), xp) + with pytest.raises(ValueError): + xp.stack([a, b]) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_out_of_bounds1(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((2, 3), xp) + with pytest.raises(ValueError): + xp.stack([a, a], axis=3) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_out_of_bounds2(self): + a = testing.shaped_arange((2, 3), cupy) + with pytest.raises(numpy.AxisError): + return cupy.stack([a, a], axis=3) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.for_all_dtypes(name="dtype") + @testing.numpy_cupy_array_equal() + def test_stack_out(self, xp, dtype): + a = testing.shaped_arange((3, 4), xp, dtype) + b = testing.shaped_reverse_arange((3, 4), xp, dtype) + c = testing.shaped_arange((3, 4), xp, dtype) + out = xp.zeros((3, 3, 4), dtype=dtype) + xp.stack((a, b, c), axis=1, out=out) + return out + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_stack_out_same_kind(self, xp): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((3, 3, 4), dtype=xp.float32) + xp.stack((a, b, c), axis=1, out=out) + return out + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_out_invalid_shape(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((3, 3, 10), dtype=xp.float64) + with pytest.raises(ValueError): + xp.stack((a, b, c), axis=1, out=out) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_out_invalid_shape_2(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((3, 3, 3, 10), dtype=xp.float64) + with pytest.raises(ValueError): + xp.stack((a, b, c), axis=1, out=out) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + def test_stack_out_invalid_dtype(self): + for xp in (numpy, cupy): + a = testing.shaped_arange((3, 4), xp, xp.float64) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) + c = testing.shaped_arange((3, 4), xp, xp.float64) + out = xp.zeros((3, 3, 4), dtype=xp.int64) + with pytest.raises(TypeError): + xp.stack((a, b, c), axis=1, out=out) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.with_requires("numpy>=1.24.0") + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal(accept_error=TypeError) + def test_stack_dtype(self, xp, dtype1, dtype2): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + return xp.stack((a, b), dtype=dtype2) + + @pytest.mark.skip("dpnp.stack() is not implemented yet") + @testing.with_requires("numpy>=1.24.0") + @pytest.mark.parametrize( + "casting", + [ + "no", + "equiv", + "safe", + "same_kind", + "unsafe", + ], + ) + @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) + @testing.numpy_cupy_array_equal( + accept_error=(TypeError, numpy.ComplexWarning) + ) + def test_stack_casting(self, xp, dtype1, dtype2, casting): + a = testing.shaped_arange((3, 4), xp, dtype1) + b = testing.shaped_arange((3, 4), xp, dtype1) + # may raise TypeError or ComplexWarning + return xp.stack((a, b), dtype=dtype2, casting=casting) + + @pytest.mark.skip("dpnp.row_stack() is not implemented yet") + @testing.for_all_dtypes(name="dtype1") + @testing.for_all_dtypes(name="dtype2") + @testing.numpy_cupy_array_equal() + def test_row_stack(self, xp, dtype1, dtype2): + a = testing.shaped_arange((4, 3), xp, dtype1) + b = testing.shaped_arange((3,), xp, dtype2) + c = testing.shaped_arange((2, 3), xp, dtype1) + return xp.row_stack((a, b, c)) + + @pytest.mark.skip("dpnp.row_stack() is not implemented yet") + def test_row_stack_wrong_ndim1(self): + a = cupy.zeros(()) + b = cupy.zeros((3,)) + with pytest.raises(ValueError): + cupy.row_stack((a, b)) + + @pytest.mark.skip("dpnp.row_stack() is not implemented yet") + def test_row_stack_wrong_ndim2(self): + a = cupy.zeros((3, 2, 3)) + b = cupy.zeros((3, 2)) + with pytest.raises(ValueError): + cupy.row_stack((a, b)) + + @pytest.mark.skip("dpnp.row_stack() is not implemented yet") + def test_row_stack_wrong_shape(self): + a = cupy.zeros((3, 2)) + b = cupy.zeros((4, 3)) + with pytest.raises(ValueError): + cupy.row_stack((a, b)) From 7e0f6eb68c5f3ffeacf564f5d23c9dbeaa9fce54 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 3 Aug 2023 20:56:07 +0200 Subject: [PATCH 2/2] Increased code coverage --- dpnp/dpnp_iface_manipulation.py | 2 +- tests/test_arraymanipulation.py | 34 +++++----- .../cupy/manipulation_tests/test_join.py | 66 ++++++++----------- tests/third_party/cupy/testing/__init__.py | 1 + tests/third_party/cupy/testing/helper.py | 38 ++++++++++- 5 files changed, 80 insertions(+), 61 deletions(-) diff --git a/dpnp/dpnp_iface_manipulation.py b/dpnp/dpnp_iface_manipulation.py index b5bd7e423eee..fca218be02fc 100644 --- a/dpnp/dpnp_iface_manipulation.py +++ b/dpnp/dpnp_iface_manipulation.py @@ -237,7 +237,7 @@ def broadcast_to(x, /, shape, subok=False): return call_origin(numpy.broadcast_to, x, shape=shape, subok=subok) -def concatenate(arrays, axis=0, out=None, *, dtype=None, **kwargs): +def concatenate(arrays, *, axis=0, out=None, dtype=None, **kwargs): """ Join a sequence of arrays along an existing axis. diff --git a/tests/test_arraymanipulation.py b/tests/test_arraymanipulation.py index 0be647765c0e..68e8f4af47a5 100644 --- a/tests/test_arraymanipulation.py +++ b/tests/test_arraymanipulation.py @@ -211,13 +211,13 @@ def test_large_concatenate_axis_None(self, dtype): np_a = numpy.arange(start, stop, dtype=dtype) dp_a = dpnp.arange(start, stop, dtype=dtype) - np_res = numpy.concatenate(np_a, None) - dp_res = dpnp.concatenate(dp_a, None) + np_res = numpy.concatenate(np_a, axis=None) + dp_res = dpnp.concatenate(dp_a, axis=None) assert_array_equal(dp_res.asnumpy(), np_res) # numpy doesn't raise an exception here but probably should with pytest.raises(numpy.AxisError): - dpnp.concatenate(dp_a, 100) + dpnp.concatenate(dp_a, axis=100) @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) def test_concatenate(self, dtype): @@ -241,13 +241,13 @@ def test_concatenate(self, dtype): assert_array_equal(dp_res.asnumpy(), np_res) # Explicit axis specification - np_res = numpy.concatenate((np_r4, np_r3), 0) - dp_res = dpnp.concatenate((dp_r4, dp_r3), 0) + np_res = numpy.concatenate((np_r4, np_r3), axis=0) + dp_res = dpnp.concatenate((dp_r4, dp_r3), axis=0) assert_array_equal(dp_res.asnumpy(), np_res) # Including negative - np_res = numpy.concatenate((np_r4, np_r3), -1) - dp_res = dpnp.concatenate((dp_r4, dp_r3), -1) + np_res = numpy.concatenate((np_r4, np_r3), axis=-1) + dp_res = dpnp.concatenate((dp_r4, dp_r3), axis=-1) assert_array_equal(dp_res.asnumpy(), np_res) @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) @@ -262,19 +262,19 @@ def test_concatenate_2d(self, dtype): dp_res = dpnp.concatenate((dp_a23, dp_a13)) assert_array_equal(dp_res.asnumpy(), np_res) - np_res = numpy.concatenate((np_a23, np_a13), 0) - dp_res = dpnp.concatenate((dp_a23, dp_a13), 0) + np_res = numpy.concatenate((np_a23, np_a13), axis=0) + dp_res = dpnp.concatenate((dp_a23, dp_a13), axis=0) assert_array_equal(dp_res.asnumpy(), np_res) for axis in [1, -1]: - np_res = numpy.concatenate((np_a23.T, np_a13.T), axis) - dp_res = dpnp.concatenate((dp_a23.T, dp_a13.T), axis) + np_res = numpy.concatenate((np_a23.T, np_a13.T), axis=axis) + dp_res = dpnp.concatenate((dp_a23.T, dp_a13.T), axis=axis) assert_array_equal(dp_res.asnumpy(), np_res) # Arrays much match shape with pytest.raises(ValueError): - numpy.concatenate((np_a23.T, np_a13.T), 0) - dpnp.concatenate((dp_a23.T, dp_a13.T), 0) + numpy.concatenate((np_a23.T, np_a13.T), axis=0) + dpnp.concatenate((dp_a23.T, dp_a13.T), axis=0) @pytest.mark.parametrize( "dtype", get_all_dtypes(no_bool=True, no_none=True) @@ -291,12 +291,12 @@ def test_concatenate_3d(self, dtype): dp_a2 = dp_a[..., 6:] for axis in [2, -1]: - np_res = numpy.concatenate((np_a0, np_a1, np_a2), axis) - dp_res = dpnp.concatenate((dp_a0, dp_a1, dp_a2), axis) + np_res = numpy.concatenate((np_a0, np_a1, np_a2), axis=axis) + dp_res = dpnp.concatenate((dp_a0, dp_a1, dp_a2), axis=axis) assert_array_equal(dp_res.asnumpy(), np_res) - np_res = numpy.concatenate((np_a0.T, np_a1.T, np_a2.T), 0) - dp_res = dpnp.concatenate((dp_a0.T, dp_a1.T, dp_a2.T), 0) + np_res = numpy.concatenate((np_a0.T, np_a1.T, np_a2.T), axis=0) + dp_res = dpnp.concatenate((dp_a0.T, dp_a1.T, dp_a2.T), axis=0) assert_array_equal(dp_res.asnumpy(), np_res) @pytest.mark.skip("out keyword is currently unsupported") diff --git a/tests/third_party/cupy/manipulation_tests/test_join.py b/tests/third_party/cupy/manipulation_tests/test_join.py index b90840c88548..e302065ca997 100644 --- a/tests/third_party/cupy/manipulation_tests/test_join.py +++ b/tests/third_party/cupy/manipulation_tests/test_join.py @@ -145,7 +145,7 @@ def test_concatenate_wrong_shape(self): with pytest.raises(ValueError): cupy.concatenate((a, b, c)) - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(name="dtype") @testing.numpy_cupy_array_equal() def test_concatenate_out(self, xp, dtype): @@ -156,42 +156,42 @@ def test_concatenate_out(self, xp, dtype): xp.concatenate((a, b, c), axis=1, out=out) return out - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_concatenate_out_same_kind(self, xp): - a = testing.shaped_arange((3, 4), xp, xp.float64) - b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) - c = testing.shaped_arange((3, 4), xp, xp.float64) + a = testing.shaped_arange((3, 4), xp, xp.float32) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float32) + c = testing.shaped_arange((3, 4), xp, xp.float32) out = xp.zeros((3, 12), dtype=xp.float32) xp.concatenate((a, b, c), axis=1, out=out) return out - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_concatenate_out_invalid_shape(self): for xp in (numpy, cupy): - a = testing.shaped_arange((3, 4), xp, xp.float64) - b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) - c = testing.shaped_arange((3, 4), xp, xp.float64) - out = xp.zeros((4, 10), dtype=xp.float64) + a = testing.shaped_arange((3, 4), xp, xp.float32) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float32) + c = testing.shaped_arange((3, 4), xp, xp.float32) + out = xp.zeros((4, 10), dtype=xp.float32) with pytest.raises(ValueError): xp.concatenate((a, b, c), axis=1, out=out) - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_concatenate_out_invalid_shape_2(self): for xp in (numpy, cupy): - a = testing.shaped_arange((3, 4), xp, xp.float64) - b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) - c = testing.shaped_arange((3, 4), xp, xp.float64) - out = xp.zeros((2, 2, 10), dtype=xp.float64) + a = testing.shaped_arange((3, 4), xp, xp.float32) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float32) + c = testing.shaped_arange((3, 4), xp, xp.float32) + out = xp.zeros((2, 2, 10), dtype=xp.float32) with pytest.raises(ValueError): xp.concatenate((a, b, c), axis=1, out=out) - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_concatenate_out_invalid_dtype(self): for xp in (numpy, cupy): - a = testing.shaped_arange((3, 4), xp, xp.float64) - b = testing.shaped_reverse_arange((3, 4), xp, xp.float64) - c = testing.shaped_arange((3, 4), xp, xp.float64) + a = testing.shaped_arange((3, 4), xp, xp.float32) + b = testing.shaped_reverse_arange((3, 4), xp, xp.float32) + c = testing.shaped_arange((3, 4), xp, xp.float32) out = xp.zeros((3, 12), dtype=xp.int64) with pytest.raises(TypeError): xp.concatenate((a, b, c), axis=1, out=out) @@ -204,7 +204,7 @@ def test_concatenate_different_dtype(self, xp, dtype1, dtype2): b = testing.shaped_arange((3, 4), xp, dtype2) return xp.concatenate((a, b)) - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) @testing.numpy_cupy_array_equal(accept_error=TypeError) def test_concatenate_out_different_dtype(self, xp, dtype1, dtype2): @@ -213,9 +213,7 @@ def test_concatenate_out_different_dtype(self, xp, dtype1, dtype2): out = xp.zeros((6, 4), dtype=dtype2) return xp.concatenate((a, b), out=out) - @pytest.mark.skip( - "`dtype` parameter is not supported by dpnp.concatenate()" - ) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.with_requires("numpy>=1.20.0") @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) @testing.numpy_cupy_array_equal(accept_error=TypeError) @@ -224,30 +222,19 @@ def test_concatenate_dtype(self, xp, dtype1, dtype2): b = testing.shaped_arange((3, 4), xp, dtype1) return xp.concatenate((a, b), dtype=dtype2) - @pytest.mark.skip("`out` parameter is not supported by dpnp.concatenate()") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.with_requires("numpy>=1.20.0") def test_concatenate_dtype_invalid_out(self): for xp in (numpy, cupy): - a = testing.shaped_arange((3, 4), xp, xp.float64) - b = testing.shaped_arange((3, 4), xp, xp.float64) + a = testing.shaped_arange((3, 4), xp, xp.float32) + b = testing.shaped_arange((3, 4), xp, xp.float32) out = xp.zeros((6, 4), dtype=xp.int64) with pytest.raises(TypeError): xp.concatenate((a, b), out=out, dtype=xp.int64) - @pytest.mark.skip( - "`casting` parameter is not supported by dpnp.concatenate()" - ) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.with_requires("numpy>=1.20.0") - @pytest.mark.parametrize( - "casting", - [ - "no", - "equiv", - "safe", - "same_kind", - "unsafe", - ], - ) + @testing.for_castings() @testing.for_all_dtypes_combination(names=["dtype1", "dtype2"]) @testing.numpy_cupy_array_equal( accept_error=(TypeError, numpy.ComplexWarning) @@ -255,7 +242,6 @@ def test_concatenate_dtype_invalid_out(self): def test_concatenate_casting(self, xp, dtype1, dtype2, casting): a = testing.shaped_arange((3, 4), xp, dtype1) b = testing.shaped_arange((3, 4), xp, dtype1) - # may raise TypeError or ComplexWarning return xp.concatenate((a, b), dtype=dtype2, casting=casting) @pytest.mark.skip("dpnp.dstack() is not implemented yet") diff --git a/tests/third_party/cupy/testing/__init__.py b/tests/third_party/cupy/testing/__init__.py index dfa1f7d27316..701c381e2f30 100644 --- a/tests/third_party/cupy/testing/__init__.py +++ b/tests/third_party/cupy/testing/__init__.py @@ -32,6 +32,7 @@ assert_warns, for_all_dtypes, for_all_dtypes_combination, + for_castings, for_CF_orders, for_complex_dtypes, for_dtypes, diff --git a/tests/third_party/cupy/testing/helper.py b/tests/third_party/cupy/testing/helper.py index ebdf38b86972..c78dc07d9997 100644 --- a/tests/third_party/cupy/testing/helper.py +++ b/tests/third_party/cupy/testing/helper.py @@ -1203,12 +1203,12 @@ def for_orders(orders, name="order"): """ def decorator(impl): - @functools.wraps(impl) - def test_func(self, *args, **kw): + @_wraps_partial(impl, name) + def test_func(*args, **kw): for order in orders: try: kw[name] = order - impl(self, *args, **kw) + impl(*args, **kw) except Exception: print(name, "is", order) raise @@ -1230,6 +1230,38 @@ def for_CF_orders(name="order"): return for_orders([None, "C", "c"], name) +def for_castings(castings=None, name="casting"): + """Decorator to parameterize tests with casting. + + Args: + castings(list of casting): casting to be tested. + name(str): Argument name to which the specified casting is passed. + + This decorator adds a keyword argument specified by ``name`` + to the test fixtures. Then, the fixtures run by passing each element of + ``castings`` to the named argument. + + """ + + if castings is None: + castings = ["no", "equiv", "safe", "same_kind", "unsafe"] + + def decorator(impl): + @_wraps_partial(impl, name) + def test_func(*args, **kw): + for casting in castings: + try: + kw[name] = casting + impl(*args, **kw) + except Exception: + print(name, "is", casting) + raise + + return test_func + + return decorator + + def with_requires(*requirements): """Run a test case only when given requirements are satisfied.