chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
@@ -0,0 +1,23 @@
# Copyright (c) 2021 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 .graph_khop_sampler import graph_khop_sampler # noqa: F401
from .graph_reindex import graph_reindex # noqa: F401
from .graph_sample_neighbors import graph_sample_neighbors # noqa: F401
from .graph_send_recv import graph_send_recv # noqa: F401
from .resnet_unit import ResNetUnit # noqa: F401
from .softmax_mask_fuse import softmax_mask_fuse # noqa: F401
from .softmax_mask_fuse_upper_triangle import ( # noqa: F401
softmax_mask_fuse_upper_triangle,
)
@@ -0,0 +1,216 @@
# 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, Literal, overload
from paddle import _C_ops
from paddle.base.data_feeder import check_variable_and_dtype
from paddle.base.layer_helper import LayerHelper
from paddle.framework import in_dynamic_or_pir_mode
if TYPE_CHECKING:
from paddle import Tensor
@overload
def graph_khop_sampler(
row: Tensor,
colptr: Tensor,
input_nodes: Tensor,
sample_sizes: list[int] | tuple[int, ...],
sorted_eids: Tensor | None = ...,
return_eids: Literal[True] = ...,
name: str | None = None,
) -> tuple[Tensor, Tensor, Tensor, Tensor]: ...
@overload
def graph_khop_sampler(
row: Tensor,
colptr: Tensor,
input_nodes: Tensor,
sample_sizes: list[int] | tuple[int, ...],
sorted_eids: Tensor | None = ...,
return_eids: Literal[False] = ...,
name: str | None = None,
) -> tuple[Tensor, Tensor, Tensor]: ...
@overload
def graph_khop_sampler(
row: Tensor,
colptr: Tensor,
input_nodes: Tensor,
sample_sizes: list[int] | tuple[int, ...],
sorted_eids: Tensor | None = ...,
return_eids: bool = ...,
name: str | None = None,
) -> tuple[Tensor, Tensor, Tensor, Tensor] | tuple[Tensor, Tensor, Tensor]: ...
def graph_khop_sampler(
row,
colptr,
input_nodes,
sample_sizes,
sorted_eids=None,
return_eids=False,
name=None,
):
"""
Graph Khop Sampler API.
This API is mainly used in Graph Learning domain, and the main purpose is to
provide high performance graph khop sampling method with subgraph reindex step.
For example, we get the CSC(Compressed Sparse Column) format of the input graph
edges as `row` and `colptr`, so as to convert graph data into a suitable format
for sampling. And the `input_nodes` means the nodes we need to sample neighbors,
and `sample_sizes` means the number of neighbors and number of layers we want
to sample.
Args:
row (Tensor): One of the components of the CSC format of the input graph, and
the shape should be [num_edges, 1] or [num_edges]. The available
data type is int32, int64.
colptr (Tensor): One of the components of the CSC format of the input graph,
and the shape should be [num_nodes + 1, 1] or [num_nodes].
The data type should be the same with `row`.
input_nodes (Tensor): The input nodes we need to sample neighbors for, and the
data type should be the same with `row`.
sample_sizes (list|tuple): The number of neighbors and number of layers we want
to sample. The data type should be int, and the shape
should only have one dimension.
sorted_eids (Tensor|None, optional): The sorted edge ids, should not be None when `return_eids`
is True. The shape should be [num_edges, 1], and the data
type should be the same with `row`. Default is None.
return_eids (bool, optional): Whether to return the id of the sample edges. Default is False.
name (str, optional): Name for the operation (optional, default is None).
For more information, please refer to :ref:`api_guide_Name`.
Returns:
- edge_src (Tensor), The src index of the output edges, also means the first column of
the edges. The shape is [num_sample_edges, 1] currently.
- edge_dst (Tensor), The dst index of the output edges, also means the second column
of the edges. The shape is [num_sample_edges, 1] currently.
- sample_index (Tensor), The original id of the input nodes and sampled neighbor nodes.
- reindex_nodes (Tensor), The reindex id of the input nodes.
- edge_eids (Tensor), Return the id of the sample edges if `return_eids` is True.
Examples:
.. code-block:: pycon
>>> # doctest: +SKIP('Number of sample edges mismatch, the sample kernel has error.')
>>> import paddle
>>> row = [3, 7, 0, 9, 1, 4, 2, 9, 3, 9, 1, 9, 7]
>>> colptr = [0, 2, 4, 5, 6, 7, 9, 11, 11, 13, 13]
>>> nodes = [0, 8, 1, 2]
>>> sample_sizes = [2, 2]
>>> row = paddle.to_tensor(row, dtype="int64")
>>> colptr = paddle.to_tensor(colptr, dtype="int64")
>>> nodes = paddle.to_tensor(nodes, dtype="int64")
>>> edge_src, edge_dst, sample_index, reindex_nodes = paddle.incubate.graph_khop_sampler( # type: ignore[operator]
... row, colptr, nodes, sample_sizes, False
... )
"""
if in_dynamic_or_pir_mode():
if return_eids:
if sorted_eids is None:
raise ValueError(
"`sorted_eid` should not be None if return_eids is True."
)
(
edge_src,
edge_dst,
sample_index,
reindex_nodes,
edge_eids,
) = _C_ops.graph_khop_sampler(
row,
colptr,
input_nodes,
sorted_eids,
sample_sizes,
True,
)
return edge_src, edge_dst, sample_index, reindex_nodes, edge_eids
else:
(
edge_src,
edge_dst,
sample_index,
reindex_nodes,
_,
) = _C_ops.graph_khop_sampler(
row,
colptr,
input_nodes,
None,
sample_sizes,
False,
)
return edge_src, edge_dst, sample_index, reindex_nodes
check_variable_and_dtype(
row, "Row", ("int32", "int64"), "graph_khop_sampler"
)
if return_eids:
if sorted_eids is None:
raise ValueError(
"`sorted_eid` should not be None if return_eids is True."
)
check_variable_and_dtype(
sorted_eids, "Eids", ("int32", "int64"), "graph_khop_sampler"
)
check_variable_and_dtype(
colptr, "Col_Ptr", ("int32", "int64"), "graph_khop_sampler"
)
check_variable_and_dtype(
input_nodes, "X", ("int32", "int64"), "graph_khop_sampler"
)
helper = LayerHelper("graph_khop_sampler", **locals())
edge_src = helper.create_variable_for_type_inference(dtype=row.dtype)
edge_dst = helper.create_variable_for_type_inference(dtype=row.dtype)
sample_index = helper.create_variable_for_type_inference(dtype=row.dtype)
reindex_nodes = helper.create_variable_for_type_inference(dtype=row.dtype)
edge_eids = helper.create_variable_for_type_inference(dtype=row.dtype)
helper.append_op(
type="graph_khop_sampler",
inputs={
"Row": row,
"Eids": sorted_eids,
"Col_Ptr": colptr,
"X": input_nodes,
},
outputs={
"Out_Src": edge_src,
"Out_Dst": edge_dst,
"Sample_Index": sample_index,
"Reindex_X": reindex_nodes,
"Out_Eids": edge_eids,
},
attrs={"sample_sizes": sample_sizes, "return_eids": return_eids},
)
if return_eids:
return edge_src, edge_dst, sample_index, reindex_nodes, edge_eids
else:
return edge_src, edge_dst, sample_index, reindex_nodes
@@ -0,0 +1,187 @@
# 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.data_feeder import check_variable_and_dtype
from paddle.base.layer_helper import LayerHelper
from paddle.framework import in_dynamic_or_pir_mode
from paddle.utils import deprecated
if TYPE_CHECKING:
from paddle import Tensor
@deprecated(
since="2.4.0",
update_to="paddle.geometric.reindex_graph",
level=1,
reason="paddle.incubate.graph_reindex will be removed in future",
)
def graph_reindex(
x: Tensor,
neighbors: Tensor,
count: Tensor,
value_buffer: Tensor | None = None,
index_buffer: Tensor | None = None,
flag_buffer_hashtable: bool = False,
name: str | None = None,
) -> tuple[Tensor, Tensor, Tensor]:
"""
Graph Reindex API.
This API is mainly used in Graph Learning domain, which should be used
in conjunction with `graph_sample_neighbors` API. And the main purpose
is to reindex the ids information of the input nodes, and return the
corresponding graph edges after reindex.
Notes:
The number in x should be unique, otherwise it would cause potential errors.
Besides, we also support multi-edge-types neighbors reindexing. If we have different
edge_type neighbors for x, we should concatenate all the neighbors and count of x.
We will reindex all the nodes from 0.
Take input nodes x = [0, 1, 2] as an example.
If we have neighbors = [8, 9, 0, 4, 7, 6, 7], and count = [2, 3, 2],
then we know that the neighbors of 0 is [8, 9], the neighbors of 1
is [0, 4, 7], and the neighbors of 2 is [6, 7].
Args:
x (Tensor): The input nodes which we sample neighbors for. The available
data type is int32, int64.
neighbors (Tensor): The neighbors of the input nodes `x`. The data type
should be the same with `x`.
count (Tensor): The neighbor count of the input nodes `x`. And the
data type should be int32.
value_buffer (Tensor, optional): Value buffer for hashtable. The data type should
be int32, and should be filled with -1. Default is None.
index_buffer (Tensor, optional): Index buffer for hashtable. The data type should
be int32, and should be filled with -1. Default is None.
flag_buffer_hashtable (bool, optional): Whether to use buffer for hashtable to speed up.
Default is False. Only useful for gpu version currently.
name (str, optional): Name for the operation (optional, default is None).
For more information, please refer to :ref:`api_guide_Name`.
Returns:
- reindex_src (Tensor), The source node index of graph edges after reindex.
- reindex_dst (Tensor), The destination node index of graph edges after reindex.
- out_nodes (Tensor), The index of unique input nodes and neighbors before reindex,
where we put the input nodes `x` in the front, and put neighbor
nodes in the back.
Examples:
.. code-block:: pycon
>>> import paddle
>>> x = [0, 1, 2]
>>> neighbors_e1 = [8, 9, 0, 4, 7, 6, 7]
>>> count_e1 = [2, 3, 2]
>>> x = paddle.to_tensor(x, dtype="int64")
>>> neighbors_e1 = paddle.to_tensor(neighbors_e1, dtype="int64")
>>> count_e1 = paddle.to_tensor(count_e1, dtype="int32")
>>> reindex_src, reindex_dst, out_nodes = paddle.incubate.graph_reindex( # type: ignore[operator]
... x,
... neighbors_e1,
... count_e1,
... )
>>> print(reindex_src)
Tensor(shape=[7], dtype=int64, place=Place(cpu), stop_gradient=True,
[3, 4, 0, 5, 6, 7, 6])
>>> print(reindex_dst)
Tensor(shape=[7], dtype=int64, place=Place(cpu), stop_gradient=True,
[0, 0, 1, 1, 1, 2, 2])
>>> print(out_nodes)
Tensor(shape=[8], dtype=int64, place=Place(cpu), stop_gradient=True,
[0, 1, 2, 8, 9, 4, 7, 6])
>>> neighbors_e2 = [0, 2, 3, 5, 1]
>>> count_e2 = [1, 3, 1]
>>> neighbors_e2 = paddle.to_tensor(neighbors_e2, dtype="int64")
>>> count_e2 = paddle.to_tensor(count_e2, dtype="int32")
>>> neighbors = paddle.concat([neighbors_e1, neighbors_e2])
>>> count = paddle.concat([count_e1, count_e2])
>>> reindex_src, reindex_dst, out_nodes = paddle.incubate.graph_reindex( # type: ignore[operator]
... x,
... neighbors,
... count,
... )
>>> print(reindex_src)
Tensor(shape=[12], dtype=int64, place=Place(cpu), stop_gradient=True,
[3, 4, 0, 5, 6, 7, 6, 0, 2, 8, 9, 1])
>>> print(reindex_dst)
Tensor(shape=[12], dtype=int64, place=Place(cpu), stop_gradient=True,
[0, 0, 1, 1, 1, 2, 2, 0, 1, 1, 1, 2])
>>> print(out_nodes)
Tensor(shape=[10], dtype=int64, place=Place(cpu), stop_gradient=True,
[0, 1, 2, 8, 9, 4, 7, 6, 3, 5])
"""
if flag_buffer_hashtable:
if value_buffer is None or index_buffer is None:
raise ValueError(
"`value_buffer` and `index_buffer` should not"
"be None if `flag_buffer_hashtable` is True."
)
if in_dynamic_or_pir_mode():
reindex_src, reindex_dst, out_nodes = _C_ops.reindex_graph(
x,
neighbors,
count,
value_buffer,
index_buffer,
)
return reindex_src, reindex_dst, out_nodes
check_variable_and_dtype(x, "X", ("int32", "int64"), "graph_reindex")
check_variable_and_dtype(
neighbors, "Neighbors", ("int32", "int64"), "graph_reindex"
)
check_variable_and_dtype(count, "Count", ("int32"), "graph_reindex")
if flag_buffer_hashtable:
check_variable_and_dtype(
value_buffer, "HashTable_Value", ("int32"), "graph_reindex"
)
check_variable_and_dtype(
index_buffer, "HashTable_Index", ("int32"), "graph_reindex"
)
helper = LayerHelper("graph_reindex", **locals())
reindex_src = helper.create_variable_for_type_inference(dtype=x.dtype)
reindex_dst = helper.create_variable_for_type_inference(dtype=x.dtype)
out_nodes = helper.create_variable_for_type_inference(dtype=x.dtype)
helper.append_op(
type="graph_reindex",
inputs={
"X": x,
"Neighbors": neighbors,
"Count": count,
"HashTable_Value": value_buffer if flag_buffer_hashtable else None,
"HashTable_Index": index_buffer if flag_buffer_hashtable else None,
},
outputs={
"Reindex_Src": reindex_src,
"Reindex_Dst": reindex_dst,
"Out_Nodes": out_nodes,
},
)
return reindex_src, reindex_dst, out_nodes
@@ -0,0 +1,229 @@
# 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, Literal, overload
from paddle import _C_ops
from paddle.base.data_feeder import check_variable_and_dtype
from paddle.base.layer_helper import LayerHelper
from paddle.framework import in_dynamic_or_pir_mode
from paddle.utils import deprecated
if TYPE_CHECKING:
from paddle import Tensor
@overload
def graph_sample_neighbors(
row: Tensor,
colptr: Tensor,
input_nodes: Tensor,
eids: Tensor | None = ...,
perm_buffer: Tensor | None = ...,
sample_size: int = ...,
return_eids: Literal[True] = ...,
flag_perm_buffer: bool = ...,
name: str | None = ...,
) -> tuple[Tensor, Tensor, Tensor]: ...
@overload
def graph_sample_neighbors(
row: Tensor,
colptr: Tensor,
input_nodes: Tensor,
eids: Tensor | None = ...,
perm_buffer: Tensor | None = ...,
sample_size: int = ...,
return_eids: Literal[False] = ...,
flag_perm_buffer: bool = ...,
name: str | None = ...,
) -> tuple[Tensor, Tensor]: ...
@overload
def graph_sample_neighbors(
row: Tensor,
colptr: Tensor,
input_nodes: Tensor,
eids: Tensor | None = ...,
perm_buffer: Tensor | None = ...,
sample_size: int = ...,
return_eids: bool = ...,
flag_perm_buffer: bool = ...,
name: str | None = ...,
) -> tuple[Tensor, Tensor] | tuple[Tensor, Tensor, Tensor]: ...
@deprecated(
since="2.4.0",
update_to="paddle.geometric.sample_neighbors",
level=1,
reason="paddle.incubate.graph_sample_neighbors will be removed in future",
)
def graph_sample_neighbors(
row,
colptr,
input_nodes,
eids=None,
perm_buffer=None,
sample_size=-1,
return_eids=False,
flag_perm_buffer=False,
name=None,
):
"""
Graph Sample Neighbors API.
This API is mainly used in Graph Learning domain, and the main purpose is to
provide high performance of graph sampling method. For example, we get the
CSC(Compressed Sparse Column) format of the input graph edges as `row` and
`colptr`, so as to convert graph data into a suitable format for sampling.
`input_nodes` means the nodes we need to sample neighbors, and `sample_sizes`
means the number of neighbors and number of layers we want to sample.
Besides, we support fisher-yates sampling in GPU version.
Args:
row (Tensor): One of the components of the CSC format of the input graph, and
the shape should be [num_edges, 1] or [num_edges]. The available
data type is int32, int64.
colptr (Tensor): One of the components of the CSC format of the input graph,
and the shape should be [num_nodes + 1, 1] or [num_nodes + 1].
The data type should be the same with `row`.
input_nodes (Tensor): The input nodes we need to sample neighbors for, and the
data type should be the same with `row`.
eids (Tensor): The eid information of the input graph. If return_eids is True,
then `eids` should not be None. The data type should be the
same with `row`. Default is None.
perm_buffer (Tensor): Permutation buffer for fisher-yates sampling. If `flag_perm_buffer`
is True, then `perm_buffer` should not be None. The data type should
be the same with `row`. Default is None.
sample_size (int): The number of neighbors we need to sample. Default value is
-1, which means returning all the neighbors of the input nodes.
return_eids (bool): Whether to return eid information of sample edges. Default is False.
flag_perm_buffer (bool): Using the permutation for fisher-yates sampling in GPU. Default
value is false, means not using it.
name (str, optional): Name for the operation (optional, default is None).
For more information, please refer to :ref:`api_guide_Name`.
Returns:
- out_neighbors (Tensor): The sample neighbors of the input nodes.
- out_count (Tensor): The number of sampling neighbors of each input node, and the shape should be the same with `input_nodes`.
- out_eids (Tensor): If `return_eids` is True, we will return the eid information of the sample edges.
Examples:
.. code-block:: pycon
>>> import paddle
>>> # edges: (3, 0), (7, 0), (0, 1), (9, 1), (1, 2), (4, 3), (2, 4),
>>> # (9, 5), (3, 5), (9, 6), (1, 6), (9, 8), (7, 8)
>>> row = [3, 7, 0, 9, 1, 4, 2, 9, 3, 9, 1, 9, 7]
>>> colptr = [0, 2, 4, 5, 6, 7, 9, 11, 11, 13, 13]
>>> nodes = [0, 8, 1, 2]
>>> sample_size = 2
>>> row = paddle.to_tensor(row, dtype="int64")
>>> colptr = paddle.to_tensor(colptr, dtype="int64")
>>> nodes = paddle.to_tensor(nodes, dtype="int64")
>>> out_neighbors, out_count = paddle.incubate.graph_sample_neighbors(
... row,
... colptr,
... nodes,
... sample_size=sample_size,
... ) # type: ignore[operator]
"""
if return_eids:
if eids is None:
raise ValueError(
"`eids` should not be None if `return_eids` is True."
)
if flag_perm_buffer:
if perm_buffer is None:
raise ValueError(
"`perm_buffer` should not be None if `flag_perm_buffer` is True."
)
if in_dynamic_or_pir_mode():
(
out_neighbors,
out_count,
out_eids,
) = _C_ops.graph_sample_neighbors(
row,
colptr,
input_nodes,
eids,
perm_buffer,
sample_size,
return_eids,
flag_perm_buffer,
)
if return_eids:
return out_neighbors, out_count, out_eids
return out_neighbors, out_count
check_variable_and_dtype(
row, "Row", ("int32", "int64"), "graph_sample_neighbors"
)
check_variable_and_dtype(
colptr, "Col_Ptr", ("int32", "int64"), "graph_sample_neighbors"
)
check_variable_and_dtype(
input_nodes, "X", ("int32", "int64"), "graph_sample_neighbors"
)
if return_eids:
check_variable_and_dtype(
eids, "Eids", ("int32", "int64"), "graph_sample_neighbors"
)
if flag_perm_buffer:
check_variable_and_dtype(
perm_buffer,
"Perm_Buffer",
("int32", "int64"),
"graph_sample_neighbors",
)
helper = LayerHelper("graph_sample_neighbors", **locals())
out_neighbors = helper.create_variable_for_type_inference(dtype=row.dtype)
out_count = helper.create_variable_for_type_inference(dtype=row.dtype)
out_eids = helper.create_variable_for_type_inference(dtype=row.dtype)
helper.append_op(
type="graph_sample_neighbors",
inputs={
"Row": row,
"Col_Ptr": colptr,
"X": input_nodes,
"Eids": eids if return_eids else None,
"Perm_Buffer": perm_buffer if flag_perm_buffer else None,
},
outputs={
"Out": out_neighbors,
"Out_Count": out_count,
"Out_Eids": out_eids,
},
attrs={
"sample_size": sample_size,
"return_eids": return_eids,
"flag_perm_buffer": flag_perm_buffer,
},
)
if return_eids:
return out_neighbors, out_count, out_eids
return out_neighbors, out_count
@@ -0,0 +1,201 @@
# Copyright (c) 2021 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, Literal
import numpy as np
from paddle import _C_ops
from paddle.base.data_feeder import (
check_dtype,
check_type,
check_variable_and_dtype,
)
from paddle.base.framework import Variable
from paddle.base.layer_helper import LayerHelper
from paddle.framework import in_dynamic_or_pir_mode
from paddle.geometric.message_passing.utils import (
convert_out_size_to_list,
get_out_size_tensor_inputs,
)
from paddle.utils import deprecated
if TYPE_CHECKING:
from paddle import Tensor
@deprecated(
since="2.4.0",
update_to="paddle.geometric.send_u_recv",
level=1,
reason="graph_send_recv in paddle.incubate will be removed in future",
)
def graph_send_recv(
x: Tensor,
src_index: Tensor,
dst_index: Tensor,
pool_type: Literal["sum", "mean", "max", "min"] = "sum",
out_size: int | Tensor | None = None,
name: str | None = None,
) -> Tensor:
"""
Graph Learning Send_Recv combine operator.
This operator is mainly used in Graph Learning domain, and the main purpose is to reduce intermediate memory
consumption in the process of message passing. Take `x` as the input tensor, we first use `src_index`
to gather the corresponding data, and then use `dst_index` to update the corresponding position of output tensor
in different pooling types, like sum, mean, max, or min. Besides, we can set `out_size` to get necessary output shape.
.. code-block:: text
Given:
X = [[0, 2, 3],
[1, 4, 5],
[2, 6, 7]]
src_index = [0, 1, 2, 0]
dst_index = [1, 2, 1, 0]
pool_type = "sum"
out_size = None
Then:
Out = [[0, 2, 3],
[2, 8, 10],
[1, 4, 5]]
Args:
x (Tensor): The input tensor, and the available data type is float32, float64, int32, int64.
src_index (Tensor): An 1-D tensor, and the available data type is int32, int64.
dst_index (Tensor): An 1-D tensor, and should have the same shape as `src_index`.
The available data type is int32, int64.
pool_type (str): The pooling types of graph_send_recv, including `sum`, `mean`, `max`, `min`.
Default value is `sum`.
out_size (int|Tensor|None): We can set `out_size` to get necessary output shape. If not set or
out_size is smaller or equal to 0, then this input will not be used.
Otherwise, `out_size` should be equal with or larger than
max(dst_index) + 1.
name (str, optional): Name for the operation (optional, default is None).
For more information, please refer to :ref:`api_guide_Name`.
Returns:
out (Tensor): The output tensor, should have the same shape and same dtype as input tensor `x`.
If `out_size` is set correctly, then it should have the same shape as `x` except
the 0th dimension.
Examples:
.. code-block:: pycon
>>> import paddle
>>> x = paddle.to_tensor([[0, 2, 3], [1, 4, 5], [2, 6, 7]], dtype="float32")
>>> indexes = paddle.to_tensor([[0, 1], [1, 2], [2, 1], [0, 0]], dtype="int32")
>>> src_index = indexes[:, 0]
>>> dst_index = indexes[:, 1]
>>> out = paddle.incubate.graph_send_recv(x, src_index, dst_index, pool_type="sum") # type: ignore[operator]
>>> print(out)
Tensor(shape=[3, 3], dtype=float32, place=Place(cpu), stop_gradient=True,
[[0. , 2. , 3. ],
[2. , 8. , 10.],
[1. , 4. , 5. ]])
>>> x = paddle.to_tensor([[0, 2, 3], [1, 4, 5], [2, 6, 7]], dtype="float32")
>>> indexes = paddle.to_tensor([[0, 1], [2, 1], [0, 0]], dtype="int32")
>>> src_index = indexes[:, 0]
>>> dst_index = indexes[:, 1]
>>> out_size = paddle.max(dst_index) + 1
>>> out = paddle.incubate.graph_send_recv(x, src_index, dst_index, pool_type="sum", out_size=out_size) # type: ignore[operator]
>>> print(out)
Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True,
[[0. , 2. , 3. ],
[2. , 8. , 10.]])
>>> x = paddle.to_tensor([[0, 2, 3], [1, 4, 5], [2, 6, 7]], dtype="float32")
>>> indexes = paddle.to_tensor([[0, 1], [2, 1], [0, 0]], dtype="int32")
>>> src_index = indexes[:, 0]
>>> dst_index = indexes[:, 1]
>>> out = paddle.incubate.graph_send_recv(x, src_index, dst_index, pool_type="sum") # type: ignore[operator]
>>> print(out)
Tensor(shape=[3, 3], dtype=float32, place=Place(cpu), stop_gradient=True,
[[0. , 2. , 3. ],
[2. , 8. , 10.],
[0. , 0. , 0. ]])
"""
if pool_type not in ["sum", "mean", "max", "min"]:
raise ValueError(
f"pool_type should be `sum`, `mean`, `max` or `min`, but received {pool_type}"
)
# TODO(daisiming): Should we add judgement for out_size: max(dst_index) + 1.
if in_dynamic_or_pir_mode():
out_size = convert_out_size_to_list(out_size, 'graph_send_recv')
return _C_ops.send_u_recv(
x, src_index, dst_index, pool_type.upper(), out_size
)
else:
check_variable_and_dtype(
x, "X", ("float32", "float64", "int32", "int64"), "graph_send_recv"
)
check_variable_and_dtype(
src_index, "Src_index", ("int32", "int64"), "graph_send_recv"
)
check_variable_and_dtype(
dst_index, "Dst_index", ("int32", "int64"), "graph_send_recv"
)
if out_size:
check_type(
out_size,
'out_size',
(int, np.int32, np.int64, Variable),
'graph_send_recv',
)
if isinstance(out_size, Variable):
check_dtype(
out_size.dtype,
'out_size',
['int32', 'int64'],
'graph_send_recv',
)
helper = LayerHelper("graph_send_recv", **locals())
out = helper.create_variable_for_type_inference(dtype=x.dtype)
dst_count = helper.create_variable_for_type_inference(
dtype="int32", stop_gradient=True
)
inputs = {"X": x, "Src_index": src_index, "Dst_index": dst_index}
attrs = {"reduce_op": pool_type.upper()}
get_out_size_tensor_inputs(
inputs=inputs,
attrs=attrs,
out_size=out_size,
op_type='graph_send_recv',
)
helper.append_op(
type="graph_send_recv",
inputs=inputs,
outputs={"Out": out, "Dst_count": dst_count},
attrs=attrs,
)
return out
@@ -0,0 +1,368 @@
# Copyright (c) 2021 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
import numpy as np
import paddle
from paddle import base
from paddle.base.layer_helper import LayerHelper
from paddle.base.param_attr import ParamAttr
from paddle.nn import (
Layer,
initializer as I,
)
if TYPE_CHECKING:
from paddle import Tensor
from paddle._typing import DataLayout2D, ParamAttrLike
def resnet_unit(
x: Tensor,
filter_x: Tensor,
scale_x: Tensor,
bias_x: Tensor,
mean_x: Tensor,
var_x: Tensor,
z: Tensor | None,
filter_z: Tensor | None,
scale_z: Tensor | None,
bias_z: Tensor | None,
mean_z: Tensor | None,
var_z: Tensor | None,
stride: int,
stride_z: int,
padding: int,
dilation: int,
groups: int,
momentum: float,
eps: float,
data_format: DataLayout2D,
fuse_add: bool,
has_shortcut: bool,
use_global_stats: bool,
is_test: bool,
act: str,
) -> Tensor:
helper = LayerHelper('resnet_unit', **locals())
bn_param_dtype = base.core.VarDesc.VarType.FP32
bit_mask_dtype = base.core.VarDesc.VarType.INT32
out = helper.create_variable_for_type_inference(x.dtype)
bit_mask = helper.create_variable_for_type_inference(
dtype=bit_mask_dtype, stop_gradient=True
)
# intermediate_out for x
conv_x = helper.create_variable_for_type_inference(
dtype=x.dtype, stop_gradient=True
)
saved_mean_x = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
saved_invstd_x = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
running_mean_x = mean_x
running_var_x = var_x
# intermediate_out for z
conv_z = helper.create_variable_for_type_inference(
dtype=x.dtype, stop_gradient=True
)
saved_mean_z = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
saved_invstd_z = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
running_mean_z = (
helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
if mean_z is None
else mean_z
)
running_var_z = (
helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
if var_z is None
else var_z
)
inputs = {
'X': x,
'FilterX': filter_x,
'ScaleX': scale_x,
'BiasX': bias_x,
'MeanX': mean_x,
'VarX': var_x,
'Z': z,
'FilterZ': filter_z,
'ScaleZ': scale_z,
'BiasZ': bias_z,
'MeanZ': mean_z,
'VarZ': var_z,
}
attrs = {
'stride': stride,
'stride_z': stride_z,
'padding': padding,
'dilation': dilation,
'group': groups,
'momentum': momentum,
'epsilon': eps,
'data_format': data_format,
'fuse_add': fuse_add,
'has_shortcut': has_shortcut,
'use_global_stats': use_global_stats,
'is_test': is_test,
'act_type': act,
}
outputs = {
'Y': out,
'BitMask': bit_mask,
'ConvX': conv_x,
'SavedMeanX': saved_mean_x,
'SavedInvstdX': saved_invstd_x,
'RunningMeanX': running_mean_x,
'RunningVarX': running_var_x,
'ConvZ': conv_z,
'SavedMeanZ': saved_mean_z,
'SavedInvstdZ': saved_invstd_z,
'RunningMeanZ': running_mean_z,
'RunningVarZ': running_var_z,
}
helper.append_op(
type='resnet_unit', inputs=inputs, outputs=outputs, attrs=attrs
)
return out
class ResNetUnit(Layer):
r"""
******Temporary version******.
ResNetUnit is designed for optimize the performance by using cudnnv8 API.
"""
def __init__(
self,
num_channels_x: int,
num_filters: int,
filter_size: int,
stride: int = 1,
momentum: float = 0.9,
eps: float = 1e-5,
data_format: DataLayout2D = 'NHWC',
act: str = 'relu',
fuse_add: bool = False,
has_shortcut: bool = False,
use_global_stats: bool = False,
is_test: bool = False,
filter_x_attr: ParamAttrLike | None = None,
scale_x_attr: ParamAttrLike | None = None,
bias_x_attr: ParamAttrLike | None = None,
moving_mean_x_name: str | None = None,
moving_var_x_name: str | None = None,
num_channels_z: int = 1,
stride_z: int = 1,
filter_z_attr: ParamAttrLike | None = None,
scale_z_attr: ParamAttrLike | None = None,
bias_z_attr: ParamAttrLike | None = None,
moving_mean_z_name: str | None = None,
moving_var_z_name: str | None = None,
) -> None:
super().__init__()
self._stride = stride
self._stride_z = stride_z
self._dilation = 1
self._kernel_size = paddle.utils.convert_to_list(
filter_size, 2, 'kernel_size'
)
self._padding = (filter_size - 1) // 2
self._groups = 1
self._momentum = momentum
self._eps = eps
self._data_format = data_format
self._act = act
self._fuse_add = fuse_add
self._has_shortcut = has_shortcut
self._use_global_stats = use_global_stats
self._is_test = is_test
# check format
valid_format = {'NHWC', 'NCHW'}
if data_format not in valid_format:
raise ValueError(
f"conv_format must be one of {valid_format}, but got conv_format='{data_format}'"
)
def _get_default_param_initializer(channels):
filter_elem_num = np.prod(self._kernel_size) * channels
std = (2.0 / filter_elem_num) ** 0.5
return I.Normal(0.0, std)
is_nchw = data_format == 'NCHW'
# initial filter
bn_param_dtype = base.core.VarDesc.VarType.FP32
if not is_nchw:
bn_param_shape = [1, 1, 1, num_filters]
filter_x_shape = [
num_filters,
filter_size,
filter_size,
num_channels_x,
]
filter_z_shape = [
num_filters,
filter_size,
filter_size,
num_channels_z,
]
else:
bn_param_shape = [1, num_filters, 1, 1]
filter_x_shape = [
num_filters,
num_channels_x,
filter_size,
filter_size,
]
filter_z_shape = [
num_filters,
num_channels_z,
filter_size,
filter_size,
]
self.filter_x = self.create_parameter(
shape=filter_x_shape,
attr=filter_x_attr,
default_initializer=_get_default_param_initializer(num_channels_x),
)
self.scale_x = self.create_parameter(
shape=bn_param_shape,
attr=scale_x_attr,
dtype=bn_param_dtype,
default_initializer=I.Constant(1.0),
)
self.bias_x = self.create_parameter(
shape=bn_param_shape,
attr=bias_x_attr,
dtype=bn_param_dtype,
is_bias=True,
)
self.mean_x = self.create_parameter(
attr=ParamAttr(
name=moving_mean_x_name,
initializer=I.Constant(0.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.mean_x.stop_gradient = True
self.var_x = self.create_parameter(
attr=ParamAttr(
name=moving_var_x_name,
initializer=I.Constant(1.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.var_x.stop_gradient = True
if has_shortcut:
self.filter_z = self.create_parameter(
shape=filter_z_shape,
attr=filter_z_attr,
default_initializer=_get_default_param_initializer(
num_channels_z
),
)
self.scale_z = self.create_parameter(
shape=bn_param_shape,
attr=scale_z_attr,
dtype=bn_param_dtype,
default_initializer=I.Constant(1.0),
)
self.bias_z = self.create_parameter(
shape=bn_param_shape,
attr=bias_z_attr,
dtype=bn_param_dtype,
is_bias=True,
)
self.mean_z = self.create_parameter(
attr=ParamAttr(
name=moving_mean_z_name,
initializer=I.Constant(0.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.mean_z.stop_gradient = True
self.var_z = self.create_parameter(
attr=ParamAttr(
name=moving_var_z_name,
initializer=I.Constant(1.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.var_z.stop_gradient = True
else:
self.filter_z = None
self.scale_z = None
self.bias_z = None
self.mean_z = None
self.var_z = None
def forward(self, x: Tensor, z: Tensor | None = None) -> Tensor:
if self._fuse_add and z is None:
raise ValueError("z can not be None")
out = resnet_unit(
x,
self.filter_x,
self.scale_x,
self.bias_x,
self.mean_x,
self.var_x,
z,
self.filter_z,
self.scale_z,
self.bias_z,
self.mean_z,
self.var_z,
self._stride,
self._stride_z,
self._padding,
self._dilation,
self._groups,
self._momentum,
self._eps,
self._data_format,
self._fuse_add,
self._has_shortcut,
self._use_global_stats,
self._is_test,
self._act,
)
return out
@@ -0,0 +1,80 @@
# Copyright (c) 2021 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
import paddle
from paddle import _C_ops
from paddle.base.layer_helper import LayerHelper
from paddle.framework import in_dynamic_or_pir_mode
if TYPE_CHECKING:
from paddle import Tensor
def softmax_mask_fuse(
x: Tensor, mask: Tensor, name: str | None = None
) -> Tensor:
"""
Do a masked softmax on x.
This is designed for speeding up Transformer structure.
Used for reducing operation such as: tmp = x + mask, out = softmax(tmp).
The equation is:
.. math::
out = softmax(x + mask)
Note:
This API only supports GPU.
Args:
x (4-D Tensor): The input tensor, should be in 4D shape, it's data type should be float16, float32.
The fourth dimension of x must be larger or equal to 32 and less then 8192.
mask (4-D Tensor): The input tensor, should be in 4D shape, it's data type should be float16, float32.
The second dimension of mask must be 1, and other dimensions must be same with x.
name (str, optional): Name for the operation (optional, default is None).
For more information, please refer to :ref:`api_guide_Name`.
Returns:
4-D Tensor. A location into which the result is stored. It's dimension is 4D. Has same shape with x.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> import paddle.incubate as incubate
>>> x = paddle.rand([2, 8, 8, 32])
>>> mask = paddle.rand([2, 1, 8, 32])
>>> rst = incubate.softmax_mask_fuse(x, mask) # type: ignore[operator]
>>> rst.shape
paddle.Size([2, 8, 8, 32])
"""
if in_dynamic_or_pir_mode():
if isinstance(mask, (paddle.Tensor)) and mask.size == 0:
return x + mask
out = _C_ops.fused_softmax_mask(x, mask)
return out
helper = LayerHelper('fused_softmax_mask', **locals())
out = helper.create_variable_for_type_inference(dtype=x.dtype)
helper.append_op(
type='fused_softmax_mask',
inputs={'X': [x], 'Mask': [mask]},
outputs={'Out': [out]},
)
return out
@@ -0,0 +1,96 @@
# Copyright (c) 2021 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.layer_helper import LayerHelper
from paddle.framework import in_dynamic_or_pir_mode
from paddle.utils.deprecated import deprecated
if TYPE_CHECKING:
from paddle import Tensor
@deprecated(
since="3.4.0",
level=1,
update_to="paddle.nn.functional.scaled_dot_product_attention",
)
def softmax_mask_fuse_upper_triangle(x: Tensor) -> Tensor:
"""
Do a masked softmax on x, which will always mask upper triangle part of x.
This is designed for speeding up GPT kind Transformer structure.
Used for reducing operation such as: tmp = x + mask, out = softmax(tmp), where the mask is
always be an upper triangle matrix.
The equation is:
.. math::
out = softmax(LowerTriangular(x))
Note:
This API only supports GPU.
Args:
x (4-D Tensor): The input tensor, should be in 4D shape, it's data type should be float16, float32
The fourth dimension of x must be larger or equal to 32 and less then 8192.
The third dimension of x must be same with the fourth dimension of x.
Returns:
4-D Tensor. A location into which the result is stored. It's dimension is 4D. Has same dimension with x.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> import paddle.incubate as incubate
>>> paddle.seed(1)
>>> paddle.set_device("gpu")
>>> x = paddle.rand((1, 1, 32, 32))
>>> rst = incubate.softmax_mask_fuse_upper_triangle(x) # type: ignore[operator]
>>> print(rst)
Tensor(shape=[1, 1, 32, 32], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[[[[1. , 0. , 0. , ..., 0. ,
0. , 0. ],
[0.49575609, 0.50424391, 0. , ..., 0. ,
0. , 0. ],
[0.26035303, 0.25114325, 0.48850375, ..., 0. ,
0. , 0. ],
...,
[0.04379999, 0.04194880, 0.05150032, ..., 0.02721255,
0. , 0. ],
[0.02348574, 0.01959674, 0.02609110, ..., 0.04046615,
0.02248267, 0. ],
[0.02280738, 0.03144657, 0.02892209, ..., 0.03885521,
0.03342311, 0.02842640]]]])
"""
if in_dynamic_or_pir_mode():
out = _C_ops.fused_softmax_mask_upper_triangle(x)
return out
helper = LayerHelper('fused_softmax_mask_upper_triangle', **locals())
out = helper.create_variable_for_type_inference(dtype=x.dtype)
helper.append_op(
type='fused_softmax_mask_upper_triangle',
inputs={'X': [x]},
outputs={'Out': [out]},
)
return out