Skip to content

Commit

Permalink
[New API]add rot90 api (PaddlePaddle#37634)
Browse files Browse the repository at this point in the history
* update

* update. test=develop

* fix. test=develop

* fix ut. test=develop

* fix ut. test=develop

* fix ut. test=develop

* update. test=develop

* fix ut. test=develop

* fix ut. test=develop

* fix sample code. test=develop

* fix ut. test=develop

* fix ut. test=develop

* fix ut. test=develop

* fix ut. test=develop

* fix paddle.rot90 doc. test=develop

* update ut. test=develop

* fix. test=develop

* fix .test=develop

* fix .test=develop

* fix doc. test=develop
  • Loading branch information
zmxdream authored and Zjq9409 committed Dec 10, 2021
1 parent bff8f94 commit 298829a
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 2 deletions.
2 changes: 0 additions & 2 deletions paddle/fluid/framework/heter_pipeline_trainer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ TEST(HeterPipelineTrainerTest, GPU) {
t3.add_trainers(1);
t3.add_trainers(1);
t3.add_trainers(1);
t3.add_dump_fields("hello");
t3.add_dump_param("fc_0");
auto* heter_section_param3 = t3.mutable_heter_section_param();
heter_section_param3->set_num_pipeline_stages(3);
heter_section_param3->set_pipeline_stage(2);
Expand Down
2 changes: 2 additions & 0 deletions python/paddle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
from .tensor.manipulation import unsqueeze_ # noqa: F401
from .tensor.manipulation import unstack # noqa: F401
from .tensor.manipulation import flip # noqa: F401
from .tensor.manipulation import rot90 # noqa: F401
from .tensor.manipulation import unbind # noqa: F401
from .tensor.manipulation import roll # noqa: F401
from .tensor.manipulation import chunk # noqa: F401
Expand Down Expand Up @@ -408,6 +409,7 @@
'bitwise_not',
'mm',
'flip',
'rot90',
'bincount',
'histogram',
'multiplex',
Expand Down
262 changes: 262 additions & 0 deletions python/paddle/fluid/tests/unittests/test_rot90_op.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
# Copyright (c) 2021 PaddlePaddle 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.

from __future__ import print_function

import unittest
import numpy as np
import paddle
import paddle.fluid as fluid
import paddle.fluid.core as core
from paddle.fluid import Program, program_guard


class TestRot90_API(unittest.TestCase):
"""Test rot90 api."""

def test_static_graph(self):
paddle.enable_static()
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=1, axes=[0, 1])
output = paddle.rot90(output, k=1, axes=[0, 1])
output = output.rot90(k=1, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[4, 1], [5, 2], [6, 3]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_static_k_0(self):
paddle.enable_static()
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=0, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_static_k_2(self):
paddle.enable_static()
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=2, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[6, 5, 4], [3, 2, 1]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_static_k_3(self):
paddle.enable_static()
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=3, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[4, 1], [5, 2], [6, 3]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_static_neg_k_1(self):
paddle.enable_static()
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=-1, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[4, 1], [5, 2], [6, 3]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_static_neg_k_2(self):
paddle.enable_static()
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=-2, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[6, 5, 4], [3, 2, 1]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_static_neg_k_3(self):
paddle.enable_static()
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
startup_program = fluid.Program()
train_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=-3, axes=[0, 1])
place = fluid.CPUPlace()
if fluid.core.is_compiled_with_cuda():
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run(startup_program)

img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
res = exe.run(train_program,
feed={'input': img},
fetch_list=[output])

out_np = np.array(res[0])
out_ref = np.array([[3, 6], [2, 5], [1, 4]]).astype(np.float32)

self.assertTrue(
(out_np == out_ref).all(),
msg='rot90 output is wrong, out =' + str(out_np))

def test_error_api(self):
paddle.enable_static()

## dims error
def run1():
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=1, axes=[0])

self.assertRaises(ValueError, run1)

## input dims error
def run2():
input = fluid.data(name='input', dtype='float32', shape=[2])
output = paddle.rot90(input, k=1, axes=[0, 1])

self.assertRaises(ValueError, run2)

def run3():
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=1, axes=[0, 0])

self.assertRaises(ValueError, run3)

def run4():
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=1, axes=[3, 1])

self.assertRaises(ValueError, run4)

def run5():
input = fluid.data(name='input', dtype='float32', shape=[2, 3])
output = paddle.rot90(input, k=1, axes=[0, 3])

self.assertRaises(ValueError, run5)

def test_dygraph(self):
img = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
with fluid.dygraph.guard():
inputs = fluid.dygraph.to_variable(img)

ret = paddle.rot90(inputs, k=1, axes=[0, 1])
ret = ret.rot90(1, axes=[0, 1])
ret = paddle.rot90(ret, k=1, axes=[0, 1])
out_ref = np.array([[4, 1], [5, 2], [6, 3]]).astype(np.float32)

self.assertTrue(
(ret.numpy() == out_ref).all(),
msg='rot90 output is wrong, out =' + str(ret.numpy()))


if __name__ == "__main__":
unittest.main()
2 changes: 2 additions & 0 deletions python/paddle/tensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
from .manipulation import unsqueeze_ # noqa: F401
from .manipulation import unstack # noqa: F401
from .manipulation import flip # noqa: F401
from .manipulation import rot90 # noqa: F401
from .manipulation import unbind # noqa: F401
from .manipulation import roll # noqa: F401
from .manipulation import chunk # noqa: F401
Expand Down Expand Up @@ -370,6 +371,7 @@
'unsqueeze_',
'unstack',
'flip',
'rot90',
'unbind',
'roll',
'tile',
Expand Down
86 changes: 86 additions & 0 deletions python/paddle/tensor/manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,92 @@ def flip(x, axis, name=None):
return out


def rot90(x, k=1, axes=[0, 1], name=None):
"""
Rotate a n-D tensor by 90 degrees in the plane specified by dims axis. Rotation direction is from the first towards the second axis if k > 0, and from the second towards the first for k < 0.
Args:
x (Tensor): The input Tensor(or LoDTensor). The data type of the input Tensor x
should be float32, float64, int32, int64, bool.
k (int): Number of times to rotate
axes (list|tuple): Axis to rotate
name (str, optional): The default value is None. Normally there is no need for user to set this property.
For more information, please refer to :ref:`api_guide_Name` .
Returns:
Tensor: Tensor or LoDTensor calculated by rot90 layer. The data type is same with input x.
Raises:
TypeError: If the data type of ``x`` is not Variable
TypeError: If the dtype of ``x`` is not float16, float32, float64, int32, int64, bool
TypeError: If the data type of ``dims`` is not list, tuple
Examples:
.. code-block:: python
import paddle
import numpy as np
data = paddle.arange(4)
data = paddle.reshape(data, (2, 2))
print(data) ## [[0, 1],[2, 3]]
y = paddle.rot90(data, 1, [0, 1])
print(y) #[[1, 3],[0, 2]]
y= paddle.rot90(data, -1, [0, 1])
print(y) #[[2, 0],[3, 1]]
data2 = paddle.arange(8)
data2 = paddle.reshape(data2, (2,2,2))
print(data2) ###[[[0, 1],[2, 3]],[[4, 5],[6, 7]]]
y = paddle.rot90(data2, 1, [1, 2])
print(y) ### [[[1, 3],[0, 2]],[[5, 7],[4, 6]]]
"""

helper = LayerHelper("rot90", **locals())
check_type(x, 'X', (Variable), 'rot90')
dtype = helper.input_dtype('x')
check_dtype(dtype, 'X',
['float16', 'float32', 'float64', 'int32', 'int64', 'bool'],
'rot90')
check_type(axes, 'axes', (list, tuple), 'rot90')

input_total_dims = len(x.shape)
total_rot_dims = len(axes)
if total_rot_dims != 2:
raise ValueError("expected total rotation axes == 2, but got axes = {}".
format(total_rot_dims))
if input_total_dims < 2:
raise ValueError("expected total dims >= 2, but got total dims = {}".
format(input_total_dims))

if not (axes[0] != axes[1] and abs(axes[0] - axes[1]) != input_total_dims):
raise ValueError(
"expected rotation axes to be different, but got axis0 = {}, and axis1 = {}".
format(axes[0], axes[1]))

if not (axes[0] < input_total_dims and axes[0] >= -input_total_dims):
raise ValueError("Rotation axis0 out of range, axis0 = {}".format(axes[
0]))
if not (axes[1] < input_total_dims and axes[1] >= -input_total_dims):
raise ValueError("Rotation axis1 out of range, axis1 = {}".format(axes[
1]))

## k % 4
k = k % 4 if k >= 0 else 4 - (-k % 4)
if k == 0:
return x
if k == 2:
return flip(flip(x, axes[0]), axes[1])

axes_list = list(range(0, input_total_dims))
(axes_list[axes[0]], axes_list[axes[1]]) = (axes_list[axes[1]],
axes_list[axes[0]])
if k == 1:
return transpose(flip(x, axes[1]), axes_list)
else:
# k == 3
return flip(transpose(x, axes_list), axes[1])


def flatten(x, start_axis=0, stop_axis=-1, name=None):
r"""
**Flatten op**
Expand Down

0 comments on commit 298829a

Please sign in to comment.