515 lines
16 KiB
Python
515 lines
16 KiB
Python
# Copyright (c) 2023 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.
|
|
import itertools
|
|
import sys
|
|
import unittest
|
|
|
|
import numpy as np
|
|
from op_test import get_device, get_device_place, is_custom_device
|
|
|
|
import paddle
|
|
from paddle.base import core
|
|
|
|
RTOL = 1e-5
|
|
ATOL = 1e-8
|
|
DTYPE_ALL = [
|
|
'float16',
|
|
'float32',
|
|
'float64',
|
|
'int32',
|
|
'int64',
|
|
'bfloat16',
|
|
]
|
|
|
|
DTYPE_COLUMN_STACK = DTYPE_ALL
|
|
|
|
PLACES = [('cpu', paddle.CPUPlace())] + (
|
|
[(get_device(), get_device_place())]
|
|
if (core.is_compiled_with_cuda() or is_custom_device())
|
|
else []
|
|
)
|
|
|
|
|
|
def rearrange_data(*inputs):
|
|
data = list(zip(*inputs))
|
|
return [list(itertools.chain(*data[i])) for i in range(4)]
|
|
|
|
|
|
def generate_data(shape, count=1, dtype='int32'):
|
|
"""generate test data
|
|
|
|
Args:
|
|
shape(list of int): shape of inputs
|
|
count(int): input count for each dim
|
|
dtype(str): dtype
|
|
|
|
Returns:
|
|
a list of data like:
|
|
[[data, dtype, shape, name], [data, dtype, shape, name] ... ]
|
|
"""
|
|
return list(
|
|
zip(
|
|
*[
|
|
[
|
|
# bfloat16 convert to uint16 for numpy
|
|
np.random.randint(0, 255, size=shape).astype(
|
|
dtype if dtype != 'bfloat16' else 'uint16'
|
|
),
|
|
dtype,
|
|
shape,
|
|
f'{shape}d_{idx}_{dtype}',
|
|
]
|
|
for idx in range(count)
|
|
]
|
|
)
|
|
)
|
|
|
|
|
|
class BaseTest(unittest.TestCase):
|
|
"""Test in each `PLACES` and in `static/dygraph`"""
|
|
|
|
def _test_static_api(
|
|
self,
|
|
func_paddle,
|
|
func_numpy,
|
|
inputs: list,
|
|
dtypes: list,
|
|
shapes: list,
|
|
names: list,
|
|
):
|
|
"""Test `static`, convert `Tensor` to `numpy array` before feed into graph"""
|
|
# convert grad value to bool if dtype is bool
|
|
grad_value = 123.0 if dtypes[0] != 'bool' else True
|
|
if dtypes[0] == 'bfloat16':
|
|
grad_value = paddle.to_tensor(grad_value, dtype=dtypes[0]).numpy()
|
|
paddle.enable_static()
|
|
|
|
for device, place in PLACES:
|
|
paddle.set_device(device)
|
|
|
|
exe = paddle.static.Executor(place)
|
|
new_scope = paddle.static.Scope()
|
|
main_program = paddle.static.Program()
|
|
with (
|
|
paddle.static.scope_guard(new_scope),
|
|
paddle.static.program_guard(main_program),
|
|
):
|
|
x = []
|
|
feed = {}
|
|
for i in range(len(inputs)):
|
|
input = inputs[i]
|
|
shape = shapes[i]
|
|
dtype = dtypes[i]
|
|
name = names[i]
|
|
|
|
_x = paddle.static.data(name, shape, dtype)
|
|
_x.stop_gradient = False
|
|
x.append(_x)
|
|
|
|
# the data feeded should NOT be a Tensor
|
|
feed[name] = input
|
|
|
|
out = func_paddle(x)
|
|
out.stop_gradient = False
|
|
|
|
y = out * 123
|
|
|
|
# not check old ir
|
|
if paddle.framework.in_pir_mode():
|
|
fetch_list = [out]
|
|
grads = paddle.autograd.ir_backward.grad(y, x)
|
|
fetch_list.append(grads)
|
|
|
|
exe = paddle.static.Executor(place)
|
|
res, *res_grad = exe.run(feed=feed, fetch_list=fetch_list)
|
|
|
|
np.testing.assert_allclose(
|
|
res_grad[0], np.ones(x[0].shape) * grad_value
|
|
)
|
|
|
|
out_ref = func_numpy(inputs)
|
|
for n, p in zip(out_ref, res):
|
|
np.testing.assert_allclose(n, p, rtol=RTOL, atol=ATOL)
|
|
|
|
def _test_dygraph_api(
|
|
self,
|
|
func_paddle,
|
|
func_numpy,
|
|
inputs: list,
|
|
dtypes: list,
|
|
shapes: list,
|
|
names: list,
|
|
):
|
|
"""Test `dygraph`, and check grads"""
|
|
paddle.disable_static()
|
|
|
|
for device, place in PLACES:
|
|
paddle.set_device(device)
|
|
|
|
out = func_paddle(
|
|
[
|
|
paddle.to_tensor(inputs[i]).astype(dtypes[i])
|
|
for i in range(len(inputs))
|
|
]
|
|
)
|
|
out_ref = func_numpy(inputs)
|
|
|
|
for n, p in zip(out_ref, out):
|
|
np.testing.assert_allclose(n, p.numpy(), rtol=RTOL, atol=ATOL)
|
|
|
|
# check grads
|
|
if len(inputs) == 1:
|
|
out = [out]
|
|
|
|
for y in out:
|
|
y.stop_gradient = False
|
|
z = y * 123
|
|
grads = paddle.grad(z, y)
|
|
self.assertTrue(len(grads), 1)
|
|
self.assertEqual(grads[0].dtype, y.dtype)
|
|
self.assertEqual(grads[0].shape, y.shape)
|
|
|
|
def _test_all(self, args, dtype=''):
|
|
self._test_dygraph_api(self.func_paddle, self.func_numpy, *args)
|
|
self._test_static_api(self.func_paddle, self.func_numpy, *args)
|
|
|
|
|
|
class BaseCases:
|
|
def test_0d(self):
|
|
self._test_all(generate_data([], count=1, dtype='float64'))
|
|
|
|
def test_1d(self):
|
|
self._test_all(generate_data([1], count=1, dtype='float64'))
|
|
|
|
def test_2d(self):
|
|
self._test_all(generate_data([1, 1], count=1, dtype='float64'))
|
|
self._test_all(generate_data([3, 2], count=1, dtype='float64'))
|
|
|
|
def test_3d(self):
|
|
self._test_all(generate_data([1, 1, 1], count=1, dtype='float64'))
|
|
self._test_all(generate_data([3, 4, 2], count=1, dtype='float64'))
|
|
|
|
def test_4d(self):
|
|
self._test_all(generate_data([1, 1, 1, 1], count=1, dtype='float64'))
|
|
self._test_all(generate_data([3, 4, 2, 5], count=1, dtype='float64'))
|
|
|
|
def test_0d_more(self):
|
|
self._test_all(generate_data([], count=3, dtype='float64'))
|
|
|
|
def test_1d_more(self):
|
|
self._test_all(generate_data([1], count=3, dtype='float64'))
|
|
self._test_all(generate_data([5], count=3, dtype='float64'))
|
|
|
|
def test_2d_more(self):
|
|
self._test_all(generate_data([1, 1], count=3, dtype='float64'))
|
|
self._test_all(generate_data([3, 2], count=3, dtype='float64'))
|
|
|
|
def test_3d_more(self):
|
|
self._test_all(generate_data([1, 1, 1], count=3, dtype='float64'))
|
|
self._test_all(generate_data([3, 4, 2], count=3, dtype='float64'))
|
|
|
|
def test_4d_more(self):
|
|
self._test_all(generate_data([1, 1, 1, 1], count=3, dtype='float64'))
|
|
self._test_all(generate_data([3, 4, 2, 5], count=3, dtype='float64'))
|
|
|
|
|
|
class TestHStack(BaseTest, BaseCases):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.hstack
|
|
self.func_numpy = np.hstack
|
|
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([], count=1, dtype='float64')
|
|
d1 = generate_data([2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_dtype(self):
|
|
for dtype in DTYPE_ALL:
|
|
if dtype == 'float16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_float16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
if dtype == 'bfloat16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_bfloat16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
self._test_all(
|
|
generate_data([], count=1, dtype=dtype),
|
|
dtype,
|
|
)
|
|
|
|
|
|
class TestHStackZeroDim1(TestHStack):
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([0, 1, 1], count=1, dtype='float64')
|
|
self._test_all(d0)
|
|
|
|
|
|
class TestHStackZeroDim2(TestHStack):
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([1, 0, 1, 1], count=1, dtype='float64')
|
|
self._test_all(d0)
|
|
|
|
|
|
class TestVStack(BaseTest, BaseCases):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.vstack
|
|
self.func_numpy = np.vstack
|
|
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([2], count=1, dtype='float64')
|
|
d1 = generate_data([1, 2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_dtype(self):
|
|
for dtype in DTYPE_ALL:
|
|
if dtype == 'float16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_float16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
if dtype == 'bfloat16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_bfloat16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
self._test_all(
|
|
generate_data([], count=1, dtype=dtype),
|
|
dtype,
|
|
)
|
|
|
|
|
|
class TestDStack(BaseTest, BaseCases):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.dstack
|
|
self.func_numpy = np.dstack
|
|
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([2], count=1, dtype='float64')
|
|
d1 = generate_data([1, 2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
d0 = generate_data([2], count=1, dtype='float64')
|
|
d1 = generate_data([1, 2, 1], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_dtype(self):
|
|
for dtype in DTYPE_ALL:
|
|
if dtype == 'float16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_float16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
if dtype == 'bfloat16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_bfloat16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
self._test_all(
|
|
generate_data([], count=1, dtype=dtype),
|
|
dtype,
|
|
)
|
|
|
|
|
|
class TestColumnStack(BaseTest, BaseCases):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.column_stack
|
|
self.func_numpy = np.column_stack
|
|
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([2], count=1, dtype='float64')
|
|
d1 = generate_data([2, 1], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_dtype(self):
|
|
for dtype in DTYPE_COLUMN_STACK:
|
|
if dtype == 'float16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_float16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
if dtype == 'bfloat16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_bfloat16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
self._test_all(
|
|
generate_data([], count=1, dtype=dtype),
|
|
dtype=dtype,
|
|
)
|
|
|
|
|
|
class TestRowStack(BaseTest, BaseCases):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.row_stack
|
|
self.func_numpy = np.vstack
|
|
|
|
def test_mix_ndim(self):
|
|
d0 = generate_data([2], count=1, dtype='float64')
|
|
d1 = generate_data([1, 2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_dtype(self):
|
|
for dtype in DTYPE_ALL:
|
|
if dtype == 'float16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_float16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
if dtype == 'bfloat16' and (
|
|
not (core.is_compiled_with_cuda() or is_custom_device())
|
|
or (
|
|
not core.is_bfloat16_supported(get_device_place())
|
|
or sys.platform == 'win32'
|
|
)
|
|
):
|
|
continue
|
|
|
|
self._test_all(
|
|
generate_data([], count=1, dtype=dtype),
|
|
dtype,
|
|
)
|
|
|
|
|
|
class ErrorCases:
|
|
def test_mix_dtype(self):
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([2], count=1, dtype='float32')
|
|
d1 = generate_data([2], count=1, dtype='float64')
|
|
self._test_dygraph_api(
|
|
self.func_paddle, self.func_numpy, *rearrange_data(d0, d1)
|
|
)
|
|
|
|
with self.assertRaises(TypeError):
|
|
d0 = generate_data([2], count=1, dtype='float32')
|
|
d1 = generate_data([2], count=1, dtype='float64')
|
|
self._test_static_api(
|
|
self.func_paddle, self.func_numpy, *rearrange_data(d0, d1)
|
|
)
|
|
|
|
def test_1d_2d(self):
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([2, 1], count=1, dtype='float64')
|
|
d1 = generate_data([3], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([1, 2], count=1, dtype='float64')
|
|
d1 = generate_data([3], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_1d_3d(self):
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([2, 3, 1], count=1, dtype='float64')
|
|
d1 = generate_data([3], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([1, 1, 1], count=1, dtype='float64')
|
|
d1 = generate_data([2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
def test_2d_3d(self):
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([2, 3, 1], count=1, dtype='float64')
|
|
d1 = generate_data([1, 3], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([1, 1, 1], count=1, dtype='float64')
|
|
d1 = generate_data([1, 2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
|
|
class ErrorCases0d1d(ErrorCases):
|
|
"""hstack works fine with 0d & 1d"""
|
|
|
|
def test_vstack_0d_1d(self):
|
|
with self.assertRaises(ValueError):
|
|
d0 = generate_data([], count=1, dtype='float64')
|
|
d1 = generate_data([2], count=1, dtype='float64')
|
|
self._test_all(rearrange_data(d0, d1))
|
|
|
|
|
|
class TestErrorHStack(BaseTest, ErrorCases):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.hstack
|
|
self.func_numpy = np.hstack
|
|
|
|
|
|
class TestErrorVStack(BaseTest, ErrorCases0d1d):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.vstack
|
|
self.func_numpy = np.vstack
|
|
|
|
|
|
class TestErrorDStack(BaseTest, ErrorCases0d1d):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.dstack
|
|
self.func_numpy = np.dstack
|
|
|
|
|
|
class TestErrorColumnStack(BaseTest, ErrorCases0d1d):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.column_stack
|
|
self.func_numpy = np.column_stack
|
|
|
|
|
|
class TestErrorRowStack(BaseTest, ErrorCases0d1d):
|
|
def setUp(self):
|
|
self.func_paddle = paddle.row_stack
|
|
self.func_numpy = np.vstack
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|