creation.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. # Copyright (c) 2022 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. import numpy as np
  15. import paddle
  16. from paddle import _C_ops, in_dynamic_mode
  17. from paddle.base.data_feeder import convert_dtype
  18. from paddle.base.framework import (
  19. _current_expected_place,
  20. _get_paddle_place,
  21. core,
  22. dygraph_only,
  23. )
  24. from paddle.base.layer_helper import LayerHelper
  25. from paddle.tensor import max, to_tensor
  26. __all__ = [
  27. 'sparse_coo_tensor',
  28. 'sparse_csr_tensor',
  29. ]
  30. def _handle_dtype(data, dtype):
  31. if dtype:
  32. if convert_dtype(dtype) != convert_dtype(data.dtype):
  33. return data.astype(convert_dtype(dtype))
  34. return data
  35. def _infer_dense_shape(indices, values):
  36. assert len(indices.shape) == 2
  37. lens = max(indices, axis=1)
  38. lens = lens + 1
  39. lens = lens.numpy()
  40. if len(values.shape) > 1:
  41. lens = np.append(lens, values.shape[1:])
  42. return list(lens)
  43. def _get_place(place):
  44. place = _get_paddle_place(place)
  45. if place is None:
  46. place = _current_expected_place()
  47. elif not isinstance(
  48. place, (core.Place, core.CPUPlace, core.CUDAPinnedPlace, core.CUDAPlace)
  49. ):
  50. raise ValueError(
  51. "'place' must be any of paddle.Place, paddle.CPUPlace, paddle.CUDAPinnedPlace, paddle.CUDAPlace"
  52. )
  53. return place
  54. def _check_indices_dtype(dtype):
  55. if dtype not in [paddle.int8, paddle.int16, paddle.int32, paddle.int64]:
  56. raise TypeError(
  57. "the dtype of indices must be 'int8' or 'int16' or 'int32' or 'int64'"
  58. )
  59. def sparse_coo_tensor(
  60. indices, values, shape=None, dtype=None, place=None, stop_gradient=True
  61. ):
  62. r"""
  63. Constructs a sparse ``paddle.Tensor`` in coordinate format according to the indices
  64. and values of the specified non-zero elements.
  65. Args:
  66. indices(list|tuple|ndarray|Tensor): the indices of non-zero elements.
  67. Can be a list, tuple, numpy\.ndarray, paddle\.Tensor. The indices must be 2-D.
  68. values(list|tuple|ndarray|Tensor): Initial values for the tensor.
  69. Can be a scalar, list, tuple, numpy\.ndarray, paddle\.Tensor.
  70. shape(list|tuple, optional): The shape of the sparse tensor also represents the shape of
  71. original dense tensor. If not provided the smallest shape will be inferred to
  72. hold all elements.
  73. dtype(str|np.dtype, optional): The desired data type of returned tensor. Can be 'bool' , 'float16' ,
  74. 'float32' , 'float64' , 'int8' , 'int16' , 'int32' , 'int64' , 'uint8',
  75. 'complex64' , 'complex128'. Default: None, infers dtype from ``data``
  76. except for python float number which gets dtype from ``get_default_type`` .
  77. place(CPUPlace|CUDAPinnedPlace|CUDAPlace|str, optional): The place to allocate Tensor. Can be
  78. CPUPlace, CUDAPinnedPlace, CUDAPlace. Default: None, means global place. If ``place`` is
  79. string, It can be ``cpu``, ``gpu:x`` and ``gpu_pinned``, where ``x`` is the index of the GPUs.
  80. stop_gradient(bool, optional): Whether to block the gradient propagation of Autograd. Default: True.
  81. Returns:
  82. Tensor: A Tensor constructed from ``indices`` and ``values`` .
  83. Examples:
  84. .. code-block:: python
  85. >>> import paddle
  86. >>> indices = [[0, 1, 2], [1, 2, 0]]
  87. >>> values = [1.0, 2.0, 3.0]
  88. >>> dense_shape = [3, 3]
  89. >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape)
  90. >>> print(coo)
  91. Tensor(shape=[3, 3], dtype=paddle.float32, place=Place(cpu), stop_gradient=True,
  92. indices=[[0, 1, 2],
  93. [1, 2, 0]],
  94. values=[1., 2., 3.])
  95. """
  96. if in_dynamic_mode():
  97. place = _get_place(place)
  98. if not isinstance(indices, core.eager.Tensor):
  99. indices = to_tensor(
  100. indices, dtype=None, place=place, stop_gradient=True
  101. )
  102. if not isinstance(values, core.eager.Tensor):
  103. values = to_tensor(values, dtype, place, stop_gradient)
  104. if len(indices.shape) != 2:
  105. raise ValueError("'indices' must be 2-D.")
  106. nnz = indices.shape[1]
  107. sparse_dim = indices.shape[0]
  108. _check_indices_dtype(indices.dtype)
  109. if nnz != values.shape[0]:
  110. raise ValueError(
  111. f"the indices and values must have same number of non-zero, but get {nnz} and {values.shape[0]}"
  112. )
  113. dense_dim = len(values.shape) - 1
  114. if not indices.place._equals(place):
  115. indices = indices._copy_to(place, False)
  116. if not values.place._equals(place):
  117. values = values._copy_to(place, False)
  118. values = _handle_dtype(values, dtype)
  119. values.stop_gradient = stop_gradient
  120. min_shape = _infer_dense_shape(indices, values)
  121. if shape is None:
  122. shape = min_shape
  123. else:
  124. shape = list(shape)
  125. if shape < min_shape:
  126. raise ValueError(
  127. f"the minimum shape required is {min_shape}, but get {shape}"
  128. )
  129. if len(shape) != sparse_dim + dense_dim:
  130. raise ValueError(
  131. f"the number of dimensions(len(shape) must be sparse_dim({sparse_dim}) + dense_dim({dense_dim}), but get {len(shape)}"
  132. )
  133. return _C_ops.sparse_sparse_coo_tensor(values, indices, shape)
  134. else:
  135. op_type = 'sparse_sparse_coo_tensor'
  136. inputs = {'values': values, 'indices': indices}
  137. if shape[0] is None:
  138. shape[0] = -1
  139. attrs = {'shape': shape}
  140. helper = LayerHelper(op_type)
  141. out = helper.create_sparse_variable_for_type_inference(dtype)
  142. helper.append_op(
  143. type=op_type, inputs=inputs, outputs={'out': out}, attrs=attrs
  144. )
  145. return out
  146. # TODO: need to support shape is None
  147. @dygraph_only
  148. def sparse_csr_tensor(
  149. crows, cols, values, shape, dtype=None, place=None, stop_gradient=True
  150. ):
  151. r"""
  152. Constructs a sparse ``paddle.Tensor`` in CSR(Compressed Sparse Row) format according to the
  153. ``crows``, ``cols`` and ``values``.
  154. Currently, the crows and cols of each batch must be incremented.
  155. Args:
  156. crows(list|tuple|ndarray|Tensor): 1-D array, each element in the rows represents the
  157. starting position of the first non-zero element of each row in values.
  158. Can be a list, tuple, numpy\.ndarray, paddle\.Tensor.
  159. cols(list|tuple|ndarray|Tensor): 1-D array, the column of non-zero elements.
  160. Can be a list, tuple, numpy\.ndarray, paddle\.Tensor.
  161. values(list|tuple|ndarray|Tensor): 1-D array, the non-zero elements.
  162. Can be a scalar, list, tuple, numpy\.ndarray, paddle\.Tensor.
  163. shape(list|tuple, optional): The shape of the sparse tensor also represents the shape of
  164. original dense tensor.
  165. hold all elements.
  166. dtype(str|np.dtype, optional): The desired data type of returned tensor. Can be 'bool' , 'float16' ,
  167. 'float32' , 'float64' , 'int8' , 'int16' , 'int32' , 'int64' , 'uint8',
  168. 'complex64' , 'complex128'. Default: None, infers dtype from ``data``
  169. except for python float number which gets dtype from ``get_default_type`` .
  170. place(CPUPlace|CUDAPinnedPlace|CUDAPlace|str, optional): The place to allocate Tensor. Can be
  171. CPUPlace, CUDAPinnedPlace, CUDAPlace. Default: None, means global place. If ``place`` is
  172. string, It can be ``cpu``, ``gpu:x`` and ``gpu_pinned``, where ``x`` is the index of the GPUs.
  173. stop_gradient(bool, optional): Whether to block the gradient propagation of Autograd. Default: True.
  174. Returns:
  175. Tensor: A Tensor constructed from ``crows``, ``cols`` and ``values`` .
  176. Examples:
  177. .. code-block:: python
  178. >>> import paddle
  179. >>> crows = [0, 2, 3, 5]
  180. >>> cols = [1, 3, 2, 0, 1]
  181. >>> values = [1, 2, 3, 4, 5]
  182. >>> dense_shape = [3, 4]
  183. >>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape)
  184. >>> print(csr)
  185. Tensor(shape=[3, 4], dtype=paddle.int64, place=Place(cpu), stop_gradient=True,
  186. crows=[0, 2, 3, 5],
  187. cols=[1, 3, 2, 0, 1],
  188. values=[1, 2, 3, 4, 5])
  189. """
  190. place = _get_place(place)
  191. if not isinstance(crows, core.eager.Tensor):
  192. crows = to_tensor(crows, dtype=None, place=place, stop_gradient=True)
  193. if not isinstance(cols, core.eager.Tensor):
  194. cols = to_tensor(cols, dtype=None, place=place, stop_gradient=True)
  195. if not isinstance(values, core.eager.Tensor):
  196. values = to_tensor(values, dtype, place, stop_gradient)
  197. _check_indices_dtype(crows.dtype)
  198. _check_indices_dtype(cols.dtype)
  199. if len(shape) != 2 and len(shape) != 3:
  200. raise ValueError(
  201. f"SparseCsrTensor only support 2-D or 3-D matrix. but get shape {shape}"
  202. )
  203. rows = shape[len(shape) - 2]
  204. if not crows.place._equals(place):
  205. crows = crows._copy_to(place, False)
  206. if not cols.place._equals(place):
  207. cols = cols._copy_to(place, False)
  208. if not values.place._equals(place):
  209. values = values._copy_to(place, False)
  210. values = _handle_dtype(values, dtype)
  211. values.stop_gradient = stop_gradient
  212. if len(crows.shape) != 1 or len(cols.shape) != 1 or len(values.shape) != 1:
  213. raise ValueError("The 'crows', 'cols' and 'values' must be 1-D.")
  214. if len(cols) != len(values):
  215. raise ValueError("the length of cols must be same as length of values")
  216. if len(shape) == 2:
  217. if crows.shape[0] != rows + 1:
  218. raise ValueError(
  219. f"The length({crows.shape[0]}) of crows must be equal to the rows({rows})+1 of matrix."
  220. )
  221. if crows[0] != 0:
  222. raise ValueError("the 0th value of crows must be 0")
  223. if crows[-1] != values.shape[0]:
  224. raise ValueError(
  225. "the last value of crows must be equal the number of non-zero"
  226. )
  227. else:
  228. if crows.shape[0] % (rows + 1) != 0:
  229. raise ValueError(
  230. f"The length({crows.shape[0]}) of crows must be divisible the rows({rows})+1 of matrix."
  231. )
  232. # TODO(zkh2016): check whether the value in crows and cols is legal
  233. return core.eager.sparse_csr_tensor(
  234. crows, cols, values, shape, stop_gradient
  235. )