diff --git a/docs/source/oneflow.rst b/docs/source/oneflow.rst index 1301c98c04d..c61138e251c 100644 --- a/docs/source/oneflow.rst +++ b/docs/source/oneflow.rst @@ -138,6 +138,7 @@ oneflow tile, to, transpose, + t, tril, unsqueeze, permute, diff --git a/docs/source/tensor.rst b/docs/source/tensor.rst index 570f7641155..79c59b9da19 100644 --- a/docs/source/tensor.rst +++ b/docs/source/tensor.rst @@ -161,6 +161,8 @@ OneFlow Tensor Class tril, triu, type_as, + t, + T, unfold, uniform_, unsqueeze, diff --git a/oneflow/api/python/framework/doc.cpp b/oneflow/api/python/framework/doc.cpp index 62b59ed73bc..e3bc10ceddf 100644 --- a/oneflow/api/python/framework/doc.cpp +++ b/oneflow/api/python/framework/doc.cpp @@ -50,6 +50,8 @@ py::object AddFunctionDoc(py::object f, const std::string& doc_string) { } } py::setattr(f, "__doc__", py::reinterpret_steal(PyUnicode_FromString(doc_str))); + } else if (Py_TYPE(obj)->tp_name == PyProperty_Type.tp_name) { + py::setattr(f, "__doc__", py::reinterpret_steal(PyUnicode_FromString(doc_str))); } else { THROW(RuntimeError) << "function is " << Py_TYPE(obj)->tp_name << ", not a valid function."; } diff --git a/oneflow/core/functional/functional_api.yaml b/oneflow/core/functional/functional_api.yaml index 84b8627982a..0fc77dce442 100755 --- a/oneflow/core/functional/functional_api.yaml +++ b/oneflow/core/functional/functional_api.yaml @@ -299,6 +299,14 @@ signature: "Tensor (Tensor input, Int32List dims) => Permute" bind_python: True +- name: "T" + signature: "Tensor (Tensor input) => TransposeAllDimProperty" + bind_python: True + +- name: "t" + signature: "Tensor (Tensor input) => TransposeAllDimFunction" + bind_python: True + - name: "reciprocal" signature: "Tensor (Tensor x) => Reciprocal" bind_python: True diff --git a/oneflow/core/functional/impl/array_functor.cpp b/oneflow/core/functional/impl/array_functor.cpp index 5b8a0770686..885419d2346 100644 --- a/oneflow/core/functional/impl/array_functor.cpp +++ b/oneflow/core/functional/impl/array_functor.cpp @@ -2523,6 +2523,32 @@ class GenTensorBufferFunctor { std::shared_ptr op_; }; +class TransposeAllDimPropertyFunctor { + public: + TransposeAllDimPropertyFunctor() {} + Maybe operator()(const std::shared_ptr& x) const { + const int64_t ndim = x->ndim(); + std::vector permute; + permute.resize(ndim); + std::iota(permute.begin(), permute.end(), 0); + std::reverse(permute.begin(), permute.end()); + return Transpose(x, permute); + } +}; + +class TransposeAllDimFunctionFunctor { + public: + TransposeAllDimFunctionFunctor() {} + Maybe operator()(const std::shared_ptr& x) const { + const int64_t ndim = x->ndim(); + CHECK_OR_RETURN(ndim <= 2) + << "RuntimeError: t() expects a tensor with <= 2 dimensions, but input tensor is " << ndim + << "D"; + if (ndim == 0 || ndim == 1) { return x; } + return Transpose2dim(x, 0, 1); + } +}; + } // namespace impl ONEFLOW_FUNCTION_LIBRARY(m) { @@ -2626,6 +2652,8 @@ ONEFLOW_FUNCTION_LIBRARY(m) { m.add_functor("TensorToTensorBuffer"); m.add_functor("TensorBufferToTensor"); m.add_functor("GenTensorBuffer"); + m.add_functor("TransposeAllDimProperty"); + m.add_functor("TransposeAllDimFunction"); }; } // namespace functional diff --git a/python/oneflow/__init__.py b/python/oneflow/__init__.py index 768f1c02030..e5f6d90aee9 100755 --- a/python/oneflow/__init__.py +++ b/python/oneflow/__init__.py @@ -174,7 +174,7 @@ def is_deprecated(func_or_class): from oneflow._C import erfinv, erfinv_ from oneflow._C import cumsum from oneflow._C import swapaxes - +from oneflow._C import t from . import sbp import atexit diff --git a/python/oneflow/framework/docstr/__init__.py b/python/oneflow/framework/docstr/__init__.py index 1b72cab6819..35c65e0beee 100644 --- a/python/oneflow/framework/docstr/__init__.py +++ b/python/oneflow/framework/docstr/__init__.py @@ -45,3 +45,4 @@ from .clamp import * from .erfinv import * from .swapaxes import * +from .tensor_t import * diff --git a/python/oneflow/framework/docstr/tensor.py b/python/oneflow/framework/docstr/tensor.py index b23816cd122..acfa2e91179 100644 --- a/python/oneflow/framework/docstr/tensor.py +++ b/python/oneflow/framework/docstr/tensor.py @@ -782,3 +782,21 @@ Alias for :func:`oneflow.Tensor.clamp_`. """, ) + +add_docstr( + oneflow.Tensor.t, + """ + Tensor.t() → Tensor + + See :func:`oneflow.t` + """, +) + +add_docstr( + oneflow.Tensor.T, + """ + Is this Tensor with its dimensions reversed. + + If `n` is the number of dimensions in `x`, `x.T` is equivalent to `x.permute(n-1, n-2, ..., 0)`. + """, +) diff --git a/python/oneflow/framework/docstr/tensor_t.py b/python/oneflow/framework/docstr/tensor_t.py new file mode 100644 index 00000000000..f7cf1dd3278 --- /dev/null +++ b/python/oneflow/framework/docstr/tensor_t.py @@ -0,0 +1,49 @@ +""" +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import oneflow +from oneflow.framework.docstr.utils import add_docstr + +add_docstr( + oneflow.t, + """ + oneflow.t(input) → Tensor. + + Expects `input` to be <= 2-D tensor and transposes dimensions 0 and 1. + + 0-D and 1-D tensors are returned as is. When input is a 2-D tensor this is equivalent to `transpose(input, 0, 1)`. + + Args: + input (oneflow.Tensor): An input tensor. + + For example: + + .. code-block:: python + + >>> import oneflow as flow + >>> import numpy as np + + >>> x = flow.tensor(np.random.randn(), dtype=flow.float32) + >>> flow.t(x).shape + oneflow.Size([]) + >>> x = flow.tensor(np.random.randn(3), dtype=flow.float32) + >>> flow.t(x).shape + oneflow.Size([3]) + >>> x = flow.tensor(np.random.randn(2,3), dtype=flow.float32) + >>> flow.t(x).shape + oneflow.Size([3, 2]) + + """, +) diff --git a/python/oneflow/framework/tensor.py b/python/oneflow/framework/tensor.py index e9b0cf8be02..a2f7e139f96 100644 --- a/python/oneflow/framework/tensor.py +++ b/python/oneflow/framework/tensor.py @@ -745,6 +745,14 @@ def _gather(self, dim, index): return flow._C.dim_gather(self, dim, index, False) +def _T(self): + return flow._C.T(self) + + +def _t(self): + return flow._C.t(self) + + def _numpy(self): assert ( not self.is_lazy @@ -915,6 +923,8 @@ def RegisterMethods(): Tensor.gather = _gather Tensor.all = _all Tensor.any = _any + Tensor.T = property(_T) + Tensor.t = _t def register_tensor_op(op_name): diff --git a/python/oneflow/test/modules/test_t.py b/python/oneflow/test/modules/test_t.py new file mode 100644 index 00000000000..5d70225fd77 --- /dev/null +++ b/python/oneflow/test/modules/test_t.py @@ -0,0 +1,42 @@ +""" +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from collections import OrderedDict + +import numpy as np + +from oneflow.test_utils.automated_test_util import * +from test_util import GenArgList + +import oneflow as flow +import oneflow.unittest + + +@flow.unittest.skip_unless_1n1d() +class TestTransposeAllDimFunction(flow.unittest.TestCase): + @autotest(check_graph=False) + def test_t_flow_with_random_data(test_case): + device = random_device() + x = random_pytorch_tensor( + ndim=constant(2).to(int), dim0=random(0, 64), dim1=random(0, 64) + ).to(device) + y = torch.t(x) + return y + + +if __name__ == "__main__": + unittest.main() diff --git a/python/oneflow/test/tensor/test_tensor.py b/python/oneflow/test/tensor/test_tensor.py index ae1709a51cd..52a23506796 100644 --- a/python/oneflow/test/tensor/test_tensor.py +++ b/python/oneflow/test/tensor/test_tensor.py @@ -900,6 +900,22 @@ def test_transpose_tensor_with_random_data(test_case): y = x.transpose(dim0=random(1, 3).to(int), dim1=random(1, 3).to(int)) return y + @autotest(check_graph=False) + def test_t_tensor_with_random_data(test_case): + device = random_device() + x = random_pytorch_tensor( + ndim=constant(2).to(int), dim0=random(0, 64), dim1=random(0, 64) + ).to(device) + y = x.t() + return y + + @autotest(check_graph=False) + def test_T_tensor_with_random_data(test_case): + device = random_device() + x = random_pytorch_tensor(ndim=random(1, 4)).to(device) + y = x.T + return y + @flow.unittest.skip_unless_1n1d() def test_tensor_where(test_case): x = flow.tensor(