chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,337 @@
|
||||
# Copyright (c) 2025 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
|
||||
|
||||
import paddle
|
||||
from paddle.testing import assert_close
|
||||
|
||||
|
||||
class TestAssertClose(unittest.TestCase):
|
||||
def setUp(self):
|
||||
paddle.set_device("cpu")
|
||||
|
||||
def test_scalars_exact_match(self):
|
||||
assert_close(1, 1)
|
||||
assert_close(1.0, 1.0)
|
||||
assert_close(True, True)
|
||||
assert_close(None, None)
|
||||
assert_close(1 + 2j, 1 + 2j)
|
||||
|
||||
def test_scalars_mismatch(self):
|
||||
with self.assertRaisesRegex(AssertionError, "Scalars are not equal!"):
|
||||
assert_close(1, 2)
|
||||
with self.assertRaisesRegex(AssertionError, "Booleans mismatch"):
|
||||
assert_close(True, False)
|
||||
with self.assertRaisesRegex(AssertionError, "None mismatch"):
|
||||
assert_close(None, 1)
|
||||
|
||||
def test_scalars_tolerances(self):
|
||||
assert_close(1.0, 1.0 + 1e-9)
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
assert_close(1.0, 1.1)
|
||||
|
||||
assert_close(1.0, 1.1, atol=0.2, rtol=0.0)
|
||||
with self.assertRaises(AssertionError):
|
||||
assert_close(1.0, 1.1, atol=0.05, rtol=0.0)
|
||||
|
||||
def test_numpy_scalars(self):
|
||||
assert_close(np.float32(1.0), np.float32(1.0))
|
||||
assert_close(np.int32(1), np.int32(1))
|
||||
assert_close(np.bool_(True), np.bool_(True))
|
||||
|
||||
assert_close(np.float64(1.0), 1.0)
|
||||
|
||||
def test_tensor_exact_match(self):
|
||||
t1 = paddle.to_tensor([1.0, 2.0, 3.0])
|
||||
t2 = paddle.to_tensor([1.0, 2.0, 3.0])
|
||||
assert_close(t1, t2)
|
||||
|
||||
def test_tensor_tolerances(self):
|
||||
t1 = paddle.to_tensor([1.0, 2.0, 3.0], dtype='float32')
|
||||
t2 = t1 + 1e-6
|
||||
assert_close(t1, t2)
|
||||
|
||||
t3 = t1 + 1e-4
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError, "Tensor-likes are not close"
|
||||
):
|
||||
assert_close(t1, t3)
|
||||
|
||||
assert_close(t1, t3, atol=1e-3, rtol=0.0)
|
||||
|
||||
def test_tensor_shape_mismatch(self):
|
||||
t1 = paddle.zeros([2, 2])
|
||||
t2 = paddle.zeros([2, 3])
|
||||
with self.assertRaisesRegex(AssertionError, "shape"):
|
||||
assert_close(t1, t2)
|
||||
|
||||
def test_tensor_dtype_check(self):
|
||||
t_float32 = paddle.to_tensor([1.0], dtype='float32')
|
||||
t_float64 = paddle.to_tensor([1.0], dtype='float64')
|
||||
|
||||
with self.assertRaisesRegex(AssertionError, "dtype"):
|
||||
assert_close(t_float32, t_float64)
|
||||
|
||||
assert_close(t_float32, t_float64, check_dtype=False)
|
||||
|
||||
def test_tensor_device_check(self):
|
||||
t1 = paddle.to_tensor([1.0])
|
||||
if paddle.device.is_compiled_with_cuda():
|
||||
t_gpu = t1.to("gpu")
|
||||
with self.assertRaisesRegex(AssertionError, "device"):
|
||||
assert_close(t1, t_gpu)
|
||||
|
||||
assert_close(t1, t_gpu, check_device=False)
|
||||
else:
|
||||
assert_close(t1, t1)
|
||||
|
||||
def test_nan_handling(self):
|
||||
val_nan = float('nan')
|
||||
t_nan = paddle.to_tensor([val_nan])
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
assert_close(val_nan, val_nan)
|
||||
with self.assertRaises(AssertionError):
|
||||
assert_close(t_nan, t_nan)
|
||||
|
||||
assert_close(val_nan, val_nan, equal_nan=True)
|
||||
assert_close(t_nan, t_nan, equal_nan=True)
|
||||
|
||||
def test_sequences(self):
|
||||
l1 = [paddle.to_tensor(1.0), 2.0]
|
||||
l2 = [paddle.to_tensor(1.0), 2.0]
|
||||
assert_close(l1, l2)
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError, "length of the sequences mismatch"
|
||||
):
|
||||
assert_close([1], [1, 2])
|
||||
|
||||
with self.assertRaisesRegex(AssertionError, "Scalars are not equal!"):
|
||||
assert_close([1], [2])
|
||||
|
||||
def test_mappings(self):
|
||||
d1 = {"a": 1, "b": paddle.to_tensor(2.0)}
|
||||
d2 = {"a": 1, "b": paddle.to_tensor(2.0)}
|
||||
assert_close(d1, d2)
|
||||
|
||||
d3 = {"a": 1, "c": 2.0}
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError, "keys of the mappings do not match"
|
||||
):
|
||||
assert_close(d1, d3)
|
||||
|
||||
def test_nested_structure_error_msg(self):
|
||||
actual = {"data": [{"val": 10}]}
|
||||
expected = {"data": [{"val": 20}]}
|
||||
|
||||
try:
|
||||
assert_close(actual, expected)
|
||||
except AssertionError as e:
|
||||
msg = str(e)
|
||||
self.assertIn("data", msg)
|
||||
self.assertIn("val", msg)
|
||||
self.assertIn("['data']", msg)
|
||||
|
||||
def test_tensor_mismatch_msg_details(self):
|
||||
t1 = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
|
||||
t2 = paddle.to_tensor([[1.0, 2.0], [3.0, 5.0]])
|
||||
|
||||
try:
|
||||
assert_close(t1, t2)
|
||||
except AssertionError as e:
|
||||
msg = str(e)
|
||||
self.assertIn("Mismatched elements: 1 / 4", msg)
|
||||
self.assertIn("Greatest absolute difference: 1.0", msg)
|
||||
self.assertIn("at index (1, 1)", msg)
|
||||
|
||||
def test_msg_override(self):
|
||||
with self.assertRaisesRegex(AssertionError, "My custom error"):
|
||||
assert_close(1, 2, msg="My custom error")
|
||||
|
||||
def test_unsupported_types(self):
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B:
|
||||
pass
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
assert_close(A(), B())
|
||||
|
||||
def test_complex_numbers(self):
|
||||
c1 = 1 + 1j
|
||||
c2 = 1 + 1j + 1e-10j
|
||||
c3 = 1 + 2j
|
||||
|
||||
assert_close(c1, c2)
|
||||
with self.assertRaises(AssertionError):
|
||||
assert_close(c1, c3)
|
||||
|
||||
def test_tolerance_validation_logic(self):
|
||||
with self.assertRaisesRegex(
|
||||
ValueError,
|
||||
"Both 'rtol' and 'atol' must be either specified or omitted",
|
||||
):
|
||||
assert_close(1.0, 1.0, rtol=1e-5)
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
ValueError,
|
||||
"Both 'rtol' and 'atol' must be either specified or omitted",
|
||||
):
|
||||
assert_close(1.0, 1.0, atol=1e-5)
|
||||
|
||||
def test_msg_callable(self):
|
||||
def custom_formatter(orig_msg):
|
||||
return f"PREFIX -> {orig_msg} <- SUFFIX"
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError, "PREFIX -> Scalars are not equal!"
|
||||
):
|
||||
assert_close(1, 2, msg=custom_formatter)
|
||||
|
||||
def test_zero_dim_tensor_mismatch(self):
|
||||
t1 = paddle.to_tensor(1.0)
|
||||
t2 = paddle.to_tensor(2.0)
|
||||
|
||||
with self.assertRaisesRegex(AssertionError, "Scalars"):
|
||||
assert_close(t1, t2)
|
||||
|
||||
try:
|
||||
assert_close(t1, t2)
|
||||
except AssertionError as e:
|
||||
self.assertNotIn("Tensor-likes", str(e))
|
||||
|
||||
def test_type_promotion_logic(self):
|
||||
t_real = paddle.to_tensor([1.0], dtype='float32')
|
||||
t_complex = paddle.to_tensor([1.0 + 0j], dtype='complex64')
|
||||
assert_close(t_real, t_complex, check_dtype=False)
|
||||
|
||||
t_c64 = paddle.to_tensor([1 + 1j], dtype='complex64')
|
||||
t_c128 = paddle.to_tensor([1 + 1j], dtype='complex128')
|
||||
assert_close(t_c64, t_c128, check_dtype=False)
|
||||
|
||||
def test_object_pair_broken_eq(self):
|
||||
from paddle.testing._comparison import ErrorMeta, ObjectPair
|
||||
|
||||
class BrokenObj:
|
||||
def __eq__(self, other):
|
||||
raise RuntimeError("Comparison crashed internal error!")
|
||||
|
||||
def __repr__(self):
|
||||
return "BrokenObj"
|
||||
|
||||
obj = BrokenObj()
|
||||
pair = ObjectPair(obj, obj)
|
||||
|
||||
try:
|
||||
pair.compare()
|
||||
except ErrorMeta as e:
|
||||
actual_error = e.to_error()
|
||||
|
||||
self.assertIsInstance(actual_error, ValueError)
|
||||
self.assertIn(
|
||||
"failed with:\nComparison crashed internal error!",
|
||||
str(actual_error),
|
||||
)
|
||||
else:
|
||||
self.fail("ObjectPair.compare() should have raised an ErrorMeta")
|
||||
|
||||
def test_pair_repr_and_extra_repr(self):
|
||||
from paddle.testing._comparison import NumberPair, ObjectPair, Pair
|
||||
|
||||
obj_pair = ObjectPair(actual=10, expected=20, id=("test_id",))
|
||||
rep_str = repr(obj_pair)
|
||||
|
||||
self.assertIn("ObjectPair(", rep_str)
|
||||
self.assertIn("id=('test_id',),", rep_str)
|
||||
self.assertIn("actual=10,", rep_str)
|
||||
self.assertIn("expected=20,", rep_str)
|
||||
|
||||
num_pair = NumberPair(1.0, 1.0, rtol=0.5, atol=0.1)
|
||||
rep_str_num = repr(num_pair)
|
||||
|
||||
self.assertIn("NumberPair(", rep_str_num)
|
||||
self.assertIn("rtol=0.5,", rep_str_num)
|
||||
self.assertIn("atol=0.1,", rep_str_num)
|
||||
|
||||
class MockTuplePair(Pair):
|
||||
def compare(self):
|
||||
pass
|
||||
|
||||
def extra_repr(self):
|
||||
return [("custom_key", "custom_value")]
|
||||
|
||||
mock_pair = MockTuplePair("act", "exp")
|
||||
rep_str_mock = repr(mock_pair)
|
||||
|
||||
self.assertIn("MockTuplePair(", rep_str_mock)
|
||||
self.assertIn("custom_key=custom_value,", rep_str_mock)
|
||||
|
||||
def test_static_graph_variable(self):
|
||||
paddle.enable_static()
|
||||
try:
|
||||
main_prog = paddle.static.Program()
|
||||
startup_prog = paddle.static.Program()
|
||||
|
||||
with paddle.static.program_guard(main_prog, startup_prog):
|
||||
x = paddle.static.data(name='x', shape=[2, 2], dtype='float32')
|
||||
y = paddle.static.data(name='y', shape=[2, 2], dtype='float32')
|
||||
|
||||
assert_close(x, y)
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError, "Python types do not match"
|
||||
):
|
||||
assert_close(x, 1)
|
||||
|
||||
z = paddle.static.data(name='z', shape=[2, 2], dtype='int32')
|
||||
assert_close(x, z, check_dtype=False)
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError,
|
||||
"The values for attribute dtype do not match",
|
||||
):
|
||||
assert_close(x, z)
|
||||
|
||||
w = paddle.static.data(name='w', shape=[2, 3], dtype='float32')
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError,
|
||||
"The values for attribute shape do not match",
|
||||
):
|
||||
assert_close(x, w)
|
||||
|
||||
v = paddle.static.data(
|
||||
name='v', shape=[2, 2, 1], dtype='float32'
|
||||
)
|
||||
with self.assertRaisesRegex(
|
||||
AssertionError,
|
||||
"The values for attribute shape do not match",
|
||||
):
|
||||
assert_close(x, v)
|
||||
|
||||
dynamic_x = paddle.static.data(
|
||||
name='dynamic_x', shape=[-1, 2], dtype='float32'
|
||||
)
|
||||
assert_close(x, dynamic_x)
|
||||
assert_close(dynamic_x, x)
|
||||
|
||||
finally:
|
||||
paddle.disable_static()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user