未验证 提交 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):
for child_node in gast.walk(node):
if isinstance(child_node, gast.Assign):
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):
for child_node in gast.walk(node):
......@@ -189,9 +190,11 @@ class ListTransformer(gast.NodeTransformer):
return True
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
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
return func_node
......
......@@ -1538,7 +1538,7 @@ def array_write(x, i, array=None):
return array
def create_array(dtype):
def create_array(dtype, initialized_list=None):
"""
This OP creates an LOD_TENSOR_ARRAY. It is used as
the input of :ref:`api_fluid_layers_array_read` and
......@@ -1548,6 +1548,8 @@ def create_array(dtype):
Args:
dtype (str): The data type of the elements in the lod_tensor_array.
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:
Variable: The empty lod_tensor_array. The data type of elements in Tensor is ``dtype``.
......@@ -1559,15 +1561,35 @@ def create_array(dtype):
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():
return []
return array
helper = LayerHelper("array", **locals())
return helper.create_variable(
tensor_array = helper.create_variable(
name="{0}.out".format(helper.name),
type=core.VarDesc.VarType.LOD_TENSOR_ARRAY,
dtype=dtype)
for val in array:
array_write(x=val, i=array_length(tensor_array), array=tensor_array)
return tensor_array
@templatedoc()
def less_than(x, y, force_cpu=None, cond=None, name=None):
......
......@@ -18,8 +18,9 @@ import warnings
import inspect
from .. import core
from ..framework import Variable, unique_name
from ..framework import Variable, unique_name, static_only
from .layer_function_generator import OpProtoHolder
from .control_flow import array_write, array_length
_supported_int_dtype_ = [
core.VarDesc.VarType.BOOL,
......@@ -182,6 +183,24 @@ def monkey_patch_variable():
out.stop_gradient = self.stop_gradient
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):
block = current_block(var)
out = create_new_tmp_var(block, var.dtype)
......@@ -344,6 +363,7 @@ def monkey_patch_variable():
# b=-a
('__neg__', _neg_),
('astype', astype),
('append', append),
('dim', lambda x: len(x.shape)),
('ndimension', lambda x: len(x.shape)),
('ndim', _ndim_),
......
......@@ -147,14 +147,17 @@ def test_list_pop_without_control_flow_2(x):
def test_list_pop_in_if(x):
x = fluid.dygraph.to_variable(x)
a = []
b = [x * 2 + (x + 1)]
if x.numpy()[0] > 0:
a.append(x)
b.append(x + 1)
a.append(fluid.layers.fill_constant(shape=[1], value=1, dtype="int64"))
else:
a.append(x + 1)
b.append(x - 1)
a.append(fluid.layers.fill_constant(shape=[2], value=2, dtype="int64"))
item1 = a.pop(1)
return item1
return item1, b[-1]
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
a = []
b = [x - 1, x + 1]
for i in range(iter_num):
a.append(x + i)
b.append(x * 2)
one = fluid.layers.ones(shape=[1], dtype="int32")
for i in range(one.numpy()[0]):
item = a.pop()
return a[0], item
return a[0], item, b[1]
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(
shape=[1], value=iter_num, dtype="int32")
a = []
b = [x]
b.append(x)
b.pop()
i = 0
while i < iter_num:
a.append(x + i)
b.append(x - i)
i += 1
if i % 2 == 1:
a.pop()
return a[0]
return a[0], b[2]
class TestListWithoutControlFlow(unittest.TestCase):
......
......@@ -15,6 +15,7 @@
from __future__ import print_function
import unittest
import paddle
import paddle.fluid.core as core
import numpy
......@@ -50,5 +51,49 @@ class TestLoDTensorArray(unittest.TestCase):
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__':
unittest.main()
......@@ -122,13 +122,15 @@ def array_write(x, i, array=None):
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
:ref:`api_paddle_tensor_array_array_write`.
Args:
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:
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):
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.
先完成此消息的编辑!
想要评论请 注册