Skip to content
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

New implementation of dpnp.outer #1436

Merged
merged 6 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions dpnp/dpnp_algo/dpnp_algo_linearalgebra.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ __all__ += [
"dpnp_inner",
"dpnp_kron",
"dpnp_matmul",
"dpnp_outer"
]


Expand Down Expand Up @@ -378,27 +377,3 @@ cpdef utils.dpnp_descriptor dpnp_matmul(utils.dpnp_descriptor in_array1, utils.d
c_dpctl.DPCTLEvent_Delete(event_ref)

return result


cpdef utils.dpnp_descriptor dpnp_outer(utils.dpnp_descriptor array1, utils.dpnp_descriptor array2):
cdef shape_type_c result_shape = (array1.size, array2.size)
result_type = numpy.promote_types(array1.dtype, array1.dtype)

result_sycl_device, result_usm_type, result_sycl_queue = utils.get_common_usm_allocation(array1, array2)

cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(result_shape,
result_type,
None,
device=result_sycl_device,
usm_type=result_usm_type,
sycl_queue=result_sycl_queue)

result_flatiter = result.get_pyobj().flat
array1_flatiter = array1.get_pyobj().flat
array2_flatiter = array2.get_pyobj().flat

for idx1 in range(array1.size):
for idx2 in range(array2.size):
result_flatiter[idx1 * array2.size + idx2] = array1_flatiter[idx1] * array2_flatiter[idx2]

return result
35 changes: 20 additions & 15 deletions dpnp/dpnp_iface_linearalgebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def dot(x1, x2, out=None, **kwargs):
y : dpnp.ndarray
Returns the dot product of `x1` and `x2`.
If `out` is given, then it is returned.

Limitations
-----------
Parameters `x1` and `x2` are supported as either scalar, :class:`dpnp.ndarray`
Expand Down Expand Up @@ -298,16 +298,16 @@ def matmul(x1, x2, out=None, **kwargs):
return call_origin(numpy.matmul, x1, x2, out=out, **kwargs)


def outer(x1, x2, **kwargs):
def outer(x1, x2, out=None):
"""
Returns the outer product of two arrays.

For full documentation refer to :obj:`numpy.outer`.

Limitations
-----------
Parameters ``x1`` and ``x2`` are supported as :obj:`dpnp.ndarray`.
Keyword arguments ``kwargs`` are currently unsupported.
Parameters `x1` and `x2` are supported as either scalar, :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`, but both `x1` and `x2` can not be scalars at the same time.
Otherwise the functions will be executed sequentially on CPU.
Input array data types are limited by supported DPNP :ref:`Data types`.

Expand All @@ -323,21 +323,26 @@ def outer(x1, x2, **kwargs):
>>> b = np.array([1, 2, 3])
>>> result = np.outer(a, b)
>>> [x for x in result]
[1, 2, 3, 1, 2, 3, 1, 2, 3]
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])

"""
x1_is_scalar = dpnp.isscalar(x1)
x2_is_scalar = dpnp.isscalar(x2)

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()
if x1_is_scalar and x2_is_scalar:
pass
elif not (x1_is_scalar or dpnp.is_supported_array_type(x1)):
pass
elif not (x2_is_scalar or dpnp.is_supported_array_type(x2)):
pass
else:
x1_in = x1 if x1_is_scalar else (x1.reshape(-1) if x1.ndim > 1 else x1)[:, None]
x2_in = x2 if x2_is_scalar else (x2.reshape(-1) if x2.ndim > 1 else x2)[None, :]
return dpnp.multiply(x1_in, x2_in, out=out)

return call_origin(numpy.outer, x1, x2, **kwargs)
return call_origin(numpy.outer, x1, x2, out=out)


def tensordot(x1, x2, axes=2):
Expand Down
1 change: 0 additions & 1 deletion tests/skipped_tests_gpu.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ tests/third_party/cupy/linalg_tests/test_einsum.py::TestEinSumError::test_too_ma
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_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
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_8_{dst_shape=(0,), src=0}::test_copyto_where
Expand Down
9 changes: 4 additions & 5 deletions tests/test_outer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import dpnp as dp
import numpy as np
import pytest

from numpy.testing import assert_raises

Expand Down Expand Up @@ -40,23 +41,21 @@ def test_the_same_matrix(self, xp, dtype):

class TestScalarOuter(unittest.TestCase):

@unittest.skip("A scalar isn't currently supported as input")
@testing.for_all_dtypes()
@testing.numpy_cupy_allclose()
@testing.numpy_cupy_allclose(type_check=False)
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()
@testing.numpy_cupy_allclose(type_check=False)
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")
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@testing.numpy_cupy_array_equal()
def test_both_inputs_as_scalar(self, xp):
a = xp.int64(4)
Expand Down