Files
2026-07-13 12:40:42 +08:00

569 lines
20 KiB
Python

# Copyright (c) 2018 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 unittest
import numpy as np
from op_test import (
OpTest,
check_cudnn_version_and_compute_capability,
convert_float_to_uint16,
skip_check_grad_ci,
)
import paddle
paddle.enable_static()
def broadcast_wrapper(shape=[1, 10, 12, 1]):
def min_wrapper(x, y, axis=-1):
return paddle.minimum(x, y.reshape(shape))
return min_wrapper
class TestElementwiseOp(OpTest):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
# If x and y have the same value, the min() is not differentiable.
# So we generate test data by the following method
# to avoid them being too close to each other.
x = np.random.uniform(0.1, 1, [13, 17]).astype("float64")
sgn = np.random.choice([-1, 1], [13, 17]).astype("float64")
y = x + sgn * np.random.uniform(0.1, 1, [13, 17]).astype("float64")
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
def test_check_output(self):
self.check_output()
def test_check_grad_normal(self):
if hasattr(self, 'attrs'):
if self.attrs['axis'] == -1:
self.check_grad(
['X', 'Y'], 'Out', check_prim=False, check_prim_pir=True
)
else:
self.check_grad(['X', 'Y'], 'Out')
else:
self.check_grad(
['X', 'Y'], 'Out', check_prim=False, check_prim_pir=True
)
def test_check_grad_ignore_x(self):
if hasattr(self, 'attrs') and self.attrs['axis'] != -1:
self.check_grad(
['Y'],
'Out',
max_relative_error=0.005,
no_grad_set=set("X"),
)
else:
self.check_grad(
['Y'],
'Out',
max_relative_error=0.005,
no_grad_set=set("X"),
check_prim=False,
check_prim_pir=True,
)
def test_check_grad_ignore_y(self):
if hasattr(self, 'attrs') and self.attrs['axis'] != -1:
self.check_grad(
['X'],
'Out',
max_relative_error=0.005,
no_grad_set=set('Y'),
check_dygraph=False,
)
else:
self.check_grad(
['X'],
'Out',
max_relative_error=0.005,
no_grad_set=set('Y'),
check_prim=False,
check_prim_pir=True,
)
def if_enable_cinn(self):
pass
class TestElementwiseFP16Op(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
self.dtype = np.float16
# If x and y have the same value, the min() is not differentiable.
# So we generate test data by the following method
# to avoid them being too close to each other.
x = np.random.uniform(0.1, 1, [13, 17]).astype(np.float16)
sgn = np.random.choice([-1, 1], [13, 17]).astype(np.float16)
y = x + sgn * np.random.uniform(0.1, 1, [13, 17]).astype(np.float16)
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinOp_ZeroDim1(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.uniform(0.1, 1, []).astype("float64")
y = np.random.uniform(0.1, 1, []).astype("float64")
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinFP16Op_ZeroDim1(TestElementwiseFP16Op):
def init_data(self):
self.x = np.random.uniform(0.1, 1, []).astype(np.float16)
self.y = np.random.uniform(0.1, 1, []).astype(np.float16)
class TestElementwiseMinOp_ZeroDim2(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
x = np.random.uniform(0.1, 1, [13, 17]).astype("float64")
y = np.random.uniform(0.1, 1, []).astype("float64")
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinFP16Op_ZeroDim2(TestElementwiseFP16Op):
def init_data(self):
self.x = np.random.uniform(0.1, 1, [13, 17]).astype("float16")
self.y = np.random.uniform(0.1, 1, []).astype("float16")
class TestElementwiseMinOp_ZeroDim3(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.uniform(0.1, 1, []).astype("float64")
y = np.random.uniform(0.1, 1, [13, 17]).astype("float64")
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinFP16Op_ZeroDim3(TestElementwiseFP16Op):
def init_data(self):
self.x = np.random.uniform(0.1, 1, []).astype("float16")
self.y = np.random.uniform(0.1, 1, [13, 17]).astype("float16")
@skip_check_grad_ci(
reason="[skip shape check] Use y_shape(1) to test broadcast."
)
class TestElementwiseMinOp_scalar(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.random_integers(-5, 5, [10, 3, 4]).astype("float64")
y = np.array([0.5]).astype("float64")
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
@skip_check_grad_ci(
reason="[skip shape check] Use y_shape(1) to test broadcast."
)
class TestElementwiseMinFP16Op_scalar(TestElementwiseFP16Op):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.random_integers(-5, 5, [10, 3, 4]).astype(np.float16)
y = np.array([0.5]).astype(np.float16)
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinOp_Vector(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.random((100,)).astype("float64")
sgn = np.random.choice([-1, 1], (100,)).astype("float64")
y = x + sgn * np.random.uniform(0.1, 1, (100,)).astype("float64")
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinFP16Op_Vector(TestElementwiseFP16Op):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.random((100,)).astype(np.float16)
sgn = np.random.choice([-1, 1], (100,)).astype(np.float16)
y = x + sgn * np.random.uniform(0.1, 1, (100,)).astype(np.float16)
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinOp_broadcast_2(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = broadcast_wrapper(shape=[1, 1, 100])
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.uniform(0.5, 1, (2, 3, 100)).astype(np.float64)
sgn = np.random.choice([-1, 1], (100,)).astype(np.float64)
y = x[0, 0, :] + sgn * np.random.uniform(1, 2, (100,)).astype(
np.float64
)
self.inputs = {'X': x, 'Y': y}
self.outputs = {
'Out': np.minimum(
self.inputs['X'], self.inputs['Y'].reshape(1, 1, 100)
)
}
class TestElementwiseMinFP16Op_broadcast_2(TestElementwiseFP16Op):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = broadcast_wrapper(shape=[1, 1, 100])
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.uniform(0.5, 1, (2, 3, 100)).astype(np.float16)
sgn = np.random.choice([-1, 1], (100,)).astype(np.float16)
y = x[0, 0, :] + sgn * np.random.uniform(1, 2, (100,)).astype(
np.float16
)
self.inputs = {'X': x, 'Y': y}
self.outputs = {
'Out': np.minimum(
self.inputs['X'], self.inputs['Y'].reshape(1, 1, 100)
)
}
class TestElementwiseMinOp_broadcast_4(TestElementwiseOp):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.prim_op_type = "prim"
self.public_python_api = paddle.minimum
self.if_enable_cinn()
x = np.random.uniform(0.5, 1, (2, 10, 2, 5)).astype(np.float64)
sgn = np.random.choice([-1, 1], (2, 10, 1, 5)).astype(np.float64)
y = x + sgn * np.random.uniform(1, 2, (2, 10, 1, 5)).astype(np.float64)
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseMinFP16Op_broadcast_4(TestElementwiseFP16Op):
def setUp(self):
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
x = np.random.uniform(0.5, 1, (2, 10, 2, 5)).astype(np.float16)
sgn = np.random.choice([-1, 1], (2, 10, 1, 5)).astype(np.float16)
y = x + sgn * np.random.uniform(1, 2, (2, 10, 1, 5)).astype(np.float16)
self.inputs = {'X': x, 'Y': y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
@unittest.skipIf(
not check_cudnn_version_and_compute_capability(8100, 8),
"only support compiled with CUDA or custom device, and for CUDA cudnn version need larger than 8.1.0 and device's compute capability is at least 8.0",
)
class TestElementwiseBF16Op(OpTest):
def init_data(self):
# If x and y have the same value, the max() is not differentiable.
# So we generate test data by the following method
# to avoid them being too close to each other.
self.x = np.random.uniform(0.1, 1, [13, 17]).astype(np.float32)
sgn = np.random.choice([-1, 1], [13, 17]).astype(np.float32)
self.y = self.x + sgn * np.random.uniform(0.1, 1, [13, 17]).astype(
np.float32
)
def setUp(self):
self.init_data()
self.op_type = "elementwise_min"
self.python_api = paddle.minimum
self.public_python_api = paddle.minimum
self.prim_op_type = "prim"
self.if_enable_cinn()
self.dtype = np.uint16
self.inputs = {
'X': convert_float_to_uint16(self.x),
'Y': convert_float_to_uint16(self.y),
}
self.outputs = {
'Out': convert_float_to_uint16(np.minimum(self.x, self.y))
}
def test_check_output(self):
self.check_output()
def test_check_grad_normal(self):
places = self._get_places()
for place in places:
if type(place) is paddle.base.libpaddle.CPUPlace:
check_prim = False
else:
check_prim = True
self.check_grad_with_place(
place,
inputs_to_check=['X', 'Y'],
output_names='Out',
no_grad_set=None,
numeric_grad_delta=0.05,
in_place=False,
max_relative_error=0.005,
user_defined_grads=None,
user_defined_grad_outputs=None,
check_dygraph=True,
check_prim=False,
only_check_prim=False,
atol=1e-5,
check_cinn=False,
check_prim_pir=check_prim,
)
def test_check_grad_ignore_x(self):
places = self._get_places()
for place in places:
if isinstance(place, paddle.base.libpaddle.CPUPlace):
check_prim = False
else:
check_prim = True
self.check_grad_with_place(
place,
inputs_to_check=['Y'],
output_names='Out',
no_grad_set=set("X"),
numeric_grad_delta=0.05,
in_place=False,
max_relative_error=0.005,
user_defined_grads=None,
user_defined_grad_outputs=None,
check_dygraph=True,
check_prim=False,
only_check_prim=False,
atol=1e-5,
check_cinn=False,
check_prim_pir=check_prim,
)
def test_check_grad_ignore_y(self):
places = self._get_places()
for place in places:
if isinstance(place, paddle.base.libpaddle.CPUPlace):
check_prim = False
else:
check_prim = True
self.check_grad_with_place(
place,
inputs_to_check=['Y'],
output_names='Out',
no_grad_set=set("X"),
numeric_grad_delta=0.05,
in_place=False,
max_relative_error=0.005,
user_defined_grads=None,
user_defined_grad_outputs=None,
check_dygraph=True,
check_prim=False,
only_check_prim=False,
atol=1e-5,
check_cinn=False,
check_prim_pir=check_prim,
)
def if_enable_cinn(self):
pass
class TestElementwiseMinBF16Op_ZeroDim1(TestElementwiseBF16Op):
def init_data(self):
self.x = np.random.uniform(0.1, 1, []).astype("float32")
self.y = np.random.uniform(0.1, 1, []).astype("float32")
class TestElementwiseMinBF16Op_scalar(TestElementwiseBF16Op):
def init_data(self):
self.x = np.random.random_integers(-5, 5, [2, 3, 20]).astype("float32")
self.y = np.array([0.5]).astype("float32")
self.__class__.no_need_check_grad = True
class TestElementwiseMinBF16Op_Vector(TestElementwiseBF16Op):
def init_data(self):
self.x = np.random.random((100,)).astype("float32")
sgn = np.random.choice([-1, 1], (100,)).astype("float32")
self.y = self.x + sgn * np.random.uniform(0.1, 1, (100,)).astype(
"float32"
)
class TestElementwiseOpEqualInput(TestElementwiseOp):
def init_data(self):
self.x = np.ones([13, 17]).astype(np.float32)
self.y = np.ones([13, 17]).astype(np.float32)
def setUp(self):
self.init_data()
self.op_type = "elementwise_min"
self.prim_op_type = "prim"
self.if_enable_cinn()
self.python_api = paddle.minimum
self.dtype = np.float32
self.public_python_api = paddle.minimum
self.inputs = {'X': self.x, 'Y': self.y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestElementwiseOp0SizeInput(TestElementwiseOp):
def init_data(self):
self.x = np.ones([0, 1, 2]).astype(np.float32)
self.y = np.ones([1, 3598, 2]).astype(np.float32)
def setUp(self):
self.init_data()
self.op_type = "elementwise_min"
self.prim_op_type = "prim"
self.if_enable_cinn()
self.python_api = paddle.minimum
self.dtype = np.float32
self.public_python_api = paddle.minimum
self.inputs = {'X': self.x, 'Y': self.y}
self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])}
class TestMinimumOutAndAlias(unittest.TestCase):
def test_dygraph(self):
paddle.disable_static()
x = paddle.to_tensor(
np.array([[1, 2], [7, 8]]), dtype='float32', stop_gradient=False
)
y = paddle.to_tensor(
np.array([[3, 4], [5, 6]]), dtype='float32', stop_gradient=False
)
def run_case(case):
out_buf = paddle.zeros_like(x)
out_buf.stop_gradient = False
if case == 'return':
z = paddle.minimum(x, y)
elif case == 'input_out':
paddle.minimum(x, y, out=out_buf)
z = out_buf
elif case == 'both_return':
z = paddle.minimum(input=x, other=y, out=out_buf)
elif case == 'both_input_out':
_ = paddle.minimum(input=x, other=y, out=out_buf)
z = out_buf
else:
raise AssertionError
ref = paddle._C_ops.minimum(x, y)
np.testing.assert_allclose(
z.numpy(), ref.numpy(), rtol=1e-6, atol=1e-6
)
(z.mean()).backward()
return z.numpy(), x.grad.numpy(), y.grad.numpy()
z1, gx1, gy1 = run_case('return')
x.clear_gradient()
y.clear_gradient()
z2, gx2, gy2 = run_case('input_out')
x.clear_gradient()
y.clear_gradient()
z3, gx3, gy3 = run_case('both_return')
x.clear_gradient()
y.clear_gradient()
z4, gx4, gy4 = run_case('both_input_out')
np.testing.assert_allclose(z1, z2, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(z1, z3, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(z1, z4, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(gx1, gx2, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(gx1, gx3, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(gx1, gx4, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(gy1, gy2, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(gy1, gy3, rtol=1e-6, atol=1e-6)
np.testing.assert_allclose(gy1, gy4, rtol=1e-6, atol=1e-6)
paddle.enable_static()
def test_static(self):
paddle.enable_static()
startup_prog = paddle.static.Program()
main_prog = paddle.static.Program()
with paddle.static.program_guard(main_prog, startup_prog):
x = paddle.static.data('X', [5, 7], 'float32')
y = paddle.static.data('Y', [5, 7], 'float32')
z = paddle.minimum(input=x, other=y)
x_data = np.random.random([5, 7]).astype('float32')
y_data = np.random.random([5, 7]).astype('float32')
ref = np.minimum(x_data, y_data)
exe = paddle.static.Executor(paddle.CPUPlace())
exe.run(startup_prog)
out = exe.run(
main_prog,
feed={'X': x_data, 'Y': y_data},
fetch_list=[z],
)
np.testing.assert_allclose(out[0], ref, rtol=1e-6, atol=1e-6)
if __name__ == '__main__':
unittest.main()