Skip to content

Commit 2783830

Browse files
New implementation of dpnp.outer (#1436)
* Add a new implementation of dpnp.outer * Update dpnp.outer implementation --------- Co-authored-by: Anton <[email protected]>
1 parent 126e2ca commit 2783830

File tree

4 files changed

+24
-46
lines changed

4 files changed

+24
-46
lines changed

dpnp/dpnp_algo/dpnp_algo_linearalgebra.pxi

-25
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ __all__ += [
4040
"dpnp_inner",
4141
"dpnp_kron",
4242
"dpnp_matmul",
43-
"dpnp_outer"
4443
]
4544

4645

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

380379
return result
381-
382-
383-
cpdef utils.dpnp_descriptor dpnp_outer(utils.dpnp_descriptor array1, utils.dpnp_descriptor array2):
384-
cdef shape_type_c result_shape = (array1.size, array2.size)
385-
result_type = numpy.promote_types(array1.dtype, array1.dtype)
386-
387-
result_sycl_device, result_usm_type, result_sycl_queue = utils.get_common_usm_allocation(array1, array2)
388-
389-
cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(result_shape,
390-
result_type,
391-
None,
392-
device=result_sycl_device,
393-
usm_type=result_usm_type,
394-
sycl_queue=result_sycl_queue)
395-
396-
result_flatiter = result.get_pyobj().flat
397-
array1_flatiter = array1.get_pyobj().flat
398-
array2_flatiter = array2.get_pyobj().flat
399-
400-
for idx1 in range(array1.size):
401-
for idx2 in range(array2.size):
402-
result_flatiter[idx1 * array2.size + idx2] = array1_flatiter[idx1] * array2_flatiter[idx2]
403-
404-
return result

dpnp/dpnp_iface_linearalgebra.py

+20-15
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def dot(x1, x2, out=None, **kwargs):
7373
y : dpnp.ndarray
7474
Returns the dot product of `x1` and `x2`.
7575
If `out` is given, then it is returned.
76-
76+
7777
Limitations
7878
-----------
7979
Parameters `x1` and `x2` are supported as either scalar, :class:`dpnp.ndarray`
@@ -298,16 +298,16 @@ def matmul(x1, x2, out=None, **kwargs):
298298
return call_origin(numpy.matmul, x1, x2, out=out, **kwargs)
299299

300300

301-
def outer(x1, x2, **kwargs):
301+
def outer(x1, x2, out=None):
302302
"""
303303
Returns the outer product of two arrays.
304304
305305
For full documentation refer to :obj:`numpy.outer`.
306306
307307
Limitations
308308
-----------
309-
Parameters ``x1`` and ``x2`` are supported as :obj:`dpnp.ndarray`.
310-
Keyword arguments ``kwargs`` are currently unsupported.
309+
Parameters `x1` and `x2` are supported as either scalar, :class:`dpnp.ndarray`
310+
or :class:`dpctl.tensor.usm_ndarray`, but both `x1` and `x2` can not be scalars at the same time.
311311
Otherwise the functions will be executed sequentially on CPU.
312312
Input array data types are limited by supported DPNP :ref:`Data types`.
313313
@@ -323,21 +323,26 @@ def outer(x1, x2, **kwargs):
323323
>>> b = np.array([1, 2, 3])
324324
>>> result = np.outer(a, b)
325325
>>> [x for x in result]
326-
[1, 2, 3, 1, 2, 3, 1, 2, 3]
326+
array([[1, 2, 3],
327+
[1, 2, 3],
328+
[1, 2, 3]])
327329
328330
"""
331+
x1_is_scalar = dpnp.isscalar(x1)
332+
x2_is_scalar = dpnp.isscalar(x2)
329333

330-
if not kwargs:
331-
if isinstance(x1, dpnp_array) and isinstance(x2, dpnp_array):
332-
ravel = lambda x: x.flatten() if x.ndim > 1 else x
333-
return ravel(x1)[:, None] * ravel(x2)[None, :]
334-
335-
x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False)
336-
x2_desc = dpnp.get_dpnp_descriptor(x2, copy_when_nondefault_queue=False)
337-
if x1_desc and x2_desc:
338-
return dpnp_outer(x1_desc, x2_desc).get_pyobj()
334+
if x1_is_scalar and x2_is_scalar:
335+
pass
336+
elif not (x1_is_scalar or dpnp.is_supported_array_type(x1)):
337+
pass
338+
elif not (x2_is_scalar or dpnp.is_supported_array_type(x2)):
339+
pass
340+
else:
341+
x1_in = x1 if x1_is_scalar else (x1.reshape(-1) if x1.ndim > 1 else x1)[:, None]
342+
x2_in = x2 if x2_is_scalar else (x2.reshape(-1) if x2.ndim > 1 else x2)[None, :]
343+
return dpnp.multiply(x1_in, x2_in, out=out)
339344

340-
return call_origin(numpy.outer, x1, x2, **kwargs)
345+
return call_origin(numpy.outer, x1, x2, out=out)
341346

342347

343348
def tensordot(x1, x2, axes=2):

tests/skipped_tests_gpu.tbl

-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ tests/third_party/cupy/linalg_tests/test_einsum.py::TestEinSumError::test_too_ma
210210
tests/third_party/cupy/linalg_tests/test_einsum.py::TestListArgEinSumError::test_dim_mismatch3
211211
tests/third_party/cupy/linalg_tests/test_einsum.py::TestListArgEinSumError::test_too_many_dims3
212212

213-
tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_reversed_outer
214213
tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_reversed_vdot
215214
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_7_{dst_shape=(0,), src=3.2}::test_copyto_where
216215
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_8_{dst_shape=(0,), src=0}::test_copyto_where

tests/test_outer.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import dpnp as dp
55
import numpy as np
6+
import pytest
67

78
from numpy.testing import assert_raises
89

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

4142
class TestScalarOuter(unittest.TestCase):
4243

43-
@unittest.skip("A scalar isn't currently supported as input")
4444
@testing.for_all_dtypes()
45-
@testing.numpy_cupy_allclose()
45+
@testing.numpy_cupy_allclose(type_check=False)
4646
def test_first_is_scalar(self, xp, dtype):
4747
scalar = xp.int64(4)
4848
a = xp.arange(5**3, dtype=dtype).reshape(5, 5, 5)
4949
return xp.outer(scalar, a)
5050

51-
@unittest.skip("A scalar isn't currently supported as input")
5251
@testing.for_all_dtypes()
53-
@testing.numpy_cupy_allclose()
52+
@testing.numpy_cupy_allclose(type_check=False)
5453
def test_second_is_scalar(self, xp, dtype):
5554
scalar = xp.int32(7)
5655
a = xp.arange(5**3, dtype=dtype).reshape(5, 5, 5)
5756
return xp.outer(a, scalar)
5857

59-
@unittest.skip("A scalar isn't currently supported as input")
58+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
6059
@testing.numpy_cupy_array_equal()
6160
def test_both_inputs_as_scalar(self, xp):
6261
a = xp.int64(4)

0 commit comments

Comments
 (0)