未验证 提交 3b52f68e 编写于 作者: A Aurelius84 提交者: GitHub

[Dy2Stat]Support append method and initialized value for List in ControlFlow (#35212)

* Support append method and initialized value for List in ControlFlow

* polish error msg and en doc

* fix code style
上级 d387820d
...@@ -93,7 +93,8 @@ class ListTransformer(gast.NodeTransformer): ...@@ -93,7 +93,8 @@ class ListTransformer(gast.NodeTransformer):
for child_node in gast.walk(node): for child_node in gast.walk(node):
if isinstance(child_node, gast.Assign): if isinstance(child_node, gast.Assign):
if self._need_to_create_tensor_array(child_node): if self._need_to_create_tensor_array(child_node):
child_node.value = self._create_tensor_array() child_node.value = self._create_tensor_array(
child_node.value)
def _transform_list_append_in_control_flow(self, node): def _transform_list_append_in_control_flow(self, node):
for child_node in gast.walk(node): for child_node in gast.walk(node):
...@@ -189,9 +190,11 @@ class ListTransformer(gast.NodeTransformer): ...@@ -189,9 +190,11 @@ class ListTransformer(gast.NodeTransformer):
return True return True
return False return False
def _create_tensor_array(self): def _create_tensor_array(self, value_node):
# Although `dtype='float32'`, other types such as `int32` can also be supported # Although `dtype='float32'`, other types such as `int32` can also be supported
func_code = "paddle.tensor.create_array(dtype='float32')" init_value = ast_to_source_code(value_node).strip()
func_code = "paddle.tensor.create_array('float32', {})".format(
init_value)
func_node = gast.parse(func_code).body[0].value func_node = gast.parse(func_code).body[0].value
return func_node return func_node
......
...@@ -1538,7 +1538,7 @@ def array_write(x, i, array=None): ...@@ -1538,7 +1538,7 @@ def array_write(x, i, array=None):
return array return array
def create_array(dtype): def create_array(dtype, initialized_list=None):
""" """
This OP creates an LOD_TENSOR_ARRAY. It is used as This OP creates an LOD_TENSOR_ARRAY. It is used as
the input of :ref:`api_fluid_layers_array_read` and the input of :ref:`api_fluid_layers_array_read` and
...@@ -1548,6 +1548,8 @@ def create_array(dtype): ...@@ -1548,6 +1548,8 @@ def create_array(dtype):
Args: Args:
dtype (str): The data type of the elements in the lod_tensor_array. dtype (str): The data type of the elements in the lod_tensor_array.
Support data type: float32, float64, int32, int64. Support data type: float32, float64, int32, int64.
initialized_list(list): Used to initialize as default value for created array.
All values in initialized list should be a Tensor.
Returns: Returns:
Variable: The empty lod_tensor_array. The data type of elements in Tensor is ``dtype``. Variable: The empty lod_tensor_array. The data type of elements in Tensor is ``dtype``.
...@@ -1559,15 +1561,35 @@ def create_array(dtype): ...@@ -1559,15 +1561,35 @@ def create_array(dtype):
data = fluid.layers.create_array(dtype='float32') # Create a float32 LoDTensorArray. data = fluid.layers.create_array(dtype='float32') # Create a float32 LoDTensorArray.
""" """
array = []
if initialized_list is not None:
if not isinstance(initialized_list, (list, tuple)):
raise TypeError(
"Require type(initialized_list) should be list/tuple, but received {}".
format(type(initialized_list)))
array = list(initialized_list)
# NOTE: Only support plain list like [x, y,...], not support nested list in static mode.
for val in array:
if not isinstance(val, Variable):
raise TypeError(
"All values in `initialized_list` should be Variable, but recevied {}.".
format(type(val)))
if in_dygraph_mode(): if in_dygraph_mode():
return [] return array
helper = LayerHelper("array", **locals()) helper = LayerHelper("array", **locals())
return helper.create_variable( tensor_array = helper.create_variable(
name="{0}.out".format(helper.name), name="{0}.out".format(helper.name),
type=core.VarDesc.VarType.LOD_TENSOR_ARRAY, type=core.VarDesc.VarType.LOD_TENSOR_ARRAY,
dtype=dtype) dtype=dtype)
for val in array:
array_write(x=val, i=array_length(tensor_array), array=tensor_array)
return tensor_array
@templatedoc() @templatedoc()
def less_than(x, y, force_cpu=None, cond=None, name=None): def less_than(x, y, force_cpu=None, cond=None, name=None):
......
...@@ -18,8 +18,9 @@ import warnings ...@@ -18,8 +18,9 @@ import warnings
import inspect import inspect
from .. import core from .. import core
from ..framework import Variable, unique_name from ..framework import Variable, unique_name, static_only
from .layer_function_generator import OpProtoHolder from .layer_function_generator import OpProtoHolder
from .control_flow import array_write, array_length
_supported_int_dtype_ = [ _supported_int_dtype_ = [
core.VarDesc.VarType.BOOL, core.VarDesc.VarType.BOOL,
...@@ -182,6 +183,24 @@ def monkey_patch_variable(): ...@@ -182,6 +183,24 @@ def monkey_patch_variable():
out.stop_gradient = self.stop_gradient out.stop_gradient = self.stop_gradient
return out return out
@static_only
def append(self, var):
"""
**Notes**:
**The type variable must be LoD Tensor Array.
"""
if not isinstance(var, Variable):
raise TypeError(
"Required input var should be Variable, but received {}".format(
type(var)))
if self.type != core.VarDesc.VarType.LOD_TENSOR_ARRAY:
raise TypeError(
"Only Variable with VarType.LOD_TENSOR_ARRAY support `append` method, but received type: {}".
format(self.type))
array_write(x=var, i=array_length(self), array=self)
def _scalar_op_(var, scale, bias): def _scalar_op_(var, scale, bias):
block = current_block(var) block = current_block(var)
out = create_new_tmp_var(block, var.dtype) out = create_new_tmp_var(block, var.dtype)
...@@ -344,6 +363,7 @@ def monkey_patch_variable(): ...@@ -344,6 +363,7 @@ def monkey_patch_variable():
# b=-a # b=-a
('__neg__', _neg_), ('__neg__', _neg_),
('astype', astype), ('astype', astype),
('append', append),
('dim', lambda x: len(x.shape)), ('dim', lambda x: len(x.shape)),
('ndimension', lambda x: len(x.shape)), ('ndimension', lambda x: len(x.shape)),
('ndim', _ndim_), ('ndim', _ndim_),
......
...@@ -147,14 +147,17 @@ def test_list_pop_without_control_flow_2(x): ...@@ -147,14 +147,17 @@ def test_list_pop_without_control_flow_2(x):
def test_list_pop_in_if(x): def test_list_pop_in_if(x):
x = fluid.dygraph.to_variable(x) x = fluid.dygraph.to_variable(x)
a = [] a = []
b = [x * 2 + (x + 1)]
if x.numpy()[0] > 0: if x.numpy()[0] > 0:
a.append(x) a.append(x)
b.append(x + 1)
a.append(fluid.layers.fill_constant(shape=[1], value=1, dtype="int64")) a.append(fluid.layers.fill_constant(shape=[1], value=1, dtype="int64"))
else: else:
a.append(x + 1) a.append(x + 1)
b.append(x - 1)
a.append(fluid.layers.fill_constant(shape=[2], value=2, dtype="int64")) a.append(fluid.layers.fill_constant(shape=[2], value=2, dtype="int64"))
item1 = a.pop(1) item1 = a.pop(1)
return item1 return item1, b[-1]
def test_list_pop_in_for_loop(x, iter_num): def test_list_pop_in_for_loop(x, iter_num):
...@@ -165,14 +168,16 @@ def test_list_pop_in_for_loop(x, iter_num): ...@@ -165,14 +168,16 @@ def test_list_pop_in_for_loop(x, iter_num):
) # TODO(liym27): Delete it if the type of parameter iter_num can be resolved ) # TODO(liym27): Delete it if the type of parameter iter_num can be resolved
a = [] a = []
b = [x - 1, x + 1]
for i in range(iter_num): for i in range(iter_num):
a.append(x + i) a.append(x + i)
b.append(x * 2)
one = fluid.layers.ones(shape=[1], dtype="int32") one = fluid.layers.ones(shape=[1], dtype="int32")
for i in range(one.numpy()[0]): for i in range(one.numpy()[0]):
item = a.pop() item = a.pop()
return a[0], item return a[0], item, b[1]
def test_list_pop_in_while_loop(x, iter_num): def test_list_pop_in_while_loop(x, iter_num):
...@@ -180,14 +185,18 @@ def test_list_pop_in_while_loop(x, iter_num): ...@@ -180,14 +185,18 @@ def test_list_pop_in_while_loop(x, iter_num):
iter_num = fluid.layers.fill_constant( iter_num = fluid.layers.fill_constant(
shape=[1], value=iter_num, dtype="int32") shape=[1], value=iter_num, dtype="int32")
a = [] a = []
b = [x]
b.append(x)
b.pop()
i = 0 i = 0
while i < iter_num: while i < iter_num:
a.append(x + i) a.append(x + i)
b.append(x - i)
i += 1 i += 1
if i % 2 == 1: if i % 2 == 1:
a.pop() a.pop()
return a[0] return a[0], b[2]
class TestListWithoutControlFlow(unittest.TestCase): class TestListWithoutControlFlow(unittest.TestCase):
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
from __future__ import print_function from __future__ import print_function
import unittest import unittest
import paddle
import paddle.fluid.core as core import paddle.fluid.core as core
import numpy import numpy
...@@ -50,5 +51,49 @@ class TestLoDTensorArray(unittest.TestCase): ...@@ -50,5 +51,49 @@ class TestLoDTensorArray(unittest.TestCase):
self.assertEqual([[1]], t.recursive_sequence_lengths()) self.assertEqual([[1]], t.recursive_sequence_lengths())
class TestCreateArray(unittest.TestCase):
def setUp(self):
self.place = paddle.CPUPlace()
self.shapes = [[10, 4], [8, 12], [1]]
def test_initialized_list_and_error(self):
paddle.disable_static()
init_data = [
numpy.random.random(shape).astype('float32')
for shape in self.shapes
]
array = paddle.tensor.create_array(
'float32', [paddle.to_tensor(x) for x in init_data])
for res, gt in zip(array, init_data):
self.assertTrue(numpy.array_equal(res, gt))
# test for None
array = paddle.tensor.create_array('float32')
self.assertTrue(isinstance(array, list))
self.assertEqual(len(array), 0)
# test error
with self.assertRaises(TypeError):
paddle.tensor.create_array('float32', 'str')
def test_static(self):
paddle.enable_static()
init_data = [paddle.ones(shape, dtype='int32') for shape in self.shapes]
array = paddle.tensor.create_array('float32', init_data)
for res, gt in zip(array, init_data):
self.assertTrue(res.shape, gt.shape)
# test error with nest list
with self.assertRaises(TypeError):
paddle.tensor.create_array('float32',
[init_data[0], [init_data[1]]])
# test error with not variable
with self.assertRaises(TypeError):
paddle.tensor.create_array('float32', ("str"))
paddle.enable_static()
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -122,13 +122,15 @@ def array_write(x, i, array=None): ...@@ -122,13 +122,15 @@ def array_write(x, i, array=None):
return layers.array_write(x, i, array) return layers.array_write(x, i, array)
def create_array(dtype): def create_array(dtype, initialized_list=None):
""" """
This OP creates an array. It is used as the input of :ref:`api_paddle_tensor_array_array_read` and This OP creates an array. It is used as the input of :ref:`api_paddle_tensor_array_array_read` and
:ref:`api_paddle_tensor_array_array_write`. :ref:`api_paddle_tensor_array_array_write`.
Args: Args:
dtype (str): The data type of the elements in the array. Support data type: float32, float64, int32, int64 and bool. dtype (str): The data type of the elements in the array. Support data type: float32, float64, int32, int64 and bool.
initialized_list(list): Used to initialize as default value for created array.
All values in initialized list should be a Tensor.
Returns: Returns:
list|Tensor: An empty array. In dynamic mode, ``array`` is a Python list. But in static mode, array is a Tensor list|Tensor: An empty array. In dynamic mode, ``array`` is a Python list. But in static mode, array is a Tensor
...@@ -149,4 +151,4 @@ def create_array(dtype): ...@@ -149,4 +151,4 @@ def create_array(dtype):
print(item) # [[5., 5., 5.]] print(item) # [[5., 5., 5.]]
""" """
return layers.create_array(dtype) return layers.create_array(dtype, initialized_list)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册