Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
6 changes: 6 additions & 0 deletions python/paddle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@


from .tensor.manipulation import ( # noqa: F401
atleast_1d,
atleast_2d,
atleast_3d,
cast,
cast_,
concat,
Expand Down Expand Up @@ -828,6 +831,9 @@
'logspace',
'reshape',
'reshape_',
'atleast_1d',
'atleast_2d',
'atleast_3d',
'reverse',
'nonzero',
'CUDAPinnedPlace',
Expand Down
3 changes: 3 additions & 0 deletions python/paddle/tensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@
from .logic import isclose # noqa: F401
from .logic import equal_all # noqa: F401
from .logic import is_tensor # noqa: F401
from .manipulation import atleast_1d # noqa: F401
from .manipulation import atleast_2d # noqa: F401
from .manipulation import atleast_3d # noqa: F401
Comment on lines +115 to +117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we need to support the usage of x.atleast_1d(), x.atleast_2d(), x.atleast_3d() when x is a tensor ? If so, add them to the list of tensor_method_func below

from .manipulation import cast # noqa: F401
from .manipulation import cast_ # noqa: F401
from .manipulation import concat # noqa: F401
Expand Down
187 changes: 187 additions & 0 deletions python/paddle/tensor/manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3886,6 +3886,193 @@ def reshape_(x, shape, name=None):
return out


def atleast_1d(*inputs, name=None):
"""
Convert inputs to tensors and return the view with at least 1-dimension. Scalar inputs are converted,
one or high-dimensional inputs are preserved.

Args:
inputs (Tensor|list(Tensor)): One or more tensors. The data type is ``float16``, ``uint16``, ``float32``, ``float64``, ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``complex64``, ``complex128``, ``bfloat16``.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.

Note:
``int8``, ``uint8``, ``complex64``, ``complex128`` are not supported in static graph mode.

Returns:
One Tensor, if there is only one input.
List of Tensors, if there are more than one inputs.

Examples:
.. code-block:: python

>>> import paddle

>>> # one input
>>> x = paddle.to_tensor(123, dtype='int32')
>>> out = paddle.atleast_1d(x)
>>> print(out)
Tensor(shape=[1], dtype=int32, place=Place(cpu), stop_gradient=True,
[123])

>>> # more than one inputs
>>> x = paddle.to_tensor(123, dtype='int32')
>>> y = paddle.to_tensor([1.23], dtype='float32')
>>> out = paddle.atleast_1d(x, y)
>>> print(out)
[Tensor(shape=[1], dtype=int32, place=Place(cpu), stop_gradient=True,
[123]), Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
[1.23000002])]

>>> # more than 1-D input
>>> x = paddle.to_tensor(123, dtype='int32')
>>> y = paddle.to_tensor([[1.23]], dtype='float32')
>>> out = paddle.atleast_1d(x, y)
>>> print(out)
[Tensor(shape=[1], dtype=int32, place=Place(cpu), stop_gradient=True,
[123]), Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
[[1.23000002]])]
"""
out = []
for tensor in inputs:
tensor = paddle.to_tensor(tensor)
if tensor.dim() == 0:
result = tensor.reshape((1,))
else:
result = tensor
out.append(result)

if len(out) == 1:
return out[0]
else:
return out


def atleast_2d(*inputs, name=None):
"""
Convert inputs to tensors and return the view with at least 2-dimension. Two or high-dimensional inputs are preserved.

Args:
inputs (Tensor|list(Tensor)): One or more tensors. The data type is ``float16``, ``uint16``, ``float32``, ``float64``, ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``complex64``, ``complex128``, ``bfloat16``.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.

Note:
``int8``, ``uint8``, ``complex64``, ``complex128`` are not supported in static graph mode.

Returns:
One Tensor, if there is only one input.
List of Tensors, if there are more than one inputs.

Examples:
.. code-block:: python

>>> import paddle

>>> # one input
>>> x = paddle.to_tensor(123, dtype='int32')
>>> out = paddle.atleast_2d(x)
>>> print(out)
Tensor(shape=[1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
[[123]])

>>> # more than one inputs
>>> x = paddle.to_tensor(123, dtype='int32')
>>> y = paddle.to_tensor([1.23], dtype='float32')
>>> out = paddle.atleast_2d(x, y)
>>> print(out)
[Tensor(shape=[1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
[[123]]), Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
[[1.23000002]])]

>>> # more than 2-D input
>>> x = paddle.to_tensor(123, dtype='int32')
>>> y = paddle.to_tensor([[[1.23]]], dtype='float32')
>>> out = paddle.atleast_2d(x, y)
>>> print(out)
[Tensor(shape=[1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
[[123]]), Tensor(shape=[1, 1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
[[[1.23000002]]])]
"""
out = []
for tensor in inputs:
tensor = paddle.to_tensor(tensor)
if tensor.dim() == 0:
result = tensor.reshape((1, 1))
elif tensor.dim() == 1:
result = paddle.unsqueeze(tensor, axis=0)
else:
result = tensor
out.append(result)

if len(out) == 1:
return out[0]
else:
return out


def atleast_3d(*inputs, name=None):
"""
Convert inputs to tensors and return the view with at least 3-dimension. Three or high-dimensional inputs are preserved.

Args:
inputs (Tensor|list(Tensor)): One or more tensors. The data type is ``float16``, ``uint16``, ``float32``, ``float64``, ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``complex64``, ``complex128``, ``bfloat16``.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.

Note:
``int8``, ``uint8``, ``complex64``, ``complex128`` are not supported in static graph mode.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么在静态图模式下这几个类型不能支持?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这几个方法里面用到了 reshape 将 0-dim 转为 n-dim,当时 rfc 的时候没想到 reshape 的动/静态图支持的不一样,静态图不支持这几个类型:

def reshape(x, shape, name=None):
    if in_dynamic_mode():
        ...
    else:
        check_variable_and_dtype(
            x,
            'x',
            [
                'float16',
                'float32',
                'float64',
                'int16',
                'int32',
                'int64',
                'bool',
                'uint16',
            ],
            'reshape',
        )

所以这里单独将这几个类型单拎出来了 ~

另外,bool 应该也支持,当时 rfc 的时候没有提到 bool 类型,是否也加进去?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reshape 的 check_variable_and_dtype 是否可以加上这几个类型呢?如果可以的话,需要单独提一个PR支持静态图下reshape的 int8, uint8, complex64, complex128类型。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看了一下底层 c++ 算子的实现,没找到 reshape 对于这几种数据类型有单独处理。

尝试修改 paddle/tensor/manipulation.py :

def reshape(x, shape, name=None):
    if in_dynamic_mode():
        ...
    elif in_pir_mode():
       ...
    else:
        check_variable_and_dtype(
            x,
            'x',
            [
                'float16',
                'float32',
                'float64',
                'int16',
                'int32',
                'int64',
                'bool',
                'uint16',
                'int8',
                'uint8',
                'complex64',
                'complex128',
            ],
            'reshape',
        )

简单验证:

In [1]: import numpy as np
   ...: import paddle
   ...: 
   ...: paddle.enable_static()
   ...: with paddle.static.program_guard(paddle.static.Program()):
   ...:     feed = {}
   ...:     x = paddle.static.data('x', [12], 'int8')
   ...:     feed['x'] = np.arange(12).astype('int8')
   ...: 
   ...:     out = paddle.reshape(x, [2, 6])
   ...:     exe = paddle.static.Executor(paddle.CUDAPlace(0))
   ...:     res = exe.run(feed=feed, fetch_list=[out])
   ...: 
   ...:     print(res)
   ...: 
[array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11]], dtype=int8)]
  • 未修改之前,会报错 TypeError
  • 修改之后,能够正常运行

另外,in_pir_mode 这里先不修改吧 ~ 我试了一下 os.environ['FLAGS_enable_new_ir_in_executor'] = 'True',接口会挂掉 ... ...

那我这边单独提 PR 再逐个类型验证一下吧 ~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it !


Returns:
One Tensor, if there is only one input.
List of Tensors, if there are more than one inputs.

Examples:
.. code-block:: python

>>> import paddle

>>> # one input
>>> x = paddle.to_tensor(123, dtype='int32')
>>> out = paddle.atleast_3d(x)
>>> print(out)
Tensor(shape=[1, 1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
[[[123]]])

>>> # more than one inputs
>>> x = paddle.to_tensor(123, dtype='int32')
>>> y = paddle.to_tensor([1.23], dtype='float32')
>>> out = paddle.atleast_3d(x, y)
>>> print(out)
[Tensor(shape=[1, 1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
[[[123]]]), Tensor(shape=[1, 1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
[[[1.23000002]]])]

>>> # more than 3-D input
>>> x = paddle.to_tensor(123, dtype='int32')
>>> y = paddle.to_tensor([[[[1.23]]]], dtype='float32')
>>> out = paddle.atleast_3d(x, y)
>>> print(out)
[Tensor(shape=[1, 1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
[[[123]]]), Tensor(shape=[1, 1, 1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
[[[[1.23000002]]]])]
"""
out = []
for tensor in inputs:
tensor = paddle.to_tensor(tensor)
if tensor.dim() == 0:
result = tensor.reshape((1, 1, 1))
elif tensor.dim() == 1:
result = paddle.unsqueeze(tensor, axis=[0, 2])
elif tensor.dim() == 2:
result = paddle.unsqueeze(tensor, axis=2)
else:
result = tensor
out.append(result)

if len(out) == 1:
return out[0]
else:
return out


def gather_nd(x, index, name=None):
"""

Expand Down
Loading