IntelPython / dpctl

Python SYCL bindings and SYCL-based Python Array API library
https://intelpython.github.io/dpctl/
Apache License 2.0
99 stars 30 forks source link

Shape setter does not work with integer scalar #1769

Closed antonwolfy closed 2 months ago

antonwolfy commented 2 months ago

The below example reproduces an issue with passing integer scalar to the shape setter:

import numpy, dpctl, dpctl.tensor as dpt

dpctl.__version__
# Out: '0.18.0dev0+158.g7450558d25'

a = dpt.usm_ndarray((2, 3))
a.shape = 6
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 a.shape = 6

File dpctl/tensor/_usmarray.pyx:579, in dpctl.tensor._usmarray.usm_ndarray.shape.__set__()

TypeError: object of type 'int' has no len()

# but works in numpy
a = numpy.ndarray((2, 3))
a.shape = 6
oleksandr-pavlyk commented 2 months ago

The .shape setter requires value to be a tuple, or a list, or a object with __len__. This is different from NumPy's behavior:


In [30]: a = dpt.ones((2,3))

In [31]: class Six:
    ...:     def __init__(self, dim=1):
    ...:         self.v = (1,) * (dim - 1) + (6,)
    ...:     def __len__(self):
    ...:         return len(self.v)
    ...:     def __iter__(self):
    ...:         return iter(self.v)
    ...:

In [32]: a.shape = Six(3)

In [33]: a
Out[33]: usm_ndarray([[[1., 1., 1., 1., 1., 1.]]], dtype=float32)

In [34]: a.shape
Out[34]: (1, 1, 6)

In [35]: import numpy as np

In [36]: b = np.ones((2,3))

In [37]: b.shape = Six(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[37], line 1
----> 1 b.shape = Six(3)

TypeError: expected a sequence of integers or a single integer, got '<__main__.Six object at 0x7f7ee86258e0>'
antonwolfy commented 2 months ago

Should that be properly clarified in the documentation? Since it might be unclear that .shape setter and reshape method behaves differently (different requirements on supported type of new shape argument) in case when the reshaping to the requested dimensions can be returned as a view.

a = dpt.usm_ndarray((2, 3))
dpt.reshape(a, 6, copy=False)
# Out: usm_ndarray([0., 0., 0., 0., 0., 0.])