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

364 lines
9.2 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.
from __future__ import annotations
import unittest
from test_case_base import TestCaseBase
import paddle
from paddle.jit import sot
from paddle.jit.sot import symbolic_translate
from paddle.jit.sot.utils import InnerError, strict_mode_guard
def dict_setitem(x):
x[0] = 1
return x[0]
def dict_delitem(x):
del x[0]
return x
def dict_delitem_getitem(a):
b = a[0]
del a[0]
b[0] = 1
return a, b
def dict_nested_1(x):
x[0][0] = 42
x[1][0] = x[0][0] + x[0][1]
x[2] = {1: 2}
return x
def dict_nested_2(x):
a = x[0]
b = x[1]
del a[0]
a[1] = b[0]
a[2] = b[1]
x[1][0] = 42
del a[1]
return a, b
def list_append_int(tensor_x, list_a):
tensor_x = tensor_x + 1
list_a.append(12)
return tensor_x, list_a
def list_append_tensor(tensor_x, list_a):
tensor_x = tensor_x + 1
list_a.append(tensor_x)
return tensor_x, list_a
def list_delitem(list_a):
del list_a[0]
return list_a[0]
def list_extend(list_a):
list_a.extend([1, 2, 3])
return list_a[0]
def list_nested(list_a):
inner_list = []
inner_list.append(list_a)
inner_list[-1].append(12)
return 12
def list_insert(list_a):
list_a.insert(0, 1)
return list_a[0]
def list_remove(list_a):
list_a.remove(1)
return list_a[0]
def list_pop(list_a):
list_a.pop(0)
list_a.pop()
list_a.pop(1)
return list_a[0]
def list_clear(list_a):
list_a.clear()
return list_a
def list_sort(list_a):
list_a.sort()
return list_a
def list_reverse(list_a):
list_a.reverse()
return list_a
def slice_in_for_loop(x, iter_num=3):
x = paddle.to_tensor(x)
a = []
iter_num = paddle.full(shape=[1], fill_value=iter_num, dtype="int32")
for i in range(iter_num):
a.append(x)
for i in range(iter_num):
a[i] = x
out = a[2]
return out
# TODO: Object SideEffect
class CustomObject:
def __init__(self):
self.x = 2
self.y = paddle.to_tensor(1)
def object_attr_set2(self, x):
self.outputs = []
self.outputs.append(x)
return self.outputs
@sot.psdb.check_no_breakgraph
def object_attr_set(cus_obj, t):
"""object side effect."""
t = t + 1
cus_obj.x = t
return t, cus_obj.x
def object_attr_breakgraph(cus_obj, t):
t = t + 1
sot.psdb.breakgraph()
cus_obj.x = t
sot.psdb.breakgraph()
return t, cus_obj.x
@sot.psdb.check_no_breakgraph
def object_attr_tensor_del(cus_obj):
del cus_obj.y
@sot.psdb.check_no_breakgraph
def object_attr_int_del(cus_obj):
del cus_obj.x
def slice_list_after_change(l):
l.reverse()
sum = 0
for i, v in zip(range(2), l[2:]):
sum += v
return sum
class ReadBufferAfterChanged(paddle.nn.Layer):
def __init__(self):
super().__init__()
self.buffer1 = paddle.to_tensor(1)
self.buffer2 = paddle.to_tensor(2)
def forward(self, x):
self.buffer1 += 1
return x + self.buffer1 + self.buffer2
def __eq__(self, other):
if not isinstance(other, ReadBufferAfterChanged):
return False
return paddle.equal(self.buffer1, other.buffer1) and paddle.equal(
self.buffer2, other.buffer2
)
class TestDictSideEffect(TestCaseBase):
def test_dict_setitem(self):
self.assert_results_with_side_effects(
dict_setitem, {0: paddle.to_tensor(0)}
)
self.assert_results_with_side_effects(
dict_setitem, {0: paddle.to_tensor(1)}
)
def test_dict_delitem(self):
self.assert_results_with_side_effects(
dict_delitem, {0: paddle.to_tensor(0), 1: paddle.to_tensor(1)}
)
self.assert_results_with_side_effects(
dict_delitem, {0: paddle.to_tensor(1), 2: paddle.to_tensor(2)}
)
def test_dict_delitem_getitem(self):
self.assert_results_with_side_effects(
dict_delitem_getitem, {0: {0: 1, 1: 2}}
)
def test_dict_nested_1(self):
self.assert_results_with_side_effects(
dict_nested_1, {0: {0: 1, 1: 2}, 1: {0: 1, 1: 2}}
)
self.assert_results_with_side_effects(
dict_nested_1, {0: {0: 123, 1: 2}, 1: {0: 1, 1: 2}}
)
def test_dict_nested_2(self):
self.assert_results_with_side_effects(
dict_nested_2, {0: {0: 1, 1: 2}, 1: {0: 1, 1: 2}}
)
self.assert_results_with_side_effects(
dict_nested_2, {0: {0: 123, 1: 2}, 1: {0: 1, 1: 2}}
)
class TestListSideEffect(TestCaseBase):
def test_list_append(self):
self.assert_results_with_side_effects(
list_append_int, paddle.to_tensor(1), [1, 2, 3]
)
self.assert_results_with_side_effects(
list_append_tensor, paddle.to_tensor(2), [1, 2, 3]
)
def test_list_delitem(self):
self.assert_results_with_side_effects(list_delitem, [1, 2, 3])
def test_list_extend(self):
self.assert_results_with_side_effects(
list_extend, [1, 2, 3, 4, 5, 6, 7, 8, 9]
)
def test_list_insert(self):
self.assert_results_with_side_effects(list_insert, [1, 2, 3])
self.assert_results_with_side_effects(
list_insert, [-1, 2, -3, 4, -5, 6, -7, 8, -9]
)
def test_list_remove(self):
self.assert_results_with_side_effects(list_remove, [1, 1, 1])
self.assert_results_with_side_effects(list_remove, [0, 1, 2])
# TODO(DrRyanHuang): change this to ValueError
with self.assertRaises(InnerError):
symbolic_translate(list_remove)([0, 2, 4])
def test_list_pop(self):
self.assert_results_with_side_effects(list_pop, [1, 2, 3, 4, 5])
self.assert_results_with_side_effects(
list_pop, [-1, 2, -3, 4, -5, 6, -7, 8, -9]
)
def test_list_clear(self):
self.assert_results_with_side_effects(list_clear, [1, 2, 3, 4, 5])
self.assert_results_with_side_effects(
list_clear, [-1, 2, -3, 4, -5, 6, -7, 8, -9]
)
def test_list_sort(self):
self.assert_results_with_side_effects(list_sort, [2, 1, 7, 3, 4, 6])
self.assert_results_with_side_effects(
list_sort, [-1, 2, -3, 4, -5, 6, -7, 8, -9]
)
def test_list_reverse(self):
self.assert_results_with_side_effects(list_reverse, [1, 2, 3, 4, 5])
self.assert_results_with_side_effects(
list_reverse, [-1, 2, -3, 4, -5, 6, -7, 8, -9]
)
def test_slice_in_for_loop(self):
x = 2
with strict_mode_guard(False):
self.assert_results_with_side_effects(slice_in_for_loop, x)
def test_list_nested(self):
self.assert_results_with_side_effects(list_nested, [1, 2, 3])
class TestSliceAfterChange(TestCaseBase):
def test_slice_list_after_change(self):
self.assert_results_with_side_effects(
slice_list_after_change, [1, 2, 3, 4]
)
self.assert_results_with_side_effects(
slice_list_after_change, [7, 8, 9, 10]
)
class TestAttrSideEffect(TestCaseBase):
def attr_check(self, func, attr_keys: list[str], cls, *inputs):
cus_obj1 = cls()
cus_obj2 = cls()
sym_output = symbolic_translate(func)(cus_obj1, *inputs)
paddle_output = func(cus_obj2, *inputs)
for key in attr_keys:
self.assert_nest_match(
getattr(cus_obj1, key, f"__MISS_KEY__{key}"),
getattr(cus_obj2, key, f"__MISS_KEY__{key}"),
)
self.assert_nest_match(sym_output, paddle_output)
def test_attr_set(self):
self.attr_check(object_attr_set, ["x"], CustomObject, 5)
self.attr_check(
CustomObject.object_attr_set2, ["outputs"], CustomObject, 6
)
self.attr_check(
CustomObject.object_attr_set2,
["outputs"],
CustomObject,
paddle.to_tensor(5),
)
self.attr_check(
object_attr_set, ["x"], CustomObject, paddle.to_tensor(5)
)
def test_attr_del(self):
self.attr_check(object_attr_tensor_del, ["y"], CustomObject)
self.attr_check(object_attr_int_del, ["x"], CustomObject)
def test_attr_set_breakgraph(self):
self.attr_check(object_attr_breakgraph, ["x"], CustomObject, 100)
self.attr_check(object_attr_breakgraph, ["x"], CustomObject, 1000)
class TestReadBufferAfterChanged(TestCaseBase):
def test_read_buffer_after_change(self):
layer = ReadBufferAfterChanged()
x = paddle.randn([1, 2, 3])
self.assert_results_with_side_effects(
layer.__class__.forward,
layer,
x,
)
if __name__ == "__main__":
unittest.main()