• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 Huawei Technologies Co., Ltd
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# ============================================================================
15"""SparseTensor implementation."""
16from __future__ import absolute_import, annotations
17
18__all__ = ['RowTensorInner', 'RowTensor', 'SparseTensor', 'COOTensor', 'CSRTensor']
19
20from typing import Tuple, Union
21
22from mindspore import log as logger
23from mindspore.common import dtype as mstype
24from mindspore.common._register_for_tensor import tensor_operator_registry
25from mindspore.common.tensor import Tensor
26from mindspore._c_expression import COOTensor as COOTensor_
27from mindspore._c_expression import CSRTensor as CSRTensor_
28from mindspore._c_expression import RowTensor as RowTensor_
29from mindspore._c_expression import Tensor as Tensor_
30from mindspore import _checkparam as validator
31from mindspore._checkparam import is_stub_tensor
32
33
34class RowTensorInner(RowTensor_):
35    """
36    Implementation for RowTensor, for MindSpore developers only.
37    """
38
39    def __init__(self, indices=None, values=None, shape=None, row_tensor=None):
40        """Init RowTensor"""
41        self.init_finished = False
42        # Directly init a RowTensor from another RowTensor
43        if row_tensor is not None:
44            if not isinstance(row_tensor, (RowTensor, RowTensor_)):
45                raise TypeError(f"Expect input `row_tensor` to be a RowTensor, but got {type(row_tensor)}")
46            if not (indices is None and values is None and shape is None):
47                raise TypeError("If input `row_tensor` is provided, `indices`, `values`, `shapes` should all be `None`")
48            RowTensor_.__init__(self, row_tensor)
49        # Init a RowTensor from indices, values and shape
50        else:
51            if is_stub_tensor(values):
52                values = values.stub_sync()
53            RowTensor_.__init__(self, indices, values, shape)
54        self.init_finished = True
55
56    def __repr__(self):
57        """Avoid PyTest Segfault when RowTensor is not initialized."""
58        if self.init_finished:
59            return RowTensor_.__repr__(self)
60        return ''
61
62    @property
63    def indices(self):
64        """Return RowTensor's indices."""
65        return Tensor(self._indices)
66
67    @property
68    def values(self):
69        """Return RowTensor's non-zero values."""
70        return Tensor(self._values)
71
72    @property
73    def dense_shape(self):
74        """Return RowTensor's shape."""
75        return self._shape
76
77
78class RowTensor(RowTensorInner):
79    """
80    A sparse representation of a set of tensor slices at given indices.
81
82    When the `values` of a RowTensor has a shape of :math:`(d_0, d_1, ..., d_n)`, then this RowTensor is used to
83    represent a subset of a larger dense tensor of shape :math:`(l_0, d_1, ..., d_n)`, where :math:`d_i` is the size of
84    i-th axis in RowTensor, :math:`l_0` is the size of 0-th axis of dense tensor and it satisfies :math:`l_0 > d_0`.
85
86    The parameter `indices` is used to specify locations from which the `RowTensor` is sliced in the first dimension of
87    the dense tensor, which means the parameters `indices` and `values` have the following relationship
88    :math:`dense[indices[i], :, :, :, ...] = values[i, :, :, :, ...]`.
89
90    For example, if indices is [0], values is [[1, 2]], shape is
91    :math:`(3, 2)` , then the dense representation of the row tensor will be:
92
93    .. code-block::
94
95        [[1, 2],
96         [0, 0],
97         [0, 0]]
98
99    .. warning::
100        This is an experimental API that is subjected to change or deletion.
101
102    Args:
103        indices (Tensor): A 1-D integer Tensor of shape :math:`(d_0)` . Default: ``None``.
104        values (Tensor): A Tensor of any dtype of shape :math:`(d_0, d_1, ..., d_n)` . Default: ``None``.
105        shape (tuple(int)): An integer tuple which contains the shape
106            of the corresponding dense tensor. Default: ``None``.
107        row_tensor (RowTensor): A RowTensor object. Default: ``None``.
108
109    Returns:
110        RowTensor, composed of `indices`, `values`, and `shape`.
111
112    Examples:
113        >>> import mindspore as ms
114        >>> from mindspore import Tensor, RowTensor
115        >>> indices = Tensor([0])
116        >>> values = Tensor([[1, 2]], dtype=ms.float32)
117        >>> shape = (3, 2)
118        >>> x = RowTensor(indices, values, shape)
119        >>> print(x.values)
120        [[1. 2.]]
121        >>> print(x.indices)
122        [0]
123        >>> print(x.dense_shape)
124        (3, 2)
125    """
126
127    def __init__(self, indices=None, values=None, shape=None, row_tensor=None):
128        """Init RowTensor"""
129        logger.warning("'RowTensor' is deprecated from version 1.7 and will be removed in a future version.")
130        super().__init__(indices, values, shape, row_tensor)
131
132
133class SparseTensor(COOTensor_):
134    """
135    A sparse representation of a set of nonzero elements from a tensor at given indices.
136
137    SparseTensor can only be used in the `Cell`'s construct method.
138
139    For a tensor dense, its SparseTensor(indices, values, dense_shape) has
140    `dense[indices[i]] = values[i]`.
141
142    For example, if indices is [[0, 1], [1, 2]], values is [1, 2], dense_shape is
143    (3, 4), then the dense representation of the sparse tensor will be:
144
145    .. code-block::
146
147        [[0, 1, 0, 0],
148         [0, 0, 2, 0],
149         [0, 0, 0, 0]]
150
151    Note:
152        The interface is deprecated from version 1.7 and will be removed in a future version.
153        Please use :class:`mindspore.COOTensor` instead.
154
155    Args:
156        indices (Tensor): A 2-D integer Tensor of shape :math:`(N, ndims)`,
157            where N and ndims are the number of `values` and number of dimensions in
158            the SparseTensor, respectively.
159        values (Tensor): A 1-D tensor of any type and shape :math:`(N)`, which
160            supplies the values for each element in `indices`.
161        shape (tuple(int)): An integer tuple of size :math:`(ndims)`,
162            which specifies the shape of the sparse tensor.
163
164    Returns:
165        SparseTensor, composed of `indices`, `values`, and `shape`.
166
167    Examples:
168        >>> import mindspore as ms
169        >>> from mindspore import Tensor, SparseTensor
170        >>> indices = Tensor([[0, 1], [1, 2]])
171        >>> values = Tensor([1, 2], dtype=ms.float32)
172        >>> shape = (3, 4)
173        >>> x = SparseTensor(indices, values, shape)
174        >>> print(x.values)
175        [1. 2.]
176        >>> print(x.indices)
177        [[0 1]
178         [1 2]]
179        >>> print(x.shape)
180        (3, 4)
181    """
182
183    def __init__(self, indices, values, shape):
184        """Init COOTensor."""
185        logger.warning("'SparseTensor' is deprecated from version 1.7 and will be removed in a future version. " +
186                       "Please use 'COOTensor' instead.")
187        if not (isinstance(indices, Tensor) and isinstance(values, Tensor) and isinstance(shape, tuple)):
188            raise TypeError("Inputs must follow: COOTensor(indices, values, shape).")
189        if is_stub_tensor(indices):
190            indices = indices.stub_sync()
191        if is_stub_tensor(values):
192            values = values.stub_sync()
193        COOTensor_.__init__(self, indices, values, shape)
194
195    @property
196    def indices(self):
197        """Return SparseTensor's indices."""
198        return Tensor(self._indices)
199
200    @property
201    def values(self):
202        """Return SparseTensor's non-zero values."""
203        return Tensor(self._values)
204
205    @property
206    def shape(self):
207        """Return SparseTensor's shape."""
208        return self._shape
209
210
211class COOTensor(COOTensor_):
212    """
213    A sparse representation of a set of nonzero elements from a tensor at given indices.
214
215    For a tensor dense, its COOTensor(indices, values, shape) has
216    `dense[indices[i]] = values[i]`.
217
218    For example, if indices is [[0, 1], [1, 2]], values is [1, 2], shape is
219    (3, 4), then the dense representation of the sparse tensor will be:
220
221    .. code-block::
222
223        [[0, 1, 0, 0],
224         [0, 0, 2, 0],
225         [0, 0, 0, 0]]
226
227    Common arithmetic operations include: addition (+), subtraction (-), multiplication (*),
228    and division (/). For details about operations supported by `COOTensor`, see
229    `operators <https://www.mindspore.cn/docs/en/master/note/static_graph_syntax_support.html#operators>`_.
230
231    .. warning::
232        - This is an experimental API that is subject to change or deletion.
233        - Currently, duplicate coordinates in the indices will not be coalesced.
234          If the indices contain out-of-bound values, the result will be undefined.
235
236    Args:
237        indices (Tensor): A 2-D integer Tensor of shape :math:`(N, ndims)`,
238            where N and ndims are the number of `values` and number of dimensions in
239            the COOTensor, respectively. Currently, `ndims` must be 2. Default: ``None`` .
240            Please make sure that the indices are in range of the given shape.
241        values (Tensor): A 1-D tensor of any type and shape :math:`(N)`, which
242            supplies the values for each element in `indices`. Default: ``None`` .
243        shape (tuple(int)): An integer tuple of shape :math:`(ndims)`,
244            which specifies the dense_shape of the sparse tensor. Default: ``None`` .
245        coo_tensor (COOTensor): A COOTensor object. Default: ``None`` .
246
247    Returns:
248        COOTensor, composed of `indices`, `values`, and `shape`.
249
250    Examples:
251        >>> import mindspore as ms
252        >>> from mindspore import Tensor, COOTensor
253        >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
254        >>> values = Tensor([1, 2], dtype=ms.float32)
255        >>> shape = (3, 4)
256        >>> x = COOTensor(indices, values, shape)
257        >>> print(x.values)
258        [1. 2.]
259        >>> print(x.indices)
260        [[0 1]
261         [1 2]]
262        >>> print(x.shape)
263        (3, 4)
264    """
265
266    def __init__(self, indices=None, values=None, shape=None, coo_tensor=None):
267        """Init COOTensor"""
268        self.init_finished = False
269        # Directly init a COOTensor from another COOTensor
270        if coo_tensor is not None:
271            if not isinstance(coo_tensor, (COOTensor, COOTensor_)):
272                raise TypeError(f"Expect input `coo_tensor` to be a COOTensor, but got {type(coo_tensor)}")
273            if not (indices is None and values is None and shape is None):
274                raise TypeError("If input `coo_tensor` is provided, `indices`, `values`, `shapes` should all be `None`")
275            COOTensor_.__init__(self, coo_tensor)
276        # Init a COOTensor from indices, values and shape
277        else:
278            validator.check_coo_tensor_input(indices, values, shape)
279            validator.check_coo_tensor_shape(indices.shape, values.shape, shape)
280            validator.check_coo_tensor_dtype(indices.dtype)
281            indices = tensor_operator_registry.get('stop_gradient')(indices)
282            if is_stub_tensor(indices):
283                indices = indices.stub_sync()
284            if is_stub_tensor(values):
285                values = values.stub_sync()
286            COOTensor_.__init__(self, indices, values, shape)
287        self.init_finished = True
288
289    def __repr__(self):
290        """Avoid PyTest Segfault when COOTensor is not initialized."""
291        if self.init_finished:
292            return COOTensor_.__repr__(self)
293        return ''
294
295    def __neg__(self):
296        return COOTensor(self.indices, -self.values, self.shape)
297
298    def __add__(self, other):
299        if not self.shape == other.shape:
300            raise ValueError("Input tensors should have the same shape.")
301        if isinstance(other, Tensor):
302            return tensor_operator_registry.get("tensor_scatter_add")(other, self.indices, self.values)
303        if isinstance(other, COOTensor):
304            return tensor_operator_registry.get('coo_add')(self, other, Tensor(0, self.values.dtype))
305        raise TypeError("COOTensor add with %s is not supported." % type(other))
306
307    def __sub__(self, other):
308        if not self.shape == other.shape:
309            raise ValueError("Input tensors should have the same shape.")
310        if isinstance(other, Tensor):
311            return tensor_operator_registry.get("tensor_scatter_add")(-other, self.indices, self.values)
312        if isinstance(other, COOTensor):
313            return tensor_operator_registry.get('coo_add')(
314                self, -other, Tensor(0, self.values.dtype))
315        raise TypeError("COOTensor subtract with %s is not supported." % type(other))
316
317    def __mul__(self, other):
318        if not self.shape == other.shape:
319            raise ValueError("Input tensors should have the same shape.")
320        if isinstance(other, Tensor):
321            other_values = tensor_operator_registry.get("gather_nd")(other, self.indices)
322            return COOTensor(self.indices, self.values * other_values, self.shape)
323        raise TypeError("COOTensor multiply with %s is not supported." % type(other))
324
325    def __div__(self, other):
326        if not self.shape == other.shape:
327            raise ValueError("Input tensors should have the same shape.")
328        if isinstance(other, Tensor):
329            logger.warning("For sparse divide, zero values in the dense tensor are ignored.")
330            other_values = tensor_operator_registry.get("gather_nd")(other, self.indices)
331            return COOTensor(self.indices, self.values / other_values, self.shape)
332        raise TypeError("COOTensor divide with %s is not supported." % type(other))
333
334    def __truediv__(self, other):
335        return self.__div__(other)
336
337    @property
338    def indices(self) -> Tensor:
339        """Return COOTensor's indices."""
340        return Tensor(self._indices)
341
342    @property
343    def values(self) -> Tensor:
344        """Return COOTensor's non-zero values."""
345        return Tensor(self._values)
346
347    @property
348    def shape(self) -> Tuple[int, ...]:
349        """Return COOTensor's shape."""
350        return self._shape
351
352    @property
353    def dtype(self) -> mstype:
354        """
355        Return the dtype of the values of COOTensor (:class:`mindspore.dtype`).
356
357        Examples:
358            >>> import mindspore as ms
359            >>> from mindspore import Tensor, COOTensor
360            >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
361            >>> values = Tensor([1, 2], dtype=ms.float32)
362            >>> shape = (3, 4)
363            >>> coo_tensor = COOTensor(indices, values, shape)
364            >>> print(coo_tensor.dtype)
365            Float32
366        """
367        return self._dtype
368
369    @property
370    def size(self) -> int:
371        """
372        Return the number of non-zero values.
373
374        Examples:
375            >>> import mindspore as ms
376            >>> from mindspore import Tensor, COOTensor
377            >>> indices = Tensor([[0, 1, 2], [1, 0, 2]], dtype=ms.int32)
378            >>> values = Tensor([1, 5, 4], dtype=ms.float32)
379            >>> shape = (3, 3)
380            >>> coo_tensor = COOTensor(indices.transpose(), values, shape)
381            >>> print(coo_tensor.size)
382            3
383        """
384        return self.values.size
385
386    @property
387    def itemsize(self) -> int:
388        """
389        Return the length of one tensor element in bytes.
390
391        Examples:
392            >>> import mindspore as ms
393            >>> from mindspore import Tensor, COOTensor
394            >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
395            >>> values = Tensor([1, 2], dtype=ms.float64)
396            >>> shape = (3, 4)
397            >>> coo_tensor = COOTensor(indices, values, shape)
398            >>> print(coo_tensor.itemsize)
399            8
400        """
401        return self.values.itemsize
402
403    @property
404    def ndim(self) -> int:
405        """
406        Return the number of tensor dimensions.
407
408        Examples:
409            >>> import mindspore as ms
410            >>> from mindspore import Tensor, COOTensor
411            >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
412            >>> values = Tensor([1, 2], dtype=ms.float32)
413            >>> coo_tensor = COOTensor(indices, values, (3, 4))
414            >>> print(coo_tensor.ndim)
415            2
416        """
417        return len(self.shape)
418
419    def coalesce(self) -> COOTensor:
420        """
421        Returns a coalesced copy of an uncoalesced sparse tensor.
422
423        Returns:
424            A COOTensor.
425
426        Supported Platforms:
427            ``GPU``
428
429        Examples:
430            >>> import mindspore as ms
431            >>> from mindspore import Tensor, COOTensor
432            >>> x_indices = Tensor([[0, 0, 1], [1, 1, 2]], dtype=ms.int64)
433            >>> x_values = Tensor([1, 5, 4], dtype=ms.float32)
434            >>> x_shape = (3, 3)
435            >>> coo_tensor = COOTensor(x_indices.transpose(), x_values, x_shape)
436            >>> res = coo_tensor.coalesce()
437            >>> print(res)
438            COOTensor(shape=[3, 3], dtype=Float32, indices=Tensor(shape=[2, 2], dtype=Int64,
439                value=[[0 1] [1 2]]), values=Tensor(shape=[2], dtype=Float32, value=[6.00000000e+00 4.00000000e+00]))
440        """
441        shape = Tensor(self.shape)
442        res_indices, res_values, _ = tensor_operator_registry.get("coalesce")(self.indices.transpose(),
443                                                                              self.values, shape)
444        return COOTensor(res_indices.transpose(), res_values, self.shape)
445
446    def to_csr(self) -> CSRTensor:
447        """
448        Converts COOTensor to CSRTensor.
449
450        Note:
451            Currently only supports CPU backend with LLVM 12.0.1 installed.
452
453        Returns:
454            CSRTensor.
455
456        Supported Platforms:
457            ``GPU`` ``CPU``
458
459        Examples:
460            >>> import mindspore as ms
461            >>> from mindspore import Tensor, COOTensor
462            >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
463            >>> values = Tensor([1, 2], dtype=ms.int32)
464            >>> shape = (3, 4)
465            >>> coo_tensor = COOTensor(indices, values, shape)
466            >>> print(coo_tensor.to_csr())
467            CSRTensor(shape=[3, 4], dtype=Int32, indptr=Tensor(shape=[4], dtype=Int32, value=[0 1 2 2]),
468                indices=Tensor(shape=[2], dtype=Int32, value=[1 2]), values=Tensor(shape=[2], dtype=Int32, value=[1 2]))
469        """
470        row_indices = self.indices[:, 0]
471        col_indices = self.indices[:, 1]
472        idx_dtype = self.indices.dtype
473        row_indices, sort_idx = tensor_operator_registry.get("sort")(
474            row_indices.astype(mstype.float32))
475        row_indices = row_indices.astype(idx_dtype)
476        col_indices = col_indices[sort_idx]
477        values = self.values[sort_idx]
478        indptr = tensor_operator_registry.get("coo2csr")(row_indices, self.shape[0])
479        return CSRTensor(indptr, col_indices, values, self.shape)
480
481    def to_dense(self) -> Tensor:
482        """
483        Converts COOTensor to Dense Tensor.
484
485        Returns:
486            Tensor.
487
488        Supported Platforms:
489            ``GPU``
490
491        Examples:
492            >>> import mindspore as ms
493            >>> from mindspore import Tensor, COOTensor
494            >>> indices = Tensor([[0, 1, 2], [1, 0, 2]], dtype=ms.int32)
495            >>> values = Tensor([1, 5, 4], dtype=ms.float32)
496            >>> shape = (3, 3)
497            >>> coo_tensor = COOTensor(indices.transpose(), values, shape)
498            >>> print(coo_tensor.to_dense())
499            [[0. 1. 0.]
500             [5. 0. 0.]
501             [0. 0. 4.]]
502        """
503        zeros_tensor = tensor_operator_registry.get("zeros")(self.shape, self.values.dtype)
504        return tensor_operator_registry.get("tensor_scatter_add")(
505            zeros_tensor, self.indices, self.values)
506
507    def astype(self, dtype: mstype) -> COOTensor:
508        """
509        Return a copy of the COOTensor, cast its values to a specified type.
510
511        Args:
512            dtype (Union[:class:`mindspore.dtype`, numpy.dtype, str]): Designated tensor dtype.
513
514        Returns:
515            COOTensor.
516
517        Supported Platforms:
518            ``Ascend`` ``GPU`` ``CPU``
519
520        Examples:
521            >>> import mindspore as ms
522            >>> from mindspore import Tensor, COOTensor
523            >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
524            >>> values = Tensor([1, 2], dtype=ms.float32)
525            >>> shape = (3, 4)
526            >>> coo_tensor = COOTensor(indices, values, shape)
527            >>> print(coo_tensor.astype(ms.float64).dtype)
528            Float64
529        """
530        data = self.values.astype(dtype)
531        return COOTensor(self.indices, data, self.shape)
532
533    def to_tuple(self) -> Tuple[Tensor, Tensor, Tuple[int, ...]]:
534        """
535        Return indices, values and shape as a tuple.
536
537        Returns:
538            Tuple.
539
540        Supported Platforms:
541            ``Ascend`` ``GPU`` ``CPU``
542
543        Examples:
544            >>> import mindspore as ms
545            >>> from mindspore import Tensor, COOTensor
546            >>> indices = Tensor([[0, 1], [1, 2]], dtype=ms.int32)
547            >>> values = Tensor([1, 2], dtype=ms.float32)
548            >>> shape = (3, 4)
549            >>> coo_tensor = COOTensor(indices, values, shape)
550            >>> print(coo_tensor.to_tuple())
551            (Tensor(shape=[2, 2], dtype=Int32, value=
552            [[0, 1],
553             [1, 2]]), Tensor(shape=[2], dtype=Float32, value= [ 1.00000000e+00,  2.00000000e+00]), (3, 4))
554        """
555        return self.indices, self.values, self.shape
556
557    def abs(self) -> COOTensor:
558        """
559        Return absolute value element-wisely.
560
561        Returns:
562            COOTensor.
563
564        Supported Platforms:
565            ``Ascend`` ``GPU`` ``CPU``
566
567        Examples:
568            >>> import mindspore as ms
569            >>> from mindspore import Tensor, COOTensor
570            >>> indices = Tensor([[0, 1, 2], [1, 0, 2]], dtype=ms.int32)
571            >>> values = Tensor([1, -5, -4], dtype=ms.float32)
572            >>> shape = (3, 3)
573            >>> coo_tensor = COOTensor(indices.transpose(), values, shape)
574            >>> res = coo_tensor.abs()
575            >>> print(res.values)
576            [1. 5. 4.]
577        """
578        data = self.values.abs()
579        return COOTensor(self.indices, data, self.shape)
580
581    def add(self, other: COOTensor, thresh: Tensor) -> COOTensor:
582        """
583        Return the sum with another COOTensor.
584
585        Args:
586            other(COOTensor): the second SparseTensor to sum.
587            thresh(Tensor): A 0-D Tensor, represents the magnitude threshold that determines
588                if an output value/index pair take space, Its dtype
589                should match that of the values if they are real. If output's
590                value is less than the `thresh`, it will vanish.
591
592        Returns:
593            COOTensor, representing the sum.
594
595        Raises:
596            ValueError: If any input(self/other)'s indices's dim is not equal to 2.
597            ValueError: If any input(self/other)'s values's dim is not equal to 1.
598            ValueError: If any input(self/other)'s shape's dim is not equal to 1.
599            ValueError: If thresh's dim is not equal to 0.
600            TypeError: If any input(self/other)'s indices's type is not equal to int64.
601            TypeError: If any input(self/other)'s shape's type is not equal to int64.
602            ValueError: If any input(self/other)'s indices's length is not equal to
603                its values's length.
604            TypeError: If any input(self/other)'s values's type is not equal to anf of
605                (int8/int16/int32/int64/float32/float64/complex64/complex128)
606            TypeError: If thresh's type is not equal to anf of
607                (int8/int16/int32/int64/float32/float64)
608            TypeError: If self's indices's type is not equal to other's indices's type
609            TypeError: If self's values's type is not equal to other's values's type
610            TypeError: If self's shape's type is not equal to other's shape's type
611            TypeError: If (self/other)'s value's type is not matched with thresh's type
612
613        Supported Platforms:
614            ``GPU`` ``CPU``
615
616        Examples:
617            >>> from mindspore import Tensor, COOTensor
618            >>> from mindspore import dtype as mstype
619            >>> indics0 = Tensor([[0, 1], [1, 2]], dtype=mstype.int64)
620            >>> values0 = Tensor([1, 2], dtype=mstype.int32)
621            >>> shape0 = (3, 4)
622            >>> input0 = COOTensor(indics0, values0, shape0)
623            >>> indics1 = Tensor([[0, 0], [1, 1]], dtype=mstype.int64)
624            >>> values1 = Tensor([3, 4], dtype=mstype.int32)
625            >>> shape1 = (3, 4)
626            >>> input1 = COOTensor(indics1, values1, shape1)
627            >>> thres = Tensor(0, dtype=mstype.int32)
628            >>> out = input0.add(input1, thres)
629            >>> print(out)
630            COOTensor(shape=[3, 4], dtype=Int32, indices=Tensor(shape=[4, 2], dtype=Int64, value=
631            [[0 0]
632             [0 1]
633             [1 1]
634             [1 2]]), values=Tensor(shape=[4], dtype=Int32, value=[3 1 4 2]))
635        """
636        return tensor_operator_registry.get('coo_add')(self, other, thresh)
637
638
639class CSRTensor(CSRTensor_):
640    r"""
641    Constructs a sparse tensor in CSR (Compressed Sparse Row) format, with specified
642    values indicated by `values` and row and column positions indicated by `indptr`
643    and `indices`.
644
645    For example, if indptr is [0, 2, 5, 6], indices is [0, 3, 1, 2, 4, 2], values is
646    [1., 2., 3., 4., 5., 6.], shape is (3, 5), then the dense representation of the sparse tensor will be:
647
648    .. code-block::
649        [[1., 0., 0., 2., 0.],
650         [0., 3., 4., 0., 5.],
651         [0., 0., 6., 0., 0.]]
652
653    The length of `indptr` should equal to `shape[0]+1`, where the elements should be equal or monotonically
654    increasing and the maximum value should be equal to the number of non-zero values in the tensor. The length
655    of `indices` and `values` should be equal to the number of non-zero values in the tensor. To be concrete, get
656    the query indices of none-zero elements in every line according to `indptr`. Then get the column positions of
657    none-zero elements in every line by looking up query indices in `indices`. Finally, get the actual values of
658    none-zero elements in every line by looking up query indices in `values`. In the former example, 'indptr' of
659    [0, 2, 5, 6] represents that the indices of 0th row of the tensor origins from [0, 2), the indices of
660    the 1st row of the tensor origins from [2, 5) and the 2nd row of the tensor origins from [5, 6). For example,
661    the column positions of the non-zero elements of the 0th row in the tensor are provided by the [0, 2) elements in
662    `indices` (i.e. [0, 3]) and the corresponding values are provided by the [0, 2) elements in `values`
663    (i.e. [1., 2.]). The column positions of the non-zero elements of the 1st row in the tensor are provided by the
664    [2, 5) elements in `indices` (i.e. [1, 2, 4]) and the corresponding values are provided by the [2, 5) elements in
665    `values` (i.e. [3., 4., 5.]). The column positions of the non-zero elements of the 2nd row in the tensor are
666    provided by the [5, 6) elements in `indices` (i.e. [2]) and the corresponding values are provided by the [5, 6)
667    elements in `values` (i.e. [6.]).
668
669    Common arithmetic operations include: addition (+), subtraction (-), multiplication (*),
670    and division (/). For details about operations supported by `CSRTensor`, see
671    `operators <https://www.mindspore.cn/docs/en/master/note/static_graph_syntax_support.html#operators>`_.
672
673    .. warning::
674        - This is an experimental API that is subjected to change.
675        - If the values given by `indptr` or `indices` are invalid, the results may be undefined. Invalid values include
676          when the length of `values` or `indices` exceeds the range indicated by `indptr`, and when the columns
677          indicated by `indices` are repeated on the same row.
678
679    Args:
680        indptr (Tensor): 1-D Tensor of shape :math:`(M)`, which equals to `shape[0] + 1`, which indicates the
681            start and end point for `values` in each row. Default: ``None``. If provided,
682            must be int16, int32 or int64.
683        indices (Tensor): 1-D Tensor of shape :math:`(N)`, which has the same length as `values`. `indices`
684            indicates the which column `values` should be placed. Default: ``None``. If provided,
685            must be int16, int32 or int64.
686        values (Tensor): Tensor, which has the same length as `indices` (values.shape[0] == indices.shape[0]).
687            `values`  stores the data for CSRTensor. Default: ``None``.
688        shape (tuple(int)): An integer tuple of shape :math:`(ndims)`, and `shape[0]` must equal to `M - 1`,
689            which all equal to number of rows of the CSRTensor. Default: ``None``.
690        csr_tensor (CSRTensor): A CSRTensor object.  Values' feature dimension should match with
691            CSRTensor's feature dimension :math:`(values.shape[1:] == csr\_tensor.shape[2:])` . Default: ``None``.
692
693    Outputs:
694        CSRTensor, with shape defined by `shape`, and dtype inferred from `value`.
695
696    Examples:
697        >>> import mindspore as ms
698        >>> from mindspore import Tensor, CSRTensor
699        >>> # initialize a csr_tensor with indptr, indices, values and shape
700        >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
701        >>> indices = Tensor([0, 1], dtype=ms.int32)
702        >>> values = Tensor([1, 2], dtype=ms.float32)
703        >>> shape = (2, 4)
704        >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
705        >>> # access a data member of CSRTensor
706        >>> print(indptr == csr_tensor.indptr)
707        [ True  True  True]
708    """
709
710    def __init__(self, indptr=None, indices=None, values=None, shape=None, csr_tensor=None):
711        "Init CSRTensor"
712        self.init_finished = False
713        # Directly init a CSRTensor from another CSRTensor
714        if csr_tensor is not None:
715            if not isinstance(csr_tensor, (CSRTensor, CSRTensor_)):
716                raise TypeError(f"Expect input `csr_tensor` to be a CSRTensor, but got {type(csr_tensor)}")
717            if not (indptr is None and indices is None and values is None and shape is None):
718                raise TypeError(
719                    "If input `csr_tensor` is provided, `indptr`, `indices`, `values`, `shapes` should all be `None`")
720            CSRTensor_.__init__(self, csr_tensor)
721        # Init a CSRTensor from indptr, indices, values and shape
722        else:
723            validator.check_csr_tensor_input(indptr, indices, values, shape)
724            validator.check_csr_tensor_shape(indptr.shape, indices.shape, values.shape, shape)
725            validator.check_csr_tensor_dtype(indptr.dtype, indices.dtype)
726            indptr = tensor_operator_registry.get('stop_gradient')(indptr)
727            indices = tensor_operator_registry.get('stop_gradient')(indices)
728            if is_stub_tensor(indptr):
729                indptr = indptr.stub_sync()
730            if is_stub_tensor(values):
731                values = values.stub_sync()
732            if is_stub_tensor(indices):
733                indices = indices.stub_sync()
734            CSRTensor_.__init__(self, indptr, indices, values, shape)
735        self.init_finished = True
736
737    def __repr__(self):
738        """Avoid PyTest Segfault when CSRTensor is not initialized."""
739        if self.init_finished:
740            return CSRTensor_.__repr__(self)
741        return ''
742
743    def __mul__(self, other):
744        return tensor_operator_registry.get('csr_mul')(self, other)
745
746    def __div__(self, other):
747        logger.warning("For CSR divide, zero values in the dense tensor are ignored.")
748        return tensor_operator_registry.get('csr_div')(self, other)
749
750    def __truediv__(self, other):
751        return self.__div__(other)
752
753    def __neg__(self):
754        return CSRTensor(self.indptr, self.indices, -self.values, self.shape)
755
756    def __add__(self, other):
757        if not self.shape == other.shape:
758            raise ValueError("Input tensors should have the same shape.")
759        if isinstance(other, CSRTensor):
760            return tensor_operator_registry.get('csr_add')(
761                self, other, Tensor(1, self.values.dtype), Tensor(1, self.values.dtype))
762        raise TypeError("CSRTensor add with %s is not supported." % type(other))
763
764    def __sub__(self, other):
765        if not self.shape == other.shape:
766            raise ValueError("Input tensors should have the same shape.")
767        if isinstance(other, CSRTensor):
768            return tensor_operator_registry.get('csr_add')(
769                self, other, Tensor(1, self.values.dtype), Tensor(-1, self.values.dtype))
770        raise TypeError("CSRTensor subtract with %s is not supported." % type(other))
771
772    @property
773    def indptr(self) -> Tensor:
774        """Return CSRTensor's row indices pointers."""
775        return Tensor(self._indptr)
776
777    @property
778    def indices(self) -> Tensor:
779        """
780        Return CSRTensor's column indices.
781
782        Examples:
783            >>> import mindspore as ms
784            >>> from mindspore import Tensor, CSRTensor
785            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
786            >>> indices = Tensor([0, 1], dtype=ms.int32)
787            >>> values = Tensor([1, 2], dtype=ms.float32)
788            >>> shape = (2, 4)
789            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
790            >>> print(csr_tensor.indices)
791            [0 1]
792        """
793        return Tensor(self._indices)
794
795    @property
796    def values(self) -> Tensor:
797        """
798        Return CSRTensor's non-zero values.
799
800        Examples:
801            >>> import mindspore as ms
802            >>> from mindspore import Tensor, CSRTensor
803            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
804            >>> indices = Tensor([0, 1], dtype=ms.int32)
805            >>> values = Tensor([1, 2], dtype=ms.float32)
806            >>> shape = (2, 4)
807            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
808            >>> print(csr_tensor.values)
809            [1. 2.]
810        """
811        return Tensor(self._values)
812
813    @property
814    def shape(self) -> Tuple[int, ...]:
815        """
816        Return CSRTensor's shape.
817
818        Examples:
819            >>> import mindspore as ms
820            >>> from mindspore import Tensor, CSRTensor
821            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
822            >>> indices = Tensor([0, 1], dtype=ms.int32)
823            >>> values = Tensor([1, 2], dtype=ms.float32)
824            >>> shape = (2, 4)
825            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
826            >>> print(csr_tensor.shape)
827            (2, 4)
828        """
829        return self._shape
830
831    @property
832    def dtype(self) -> mstype:
833        """
834        Return the dtype of the values of CSRTensor (:class:`mindspore.dtype`).
835
836        Examples:
837            >>> import mindspore as ms
838            >>> from mindspore import Tensor, CSRTensor
839            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
840            >>> indices = Tensor([0, 1], dtype=ms.int32)
841            >>> values = Tensor([1, 2], dtype=ms.float32)
842            >>> shape = (2, 4)
843            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
844            >>> print(csr_tensor.dtype)
845            Float32
846        """
847        return self._dtype
848
849    @property
850    def size(self) -> int:
851        """
852        Return the number of non-zero values.
853
854        Examples:
855            >>> import mindspore as ms
856            >>> from mindspore import Tensor, CSRTensor
857            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
858            >>> indices = Tensor([0, 1], dtype=ms.int32)
859            >>> values = Tensor([1, 2], dtype=ms.float32)
860            >>> shape = (2, 4)
861            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
862            >>> print(csr_tensor.size)
863            2
864        """
865        return self.values.size
866
867    @property
868    def itemsize(self) -> int:
869        """
870        Return the length of one tensor element in bytes.
871
872        Examples:
873            >>> import mindspore as ms
874            >>> from mindspore import Tensor, CSRTensor
875            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
876            >>> indices = Tensor([0, 1], dtype=ms.int32)
877            >>> values = Tensor([1, 2], dtype=ms.float64)
878            >>> shape = (2, 4)
879            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
880            >>> print(csr_tensor.itemsize)
881            8
882        """
883        return self.values.itemsize
884
885    @property
886    def ndim(self) -> int:
887        """
888        Return the number of tensor dimensions.
889
890        Examples:
891            >>> import mindspore as ms
892            >>> from mindspore import Tensor, CSRTensor
893            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
894            >>> indices = Tensor([0, 1], dtype=ms.int32)
895            >>> values = Tensor([1, 2], dtype=ms.float32)
896            >>> shape = (2, 4)
897            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
898            >>> print(csr_tensor.ndim)
899            2
900        """
901        return len(self.shape)
902
903    def to_tuple(self) -> Tuple[Tensor, Tensor, Tensor, Tuple[int, ...]]:
904        """
905        Return indptr, indices, values and shape as a tuple.
906
907        Returns:
908            Tuple.
909
910        Supported Platforms:
911            ``Ascend`` ``GPU`` ``CPU``
912
913        Examples:
914            >>> import mindspore as ms
915            >>> from mindspore import Tensor, CSRTensor
916            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
917            >>> indices = Tensor([0, 1], dtype=ms.int32)
918            >>> values = Tensor([1, 2], dtype=ms.float32)
919            >>> shape = (2, 4)
920            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
921            >>> print(csr_tensor.to_tuple())
922            (Tensor(shape=[3], dtype=Int32, value= [0, 1, 2]), Tensor(shape=[2], dtype=Int32, value= [0, 1]),
923                Tensor(shape=[2], dtype=Float32, value= [ 1.00000000e+00,  2.00000000e+00]), (2, 4))
924
925        """
926        return self.indptr, self.indices, self.values, self.shape
927
928    def to_coo(self) -> COOTensor:
929        """
930        Converts CSRTensor to COOTensor.
931
932        Note:
933            Currently only supports CPU backend with LLVM 12.0.1 installed.
934
935        Returns:
936            COOTensor.
937
938        Supported Platforms:
939            ``GPU`` ``CPU``
940
941        Examples:
942            >>> import mindspore as ms
943            >>> from mindspore import Tensor, CSRTensor
944            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
945            >>> indices = Tensor([0, 1], dtype=ms.int32)
946            >>> values = Tensor([1, 2], dtype=ms.int32)
947            >>> shape = (2, 4)
948            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
949            >>> print(csr_tensor.to_coo())
950            COOTensor(shape=[2, 4], dtype=Int32, indices=Tensor(shape=[2, 2], dtype=Int32, value=
951            [[0 0]
952             [1 1]]), values=Tensor(shape=[2], dtype=Int32, value=[1 2]))
953        """
954        if self.ndim != 2:
955            raise ValueError("Currently only support 2-D CSRTensor when converting to COOTensor.")
956        row_indices = tensor_operator_registry.get("csr2coo")(self.indptr, self.values.shape[0])
957        coo_indices = tensor_operator_registry.get("stack")((row_indices, self.indices), 1)
958        return COOTensor(coo_indices, self.values, self.shape)
959
960    def to_dense(self) -> Tensor:
961        """
962        Converts CSRTensor to Dense Tensor.
963
964        Returns:
965            Tensor.
966
967        Supported Platforms:
968            ``GPU``
969
970        Examples:
971            >>> import mindspore as ms
972            >>> from mindspore import Tensor, CSRTensor
973            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
974            >>> indices = Tensor([0, 1], dtype=ms.int32)
975            >>> values = Tensor([1, 2], dtype=ms.float32)
976            >>> shape = (2, 4)
977            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
978            >>> print(csr_tensor.to_dense())
979            [[1. 0. 0. 0.]
980             [0. 2. 0. 0.]]
981        """
982        return tensor_operator_registry.get("csr_to_dense")(self)
983
984    def astype(self, dtype: mstype) -> CSRTensor:
985        """
986        Return a copy of the CSRTensor, cast its values to a specified type.
987
988        Args:
989            dtype (Union[:class:`mindspore.dtype`, numpy.dtype, str]): Designated tensor dtype.
990
991        Returns:
992            CSRTensor.
993
994        Supported Platforms:
995            ``Ascend`` ``GPU`` ``CPU``
996
997        Examples:
998            >>> import mindspore as ms
999            >>> from mindspore import Tensor, CSRTensor
1000            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
1001            >>> indices = Tensor([0, 1], dtype=ms.int32)
1002            >>> values = Tensor([1, 2], dtype=ms.float32)
1003            >>> shape = (2, 4)
1004            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
1005            >>> print(csr_tensor.astype(ms.float64).dtype)
1006            Float64
1007        """
1008        data = self.values.astype(dtype)
1009        return CSRTensor(self.indptr, self.indices, data, self.shape)
1010
1011    def mv(self, dense_vector: Tensor) -> Tensor:
1012        """
1013        Return the matrix multiplication result of the right-multiply dense matrix of the CSRTensor.
1014        The CSRTensor with shape `[M, N]` needs to adapt the dense vector with shape `[N, 1]`
1015        to get the dense vector with result `[M, 1]`.
1016
1017        Note:
1018            Currently only supports CPU backend with LLVM 12.0.1 installed.
1019
1020        Args:
1021            dense_vector (Tensor): A dense Tensor, its shape must be (csr_tensor.shape[1], 1)
1022
1023        Returns:
1024            Tensor.
1025
1026        Supported Platforms:
1027            ``GPU`` ``CPU``
1028
1029        Examples:
1030            >>> from mindspore import Tensor, CSRTensor
1031            >>> from mindspore import dtype as mstype
1032            >>> indptr = Tensor([0, 1, 2], dtype=mstype.int32)
1033            >>> indices = Tensor([0, 1], dtype=mstype.int32)
1034            >>> values = Tensor([2, 1], dtype=mstype.float32)
1035            >>> dense_shape = (2, 4)
1036            >>> csr_tensor = CSRTensor(indptr, indices, values, dense_shape)
1037            >>> dense = Tensor([[1], [1], [1], [1]], dtype=mstype.float32)
1038            >>> print(csr_tensor.mv(dense))
1039            [[2.]
1040            [1.]]
1041        """
1042        validator.check_value_type('dense_vector', dense_vector, (Tensor, Tensor_,), 'CSRTensor.mv')
1043        return tensor_operator_registry.get("csr_mv")(self, dense_vector)
1044
1045    def mm(self, matrix: Union[Tensor, CSRTensor]) -> Union[Tensor, CSRTensor]:
1046        """
1047        Return the matrix multiplication result of the right-multiply matrix(dense or CSRTensor) of the CSRTensor.
1048        The CSRTensor with shape `[M, N]` needs to adapt the right matrix with shape `[N, K]`
1049        to get the dense matrix or CSRTensor with result `[M, K]`.
1050
1051        Note:
1052            If right matrix is CSRTensor, currently only supports GPU backend.
1053            If right matrix is Tensor, currently supports CPU backend with LLVM no lower than 12.0.1, and GPU backend.
1054
1055        Args:
1056            matrix (Tensor or CSRTensor): A dense Tensor or CSRTensor,
1057                its shape[0] should be equal to csr_tensor.shape[1]
1058
1059        Returns:
1060            Tensor or CSRTensor.
1061
1062        Supported Platforms:
1063            ``GPU`` ``CPU``
1064
1065        Examples:
1066            >>> from mindspore import Tensor, CSRTensor
1067            >>> from mindspore import dtype as mstype
1068            >>> indptr = Tensor([0, 1, 2], dtype=mstype.int32)
1069            >>> indices = Tensor([0, 1], dtype=mstype.int32)
1070            >>> values = Tensor([2, 1], dtype=mstype.float32)
1071            >>> dense_shape = (2, 4)
1072            >>> csr_tensor = CSRTensor(indptr, indices, values, dense_shape)
1073            >>> dense_matrix = Tensor([[1., 2.], [1, 2.], [1, 2.], [1., 2.]], dtype=mstype.float32)
1074            >>> print(csr_tensor.mm(dense_matrix))
1075            [[2. 4.]
1076            [1. 2.]]
1077        """
1078        if isinstance(matrix, CSRTensor):
1079            return tensor_operator_registry.get("csr_mm")(self, matrix)
1080        validator.check_value_type('matrix', matrix, (Tensor, Tensor_,), 'CSRTensor.mm')
1081        return tensor_operator_registry.get("csr_mm_akg")()(self.indptr, self.indices, self.values,
1082                                                            self.shape, matrix)
1083
1084    def sum(self, axis: int) -> Tensor:
1085        """
1086        Reduces a dimension of a CSRTensor by summing all elements in the dimension.
1087
1088        Note:
1089            Currently only supports CPU backend with LLVM 12.0.1 installed.
1090
1091        Args:
1092            axis (int): The dimensions to reduce.
1093
1094        Returns:
1095            Tensor, the dtype is the same as `CSRTensor.values`.
1096
1097        Supported Platforms:
1098            ``GPU`` ``CPU``
1099
1100        Examples:
1101            >>> from mindspore import Tensor, CSRTensor
1102            >>> from mindspore import dtype as mstype
1103            >>> indptr = Tensor([0, 1, 2], dtype=mstype.int32)
1104            >>> indices = Tensor([0, 1], dtype=mstype.int32)
1105            >>> values = Tensor([2, 1], dtype=mstype.float32)
1106            >>> dense_shape = (2, 4)
1107            >>> csr_tensor = CSRTensor(indptr, indices, values, dense_shape)
1108            >>> print(csr_tensor.sum(1))
1109            [[2.]
1110            [1.]]
1111        """
1112        return tensor_operator_registry.get("csr_reduce_sum")(self, axis)
1113
1114    def abs(self) -> CSRTensor:
1115        """
1116        Return absolute value element-wisely.
1117
1118        Returns:
1119            CSRTensor, with all values being non-negative.
1120
1121        Supported Platforms:
1122            ``Ascend`` ``GPU`` ``CPU``
1123
1124        Examples:
1125            >>> import mindspore as ms
1126            >>> from mindspore import Tensor, CSRTensor
1127            >>> indptr = Tensor([0, 1, 2], dtype=ms.int32)
1128            >>> indices = Tensor([0, 1], dtype=ms.int32)
1129            >>> values = Tensor([-1, -2], dtype=ms.float32)
1130            >>> shape = (2, 4)
1131            >>> csr_tensor = CSRTensor(indptr, indices, values, shape)
1132            >>> print(csr_tensor.abs().values)
1133            [1. 2.]
1134        """
1135        data = self.values.abs()
1136        return CSRTensor(self.indptr, self.indices, data, self.shape)
1137
1138    def add(self, b: CSRTensor, alpha: Tensor, beta: Tensor) -> CSRTensor:
1139        """
1140        Addition of two CSR Tensors : C = alpha * A + beta * B
1141
1142        Args:
1143            b (CSRTensor): Sparse CSR Tensor.
1144            alpha(Tensor): Dense Tensor, its shape must be able to broadcast to self.
1145            beta(Tensor): Dense Tensor, its shape must be able to broadcast to b.
1146
1147        Returns:
1148            CSRTensor.
1149
1150        Supported Platforms:
1151            ``GPU`` ``CPU``
1152
1153        Examples:
1154            >>> from mindspore import Tensor, CSRTensor
1155            >>> import mindspore.common.dtype as mstype
1156            >>> indptr = Tensor([0, 1, 2], dtype=mstype.int32)
1157            >>> indices = Tensor([0, 1], dtype=mstype.int32)
1158            >>> values_a = Tensor([2, 1], dtype=mstype.float32)
1159            >>> values_b = Tensor([1, 2], dtype=mstype.float32)
1160            >>> dense_shape = (2, 4)
1161            >>> alpha = Tensor(1, mstype.float32)
1162            >>> beta = Tensor(1, mstype.float32)
1163            >>> a = CSRTensor(indptr, indices, values_a, dense_shape)
1164            >>> b = CSRTensor(indptr, indices, values_b, dense_shape)
1165            >>> print(a.add(b, alpha, beta))
1166            CSRTensor(shape=[2, 4], dtype=Float32,
1167                      indptr=Tensor(shape=[3], dtype=Int32, value=[0 1 2]),
1168                      indices=Tensor(shape=[2], dtype=Int32, value=[0 1]),
1169                      values=Tensor(shape=[2], dtype=Float32, value=[ 3.00000000e+00  3.00000000e+00]))
1170        """
1171        return tensor_operator_registry.get('csr_add')(self, b, alpha, beta)
1172