From 29cffabdd23a89668770e1756e6cbfe6b0f4328c Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 18 Nov 2022 10:43:42 -0600 Subject: [PATCH] Improve outer method --- dpnp/dpnp_iface_linearalgebra.py | 14 ++++-- tests/skipped_tests_gpu.tbl | 4 +- tests/test_outer.py | 77 ++++++++++++++++++++++++++++++++ tests/test_sycl_queue.py | 15 ++++--- 4 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 tests/test_outer.py diff --git a/dpnp/dpnp_iface_linearalgebra.py b/dpnp/dpnp_iface_linearalgebra.py index 7cd76d7c22b5..1fd6eba2d9cc 100644 --- a/dpnp/dpnp_iface_linearalgebra.py +++ b/dpnp/dpnp_iface_linearalgebra.py @@ -40,6 +40,7 @@ """ +from dpnp.dpnp_array import dpnp_array from dpnp.dpnp_algo import * from dpnp.dpnp_utils import * import dpnp @@ -312,10 +313,15 @@ def outer(x1, x2, **kwargs): """ - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) - x2_desc = dpnp.get_dpnp_descriptor(x2, copy_when_nondefault_queue=False) - if x1_desc and x2_desc and not kwargs: - return dpnp_outer(x1_desc, x2_desc).get_pyobj() + if not kwargs: + if isinstance(x1, dpnp_array) and isinstance(x2, dpnp_array): + ravel = lambda x: x.flatten() if x.ndim > 1 else x + return ravel(x1)[:, None] * ravel(x2)[None, :] + + x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) + x2_desc = dpnp.get_dpnp_descriptor(x2, copy_when_nondefault_queue=False) + if x1_desc and x2_desc: + return dpnp_outer(x1_desc, x2_desc).get_pyobj() return call_origin(numpy.outer, x1, x2, **kwargs) diff --git a/tests/skipped_tests_gpu.tbl b/tests/skipped_tests_gpu.tbl index f4d829d630d1..b3b8742aa23f 100644 --- a/tests/skipped_tests_gpu.tbl +++ b/tests/skipped_tests_gpu.tbl @@ -286,7 +286,7 @@ tests/third_party/cupy/linalg_tests/test_einsum.py::TestEinSumError::test_too_fe tests/third_party/cupy/linalg_tests/test_einsum.py::TestEinSumError::test_too_many_dimension3 tests/third_party/cupy/linalg_tests/test_einsum.py::TestListArgEinSumError::test_dim_mismatch3 tests/third_party/cupy/linalg_tests/test_einsum.py::TestListArgEinSumError::test_too_many_dims3 -tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_outer + tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_reversed_outer tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_reversed_vdot tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_7_{dst_shape=(0,), src=3.2}::test_copyto_where @@ -347,8 +347,6 @@ tests/third_party/cupy/sorting_tests/test_sort.py::TestPartition_param_2_{extern tests/third_party/cupy/statistics_tests/test_correlation.py::TestCov::test_cov_empty tests/third_party/cupy/statistics_tests/test_meanvar.py::TestMeanVar::test_external_mean_axis -tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_multidim_outer - tests/third_party/cupy/sorting_tests/test_sort.py::TestPartition_param_3_{external=True, length=20000}::test_partition_axis tests/third_party/cupy/sorting_tests/test_sort.py::TestPartition_param_3_{external=True, length=20000}::test_partition_negative_axis tests/third_party/cupy/sorting_tests/test_sort.py::TestPartition_param_3_{external=True, length=20000}::test_partition_none_axis diff --git a/tests/test_outer.py b/tests/test_outer.py new file mode 100644 index 000000000000..6c91fad45df4 --- /dev/null +++ b/tests/test_outer.py @@ -0,0 +1,77 @@ +import unittest +from tests.third_party.cupy import testing + +import dpnp as dp +import numpy as np + +from numpy.testing import assert_raises + + +class TestOuter(unittest.TestCase): + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose() + def test_two_vectors(self, xp, dtype): + a = xp.ones((10, ), dtype=dtype) + b = xp.linspace(-2, 2, 5, dtype=dtype) + + return xp.outer(a, b) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose() + def test_two_matrix(self, xp, dtype): + a = xp.ones((10, 10, 10), dtype=dtype) + b = xp.full(shape=(3, 7), fill_value=42, dtype=dtype) + + return xp.outer(a, b) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose() + def test_the_same_vector(self, xp, dtype): + a = xp.full(shape=(100, ), fill_value=7, dtype=dtype) + return xp.outer(a, a) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose() + def test_the_same_matrix(self, xp, dtype): + a = xp.arange(27, dtype=dtype).reshape(3, 3, 3) + return xp.outer(a, a) + + +class TestScalarOuter(unittest.TestCase): + + @unittest.skip("A scalar isn't currently supported as input") + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose() + def test_first_is_scalar(self, xp, dtype): + scalar = xp.int64(4) + a = xp.arange(5**3, dtype=dtype).reshape(5, 5, 5) + return xp.outer(scalar, a) + + @unittest.skip("A scalar isn't currently supported as input") + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose() + def test_second_is_scalar(self, xp, dtype): + scalar = xp.int32(7) + a = xp.arange(5**3, dtype=dtype).reshape(5, 5, 5) + return xp.outer(a, scalar) + + @unittest.skip("A scalar isn't currently supported as input") + @testing.numpy_cupy_array_equal() + def test_both_inputs_as_scalar(self, xp): + a = xp.int64(4) + b = xp.int32(17) + return xp.outer(a, b) + + +class TestListOuter(unittest.TestCase): + + def test_list(self): + a = np.arange(27).reshape(3, 3, 3) + b: list[list[list[int]]] = a.tolist() + dp_a = dp.array(a) + + with assert_raises(NotImplementedError): + dp.outer(b, dp_a) + dp.outer(dp_a, b) + dp.outer(b, b) diff --git a/tests/test_sycl_queue.py b/tests/test_sycl_queue.py index f5b5fd1a74ba..771e9118837c 100644 --- a/tests/test_sycl_queue.py +++ b/tests/test_sycl_queue.py @@ -180,6 +180,9 @@ def test_1in_1out(func, data, device): pytest.param("fmod", [-3., -2., -1., 1., 2., 3.], [2., 2., 2., 2., 2., 2.]), + pytest.param("matmul", + [[1., 0.], [0., 1.]], + [[4., 1.], [1., 2.]]), pytest.param("maximum", [2., 3., 4.], [1., 5., 2.]), @@ -189,6 +192,9 @@ def test_1in_1out(func, data, device): pytest.param("multiply", [0., 1., 2., 3., 4., 5., 6., 7., 8.], [0., 1., 2., 0., 1., 2., 0., 1., 2.]), + pytest.param("outer", + [0., 1., 2., 3., 4., 5.], + [0., 1., 2., 0.]), pytest.param("power", [0., 1., 2., 3., 4., 5.], [1., 2., 3., 3., 2., 1.]), @@ -198,9 +204,6 @@ def test_1in_1out(func, data, device): pytest.param("subtract", [0., 1., 2., 3., 4., 5., 6., 7., 8.], [0., 1., 2., 0., 1., 2., 0., 1., 2.]), - pytest.param("matmul", - [[1., 0.], [0., 1.]], - [[4., 1.], [1., 2.]]), ], ) @pytest.mark.parametrize("device", @@ -217,10 +220,8 @@ def test_2in_1out(func, data1, data2, device): numpy.testing.assert_array_equal(result, expected) - expected_queue = x1.get_array().sycl_queue - result_queue = result.get_array().sycl_queue - - assert_sycl_queue_equal(result_queue, expected_queue) + assert_sycl_queue_equal(result.sycl_queue, x1.sycl_queue) + assert_sycl_queue_equal(result.sycl_queue, x2.sycl_queue) @pytest.mark.parametrize(