Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ def replace_list_with_tensor_array(self, node):
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):
Expand Down Expand Up @@ -189,9 +190,11 @@ def _need_to_create_tensor_array(self, node):
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

Expand Down
28 changes: 25 additions & 3 deletions python/paddle/fluid/layers/control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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``.
Expand All @@ -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):
Expand Down
22 changes: 21 additions & 1 deletion python/paddle/fluid/layers/math_op_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
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,
Expand Down Expand Up @@ -182,6 +183,24 @@ def astype(self, dtype):
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)
Expand Down Expand Up @@ -344,6 +363,7 @@ def __impl__(self, other_var):
# b=-a
('__neg__', _neg_),
('astype', astype),
('append', append),
('dim', lambda x: len(x.shape)),
('ndimension', lambda x: len(x.shape)),
('ndim', _ndim_),
Expand Down
15 changes: 12 additions & 3 deletions python/paddle/fluid/tests/unittests/dygraph_to_static/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -165,29 +168,35 @@ 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):
x = fluid.dygraph.to_variable(x)
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):
Expand Down
45 changes: 45 additions & 0 deletions python/paddle/fluid/tests/unittests/test_lod_tensor_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from __future__ import print_function

import unittest
import paddle
import paddle.fluid.core as core
import numpy

Expand Down Expand Up @@ -50,5 +51,49 @@ def test_get_set(self):
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()
6 changes: 4 additions & 2 deletions python/paddle/tensor/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)