array.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # Define functions about array.
  15. import paddle
  16. from ..base.data_feeder import check_type, check_variable_and_dtype
  17. from ..base.framework import in_pir_mode
  18. from ..common_ops_import import Variable
  19. from ..framework import LayerHelper, core, in_dynamic_mode
  20. __all__ = []
  21. def array_length(array):
  22. """
  23. This OP is used to get the length of the input array.
  24. Args:
  25. array (list|Tensor): The input array that will be used to compute the length. In dynamic mode, ``array`` is a Python list. But in static graph mode, array is a Tensor whose VarType is LOD_TENSOR_ARRAY.
  26. Returns:
  27. Tensor: 0-D Tensor with shape [], which is the length of array.
  28. Examples:
  29. .. code-block:: python
  30. >>> import paddle
  31. >>> arr = paddle.tensor.create_array(dtype='float32')
  32. >>> x = paddle.full(shape=[3, 3], fill_value=5, dtype="float32")
  33. >>> i = paddle.zeros(shape=[1], dtype="int32")
  34. >>> arr = paddle.tensor.array_write(x, i, array=arr)
  35. >>> arr_len = paddle.tensor.array_length(arr)
  36. >>> print(arr_len)
  37. 1
  38. """
  39. if in_dynamic_mode():
  40. assert isinstance(
  41. array, list
  42. ), "The 'array' in array_write must be a list in dygraph mode"
  43. return len(array)
  44. elif in_pir_mode():
  45. if (
  46. not isinstance(array, paddle.pir.Value)
  47. or not array.is_dense_tensor_array_type()
  48. ):
  49. raise TypeError(
  50. "array should be tensor array variable in array_length Op"
  51. )
  52. return paddle._pir_ops.array_length(array)
  53. else:
  54. if (
  55. not isinstance(array, Variable)
  56. or array.type != core.VarDesc.VarType.LOD_TENSOR_ARRAY
  57. ):
  58. raise TypeError(
  59. "array should be tensor array variable in array_length Op"
  60. )
  61. helper = LayerHelper('array_length', **locals())
  62. tmp = helper.create_variable_for_type_inference(dtype='int64')
  63. tmp.stop_gradient = True
  64. helper.append_op(
  65. type='lod_array_length',
  66. inputs={'X': [array]},
  67. outputs={'Out': [tmp]},
  68. )
  69. return tmp
  70. def array_read(array, i):
  71. """
  72. This OP is used to read data at the specified position from the input array.
  73. Case:
  74. .. code-block:: text
  75. Input:
  76. The shape of first three tensors are [1], and that of the last one is [1,2]:
  77. array = ([0.6], [0.1], [0.3], [0.4, 0.2])
  78. And:
  79. i = [3]
  80. Output:
  81. output = [0.4, 0.2]
  82. Args:
  83. array (list|Tensor): The input array. In dynamic mode, ``array`` is a Python list. But in static graph mode, array is a Tensor whose ``VarType`` is ``LOD_TENSOR_ARRAY``.
  84. i (Tensor): 1-D Tensor, whose shape is [1] and dtype is int64. It represents the
  85. specified read position of ``array``.
  86. Returns:
  87. Tensor: A Tensor that is read at the specified position of ``array``.
  88. Examples:
  89. .. code-block:: python
  90. >>> import paddle
  91. >>> arr = paddle.tensor.create_array(dtype="float32")
  92. >>> x = paddle.full(shape=[1, 3], fill_value=5, dtype="float32")
  93. >>> i = paddle.zeros(shape=[1], dtype="int32")
  94. >>> arr = paddle.tensor.array_write(x, i, array=arr)
  95. >>> item = paddle.tensor.array_read(arr, i)
  96. >>> print(item.numpy())
  97. [[5. 5. 5.]]
  98. """
  99. if in_dynamic_mode():
  100. assert isinstance(
  101. array, list
  102. ), "The 'array' in array_read must be list in dygraph mode"
  103. assert isinstance(
  104. i, Variable
  105. ), "The index 'i' in array_read must be Variable in dygraph mode"
  106. assert i.shape == [
  107. 1
  108. ], "The shape of index 'i' should be [1] in dygraph mode"
  109. i = i.item(0)
  110. return array[i]
  111. elif in_pir_mode():
  112. if (
  113. not isinstance(array, paddle.pir.Value)
  114. or not array.is_dense_tensor_array_type()
  115. ):
  116. raise TypeError(
  117. "array should be tensor array variable in array_length Op"
  118. )
  119. return paddle._pir_ops.array_read(array, i)
  120. else:
  121. check_variable_and_dtype(i, 'i', ['int64'], 'array_read')
  122. helper = LayerHelper('array_read', **locals())
  123. if (
  124. not isinstance(array, Variable)
  125. or array.type != core.VarDesc.VarType.LOD_TENSOR_ARRAY
  126. ):
  127. raise TypeError("array should be tensor array variable")
  128. out = helper.create_variable_for_type_inference(dtype=array.dtype)
  129. helper.append_op(
  130. type='read_from_array',
  131. inputs={'X': [array], 'I': [i]},
  132. outputs={'Out': [out]},
  133. )
  134. return out
  135. def array_write(x, i, array=None):
  136. """
  137. This OP writes the input ``x`` into the i-th position of the ``array`` returns the modified array.
  138. If ``array`` is none, a new array will be created and returned.
  139. Args:
  140. x (Tensor): The input data to be written into array. It's multi-dimensional
  141. Tensor or LoDTensor. Data type: float32, float64, int32, int64 and bool.
  142. i (Tensor): 0-D Tensor with shape [], which represents the position into which
  143. ``x`` is written.
  144. array (list|Tensor, optional): The array into which ``x`` is written. The default value is None,
  145. when a new array will be created and returned as a result. In dynamic mode, ``array`` is a Python list.
  146. But in static graph mode, array is a Tensor whose ``VarType`` is ``LOD_TENSOR_ARRAY``.
  147. Returns:
  148. list|Tensor: The input ``array`` after ``x`` is written into.
  149. Examples:
  150. .. code-block:: python
  151. >>> import paddle
  152. >>> arr = paddle.tensor.create_array(dtype="float32")
  153. >>> x = paddle.full(shape=[1, 3], fill_value=5, dtype="float32")
  154. >>> i = paddle.zeros(shape=[1], dtype="int32")
  155. >>> arr = paddle.tensor.array_write(x, i, array=arr)
  156. >>> item = paddle.tensor.array_read(arr, i)
  157. >>> print(item.numpy())
  158. [[5. 5. 5.]]
  159. """
  160. if in_dynamic_mode():
  161. assert isinstance(
  162. x, Variable
  163. ), "The input data 'x' in array_write must be Variable in dygraph mode"
  164. assert isinstance(
  165. i, Variable
  166. ), "The index 'i' in array_write must be Variable in dygraph mode"
  167. assert i.shape == [
  168. 1
  169. ], "The shape of index 'i' should be [1] in dygraph mode"
  170. i = i.item(0)
  171. if array is None:
  172. array = create_array(x.dtype)
  173. assert isinstance(
  174. array, list
  175. ), "The 'array' in array_write must be a list in dygraph mode"
  176. assert i <= len(
  177. array
  178. ), "The index 'i' should not be greater than the length of 'array' in dygraph mode"
  179. if i < len(array):
  180. array[i] = x
  181. else:
  182. array.append(x)
  183. return array
  184. elif in_pir_mode():
  185. check_variable_and_dtype(i, 'i', ['int64'], 'array_write')
  186. if not isinstance(x, paddle.pir.Value):
  187. raise TypeError(f"x should be pir.Value, but received {type(x)}.")
  188. if array is not None:
  189. if (
  190. not isinstance(array, paddle.pir.Value)
  191. or not array.is_dense_tensor_array_type()
  192. ):
  193. raise TypeError("array should be tensor array variable")
  194. if array is None:
  195. array = paddle._pir_ops.create_array(x.dtype)
  196. paddle._pir_ops.array_write_(array, x, i)
  197. return array
  198. else:
  199. check_variable_and_dtype(i, 'i', ['int64'], 'array_write')
  200. check_type(x, 'x', (Variable), 'array_write')
  201. helper = LayerHelper('array_write', **locals())
  202. if array is not None:
  203. if (
  204. not isinstance(array, Variable)
  205. or array.type != core.VarDesc.VarType.LOD_TENSOR_ARRAY
  206. ):
  207. raise TypeError(
  208. "array should be tensor array variable in array_write Op"
  209. )
  210. if array is None:
  211. array = helper.create_variable(
  212. name=f"{helper.name}.out",
  213. type=core.VarDesc.VarType.LOD_TENSOR_ARRAY,
  214. dtype=x.dtype,
  215. )
  216. helper.append_op(
  217. type='write_to_array',
  218. inputs={'X': [x], 'I': [i]},
  219. outputs={'Out': [array]},
  220. )
  221. return array
  222. def create_array(dtype, initialized_list=None):
  223. """
  224. This OP creates an array. It is used as the input of :ref:`api_paddle_tensor_array_array_read` and
  225. :ref:`api_paddle_tensor_array_array_write`.
  226. Args:
  227. dtype (str): The data type of the elements in the array. Support data type: float32, float64, int32, int64 and bool.
  228. initialized_list(list): Used to initialize as default value for created array.
  229. All values in initialized list should be a Tensor.
  230. Returns:
  231. list|Tensor: An empty array. In dynamic mode, ``array`` is a Python list. But in static graph mode, array is a Tensor
  232. whose ``VarType`` is ``LOD_TENSOR_ARRAY``.
  233. Examples:
  234. .. code-block:: python
  235. >>> import paddle
  236. >>> arr = paddle.tensor.create_array(dtype="float32")
  237. >>> x = paddle.full(shape=[1, 3], fill_value=5, dtype="float32")
  238. >>> i = paddle.zeros(shape=[1], dtype="int32")
  239. >>> arr = paddle.tensor.array_write(x, i, array=arr)
  240. >>> item = paddle.tensor.array_read(arr, i)
  241. >>> print(item.numpy())
  242. [[5. 5. 5.]]
  243. """
  244. array = []
  245. if initialized_list is not None:
  246. if not isinstance(initialized_list, (list, tuple)):
  247. raise TypeError(
  248. f"Require type(initialized_list) should be list/tuple, but received {type(initialized_list)}"
  249. )
  250. array = list(initialized_list)
  251. # NOTE: Only support plain list like [x, y,...], not support nested list in static graph mode.
  252. for val in array:
  253. if not isinstance(val, (Variable, paddle.pir.Value)):
  254. raise TypeError(
  255. f"All values in `initialized_list` should be Variable or pir.Value, but received {type(val)}."
  256. )
  257. if in_dynamic_mode():
  258. return array
  259. elif in_pir_mode():
  260. if not isinstance(dtype, (core.VarDesc.VarType, core.DataType)):
  261. dtype = paddle.base.framework.convert_np_dtype_to_dtype_(dtype)
  262. out = paddle._pir_ops.create_array(dtype)
  263. for val in array:
  264. paddle._pir_ops.array_write_(out, val, array_length(out))
  265. return out
  266. else:
  267. helper = LayerHelper("array", **locals())
  268. tensor_array = helper.create_variable(
  269. name=f"{helper.name}.out",
  270. type=core.VarDesc.VarType.LOD_TENSOR_ARRAY,
  271. dtype=dtype,
  272. )
  273. for val in array:
  274. array_write(x=val, i=array_length(tensor_array), array=tensor_array)
  275. return tensor_array