Files
2026-07-13 12:40:42 +08:00

549 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from typing import TYPE_CHECKING
from paddle import _C_ops
from paddle.base.framework import (
core,
dygraph_only,
in_dygraph_mode,
in_dynamic_or_pir_mode,
in_pir_mode,
)
from paddle.base.layer_helper import LayerHelper
if TYPE_CHECKING:
from paddle import Tensor
__all__ = []
_int_dtype_ = [
core.VarDesc.VarType.UINT8,
core.VarDesc.VarType.INT8,
core.VarDesc.VarType.INT16,
core.VarDesc.VarType.INT32,
core.VarDesc.VarType.INT64,
core.VarDesc.VarType.BOOL,
core.DataType.UINT8,
core.DataType.INT8,
core.DataType.INT16,
core.DataType.INT32,
core.DataType.INT64,
core.DataType.BOOL,
]
_pir_int_dtype_ = {
core.DataType.UINT8: 1,
core.DataType.INT8: 1,
core.DataType.INT16: 2,
core.DataType.INT32: 4,
core.DataType.INT64: 8,
core.DataType.BOOL: 1,
}
def matmul(x: Tensor, y: Tensor, name: str | None = None) -> Tensor:
"""
Applies matrix multiplication of two Tensors.
The supported input/output Tensor type are as follows:
Note:
x[SparseCsrTensor] @ y[SparseCsrTensor] -> out[SparseCsrTensor]
x[SparseCsrTensor] @ y[DenseTensor] -> out[DenseTensor]
x[SparseCooTensor] @ y[SparseCooTensor] -> out[SparseCooTensor]
x[SparseCooTensor] @ y[DenseTensor] -> out[DenseTensor]
It supports backward propagation.
Dimensions `x` and `y` must be >= 2D. Automatic broadcasting of Tensor is not supported.
the shape of `x` should be `[*, M, K]` , and the shape of `y` should be `[*, K, N]` , where `*`
is zero or more batch dimensions.
Args:
x (SparseTensor): The input tensor. It can be SparseCooTensor/SparseCsrTensor. The data type can be float32 or float64.
y (SparseTensor|DenseTensor): The input tensor. It can be SparseCooTensor/SparseCsrTensor/DenseTensor. The data type can be float32 or float64.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
SparseTensor|DenseTensor: Determined by `x` and `y` .
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> paddle.device.set_device('gpu')
>>> # csr @ dense -> dense
>>> crows = [0, 1, 2, 3]
>>> cols = [1, 2, 0]
>>> values = [1.0, 2.0, 3.0]
>>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, [3, 3])
>>> print(csr)
Tensor(shape=[3, 3], dtype=paddle.float32, place=Place(gpu:0), stop_gradient=True,
crows=[0, 1, 2, 3],
cols=[1, 2, 0],
values=[1., 2., 3.])
>>> dense = paddle.ones([3, 2])
>>> out = paddle.sparse.matmul(csr, dense)
>>> print(out)
Tensor(shape=[3, 2], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[[1., 1.],
[2., 2.],
[3., 3.]])
>>> # coo @ dense -> dense
>>> indices = [[0, 1, 2], [1, 2, 0]]
>>> values = [1.0, 2.0, 3.0]
>>> coo = paddle.sparse.sparse_coo_tensor(indices, values, [3, 3])
>>> print(coo)
Tensor(shape=[3, 3], dtype=paddle.float32, place=Place(gpu:0), stop_gradient=True,
indices=[[0, 1, 2],
[1, 2, 0]],
values=[1., 2., 3.])
>>> dense = paddle.ones([3, 2])
>>> out = paddle.sparse.matmul(coo, dense)
>>> print(out)
Tensor(shape=[3, 2], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[[1., 1.],
[2., 2.],
[3., 3.]])
"""
assert in_dynamic_or_pir_mode(), (
"Currently, Sparse API only support dynamic mode or pir mode."
)
return _C_ops.sparse_matmul(x, y)
def masked_matmul(
x: Tensor, y: Tensor, mask: Tensor, name: str | None = None
) -> Tensor:
"""
Applies matrix multiplication of two Dense Tensors.
The supported input/output Tensor layout are as follows:
Note:
x[DenseTensor] @ y[DenseTensor] * mask[SparseCooTensor] -> out[SparseCooTensor]
x[DenseTensor] @ y[DenseTensor] * mask[SparseCsrTensor] -> out[SparseCsrTensor]
It supports backward propagation.
Dimensions `x` and `y` must be >= 2D. Automatic broadcasting of Tensor is not supported.
the shape of `x` should be `[*, M, K]` , and the shape of `y` should be `[*, K, N]` , and the shape of `mask` should be `[*, M, N]` ,
where `*` is zero or more batch dimensions.
Args:
x (DenseTensor): The input tensor. It is DenseTensor. The data type can be float32 or float64.
y (DenseTensor): The input tensor. It is DenseTensor. The data type can be float32 or float64.
mask (SparseTensor): The mask tensor, which can be SparseCooTensor/SparseCsrTensor. It specify sparse coordinates. The data type can be float32 or float64.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
SparseTensor: SparseCooTensor or SparseCsrTensor, which is same with `mask` .
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> paddle.device.set_device('gpu')
>>> paddle.seed(100)
>>> # dense @ dense * csr_mask -> csr
>>> crows = [0, 2, 3, 5]
>>> cols = [1, 3, 2, 0, 1]
>>> values = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> dense_shape = [3, 4]
>>> mask = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape)
>>> print(mask)
Tensor(shape=[3, 4], dtype=paddle.float32, place=Place(gpu:0), stop_gradient=True,
crows=[0, 2, 3, 5],
cols=[1, 3, 2, 0, 1],
values=[1., 2., 3., 4., 5.])
>>> x = paddle.rand([3, 5])
>>> y = paddle.rand([5, 4])
>>> out = paddle.sparse.masked_matmul(x, y, mask)
>>> print(out)
Tensor(shape=[3, 4], dtype=paddle.float32, place=Place(gpu:0), stop_gradient=True,
crows=[0, 2, 3, 5],
cols=[1, 3, 2, 0, 1],
values=[0.98986477, 0.97800624, 1.14591956, 0.68561077, 0.94714981])
"""
assert in_dynamic_or_pir_mode(), (
"Currently, Sparse API only support dynamic mode or pir mode."
)
return _C_ops.sparse_masked_matmul(x, y, mask)
def mv(x: Tensor, vec: Tensor, name: str | None = None) -> Tensor:
"""
Applies matrix-vector product of Sparse Matrix 'x' and Dense vector 'vec' .
The supported input/output Tensor layout are as follows:
Note:
x[SparseCsrTensor] @ vec[DenseTensor] -> out[DenseTensor]
x[SparseCooTensor] @ vec[DenseTensor] -> out[DenseTensor]
It supports backward propagation.
The shape of `x` should be `[M, N]` , and the shape of `vec` should be `[N]` ,
and the shape of `out` will be `[M]` .
Args:
x (SparseTensor): The input 2D tensor. It must be SparseCooTensor/SparseCsrTensor. The data type can be float32 or float64.
vec (DenseTensor): The input 1D tensor. It must be DenseTensor vector. The data type can be float32 or float64.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
DenseTensor: 1D DenseTensor whose dtype is same with input.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> paddle.device.set_device('gpu')
>>> paddle.seed(100)
>>> # csr @ dense -> dense
>>> crows = [0, 2, 3, 5]
>>> cols = [1, 3, 2, 0, 1]
>>> values = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> dense_shape = [3, 4]
>>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape)
>>> print(csr)
Tensor(shape=[3, 4], dtype=paddle.float32, place=Place(gpu:0), stop_gradient=True,
crows=[0, 2, 3, 5],
cols=[1, 3, 2, 0, 1],
values=[1., 2., 3., 4., 5.])
>>> vec = paddle.randn([4])
>>> out = paddle.sparse.mv(csr, vec)
>>> print(out)
Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[-3.85499096, -2.42975140, -1.75087738])
"""
assert in_dynamic_or_pir_mode(), (
"Currently, Sparse API only support dynamic mode or pir mode."
)
return _C_ops.sparse_mv(x, vec)
def add(x: Tensor, y: Tensor, name: str | None = None) -> Tensor:
"""
Add two sparse tensors element-wise. Input x and y's shape should be identical and have same sparse
typeSparseCooTensor or SparseCsrTensor.If input is SparseCooTensor, x and y's sparse_dim should be identical.
The equation is:
.. math::
out = x + y
Args:
x (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
y (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
Tensor: the result tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.device.set_device("cpu")
>>> x = paddle.to_tensor([[0, -1, 0, 2], [0, 0, -3, 0], [4, 5, 0, 0]], 'float32')
>>> y = paddle.to_tensor([[0, 0, 0, -2], [0, 2, -3, 0], [2, 3, 4, 8]], 'float32')
>>> sparse_x = x.to_sparse_csr()
>>> sparse_y = y.to_sparse_csr()
>>> sparse_z = paddle.sparse.add(sparse_x, sparse_y)
>>> print(sparse_z.to_dense())
Tensor(shape=[3, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[ 0., -1., 0., 0.],
[ 0., 2., -6., 0.],
[ 6., 8., 4., 8.]])
"""
if in_dynamic_or_pir_mode():
return _C_ops.sparse_add(x, y)
else:
op_type = 'sparse_add'
inputs = {'x': x, 'y': y}
helper = LayerHelper(op_type)
out = helper.create_sparse_variable_for_type_inference(x.dtype)
helper.append_op(
type=op_type, inputs=inputs, outputs={'out': out}, attrs={}
)
return out
def subtract(x: Tensor, y: Tensor, name: str | None = None) -> Tensor:
"""
Subtract two sparse tensors element-wise. Input x and y's shape should be identical and have same sparse
typeSparseCooTensor or SparseCsrTensor.If input is SparseCooTensor, x and y's sparse_dim should be identical.
The equation is:
.. math::
out = x - y
Args:
x (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
y (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
Tensor: the result tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.device.set_device("cpu")
>>> x = paddle.to_tensor([[0, -1, 0, 2], [0, 0, -3, 0], [4, 5, 0, 0]], 'float32')
>>> y = paddle.to_tensor([[0, 0, 0, -2], [0, 2, -3, 0], [2, 3, 4, 8]], 'float32')
>>> sparse_x = x.to_sparse_csr()
>>> sparse_y = y.to_sparse_csr()
>>> sparse_z = paddle.sparse.subtract(sparse_x, sparse_y)
>>> print(sparse_z.to_dense())
Tensor(shape=[3, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[ 0., -1., 0., 4.],
[ 0., -2., 0., 0.],
[ 2., 2., -4., -8.]])
"""
if in_dygraph_mode():
return _C_ops.sparse_subtract(x, y)
elif in_pir_mode():
return _C_ops.sparse_subtract(x, y)
else:
raise RuntimeError(
"We currently only support dynamic graph mode or the new IR mode."
)
def multiply(x: Tensor, y: Tensor, name: str | None = None) -> Tensor:
"""
Multiply two sparse tensors element-wise. Input x and y's shape should be identical and have same sparse
typeSparseCooTensor or SparseCsrTensor.If input is SparseCooTensor, x and y's sparse_dim should be identical.
The equation is:
.. math::
out = x * y
Args:
x (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
y (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
Tensor: the result tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.device.set_device("cpu")
>>> x = paddle.to_tensor([[0, -1, 0, 2], [0, 0, -3, 0], [4, 5, 0, 0]], 'float32')
>>> y = paddle.to_tensor([[0, 0, 0, -2], [0, 2, -3, 0], [2, 3, 4, 8]], 'float32')
>>> sparse_x = x.to_sparse_csr()
>>> sparse_y = y.to_sparse_csr()
>>> sparse_z = paddle.sparse.multiply(sparse_x, sparse_y)
>>> print(sparse_z.to_dense())
Tensor(shape=[3, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[ 0., -0., 0., -4.],
[ 0., 0., 9., 0.],
[ 8., 15., 0., 0.]])
"""
if isinstance(y, (int, float)):
return _C_ops.sparse_scale(x, float(y), 0.0, True)
else:
if in_dygraph_mode():
return _C_ops.sparse_multiply(x, y)
elif in_pir_mode():
return _C_ops.sparse_multiply(x, y)
else:
raise RuntimeError(
"We currently only support dynamic graph mode or the new IR mode."
)
def divide(x: Tensor, y: Tensor, name: str | None = None) -> Tensor:
"""
Divide two sparse tensors element-wise. Input x and y's shape should be identical and have same sparse
typeSparseCooTensor or SparseCsrTensor.If input is SparseCooTensor, x and y's sparse_dim should be identical.
The equation is:
.. math::
out = x / y
Args:
x (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
y (Tensor): the input tensor, it's data type should be float32, float64, int32, int64, complex64, complex128.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
Tensor: the result tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.device.set_device("cpu")
>>> x = paddle.to_tensor([[0, -1, 0, 2], [0, 0, -3, 0], [4, 5, 0, 0]], 'float32')
>>> y = paddle.to_tensor([[0, 0, 0, -2], [0, 2, -3, 0], [2, 3, 4, 8]], 'float32')
>>> sparse_x = x.to_sparse_csr()
>>> sparse_y = y.to_sparse_csr()
>>> sparse_z = paddle.sparse.divide(sparse_x, sparse_y)
>>> print(sparse_z.to_dense())
Tensor(shape=[3, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[ nan , -inf. , nan , -1. ],
[ nan , 0. , 1. , nan ],
[ 2. , 1.66666663, 0. , 0. ]])
"""
if isinstance(y, (int, float)):
return _C_ops.sparse_divide_scalar(x, float(y))
else:
if in_dygraph_mode():
return _C_ops.sparse_divide(x, y)
elif in_pir_mode():
return _C_ops.sparse_divide(x, y)
else:
raise RuntimeError(
"We currently only support dynamic graph mode or the new IR mode."
)
def is_same_shape(x: Tensor, y: Tensor) -> bool:
"""
Return the results of shape comparison between two Tensors, check whether x.shape equal to y.shape.
Any two type Tensor among DenseTensor/SparseCooTensor/SparseCsrTensor are supported.
Args:
x (Tensor): The input tensor. It can be DenseTensor/SparseCooTensor/SparseCsrTensor.
y (Tensor): The input tensor. It can be DenseTensor/SparseCooTensor/SparseCsrTensor.
Returns:
bool: True for same shape and False for different shape.
Examples:
.. code-block:: pycon
>>> import paddle
>>> x = paddle.rand([2, 3, 8])
>>> y = paddle.rand([2, 3, 8])
>>> y = y.to_sparse_csr()
>>> z = paddle.rand([2, 5])
>>> paddle.sparse.is_same_shape(x, y)
True
>>> paddle.sparse.is_same_shape(x, z)
False
"""
assert in_dynamic_or_pir_mode(), (
"Currently, Sparse API only support dynamic mode or pir mode."
)
return x.is_same_shape(y)
@dygraph_only
def mask_as(x: Tensor, mask: Tensor, name: str | None = None) -> Tensor:
r"""
Filter the input dense tensor `x` using the `indices` of the sparse matrix `mask`,
which in turn generates a sparse matrix of the corresponding format.
The input `x` and `mask` must have the same shape, and the sparse tensor returned has the same indices as `mask`
even `zero` values exist in the corresponding indices.
Args:
x (Tensor): The input tensor. It should be a DenseTensor.
The data type can be float32, float64, int32, int64, complex64, complex128, int8, int16, float16.
mask (Tensor): The input tensor. It can be SparseCooTensor or SparseCsrTensor.
It should be 2D or 3D when the mask is SparseCsrTensor.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
Tensor: A sparse tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.set_device('cpu')
>>> # csr sparse tensor
>>> crows = [0, 2, 3, 5]
>>> cols = [1, 3, 2, 0, 1]
>>> values = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> dense_shape = [3, 4]
>>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape)
>>> paddle.seed(2024)
>>> x = paddle.rand(dense_shape).astype(csr.dtype)
>>> out = paddle.sparse.mask_as(x, csr)
>>> print(out)
Tensor(shape=[3, 4], dtype=paddle.float32, place=Place(cpu), stop_gradient=True,
crows=[0, 2, 3, 5],
cols=[1, 3, 2, 0, 1],
values=[0.23659813, 0.08467803, 0.64152628, 0.66596609, 0.90394485])
>>> # coo sparse tensor
>>> indices = [[0, 1, 2], [1, 2, 0]]
>>> values = [1.0, 2.0, 3.0]
>>> dense_shape = [3, 3]
>>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape)
>>> paddle.seed(2024)
>>> x = paddle.rand(dense_shape).astype(coo.dtype)
>>> out = paddle.sparse.mask_as(x, coo)
>>> print(out)
Tensor(shape=[3, 3], dtype=paddle.float32, place=Place(cpu), stop_gradient=True,
indices=[[0, 1, 2],
[1, 2, 0]],
values=[0.23659813, 0.40340215, 0.64152628])
"""
return _C_ops.sparse_mask_as(x, mask)