chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
version.py
|
||||
cuda_env.py
|
||||
version
|
||||
@@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import typing
|
||||
|
||||
from paddle.base import core, libpaddle
|
||||
from paddle.base.libpaddle import (
|
||||
_get_current_raw_stream as _cuda_getCurrentRawStream, # noqa: F401
|
||||
)
|
||||
|
||||
# Define _GLIBCXX_USE_CXX11_ABI based on compilation flags
|
||||
_GLIBCXX_USE_CXX11_ABI = getattr(libpaddle, '_GLIBCXX_USE_CXX11_ABI', True)
|
||||
_PYBIND11_COMPILER_TYPE = getattr(libpaddle, '_PYBIND11_COMPILER_TYPE', "")
|
||||
_PYBIND11_STDLIB = getattr(libpaddle, '_PYBIND11_STDLIB', "")
|
||||
_PYBIND11_BUILD_ABI = getattr(libpaddle, '_PYBIND11_BUILD_ABI', "")
|
||||
|
||||
|
||||
def _get_custom_class_python_wrapper(
|
||||
namespace_name: str, class_name: str
|
||||
) -> typing.Any:
|
||||
return core.torch_compat._get_custom_class_python_wrapper(
|
||||
namespace_name, class_name
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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 paddle.base import core
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
for name in dir(core.eager.ops):
|
||||
globals()[name] = getattr(core.eager.ops, name)
|
||||
__all__.append(name)
|
||||
|
||||
for name in dir(core.pir.ops):
|
||||
globals()[name] = getattr(core.pir.ops, name)
|
||||
if name not in __all__:
|
||||
__all__.append(name)
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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 paddle._typing.libs.libpaddle.eager.ops import * # noqa: F403
|
||||
from paddle._typing.libs.libpaddle.pir.ops import * # noqa: F403
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
# The file has been adapted from pytorch project
|
||||
# Licensed under BSD-style license -
|
||||
# https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import types
|
||||
from typing import Any
|
||||
|
||||
import paddle
|
||||
|
||||
from ._ops import import_module, load_library
|
||||
|
||||
PADDLE_CLASSES_MODULE_NAME = "paddle.classes"
|
||||
|
||||
|
||||
class ClassesNameSpace(types.ModuleType):
|
||||
def __init__(self, name: str):
|
||||
super().__init__(f"{PADDLE_CLASSES_MODULE_NAME}.{name}")
|
||||
self.name = name
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
if name == "__file__":
|
||||
return PADDLE_CLASSES_MODULE_NAME # type: ignore
|
||||
return paddle.base.core.torch_compat._get_custom_class_python_wrapper(
|
||||
self.name, name
|
||||
)
|
||||
|
||||
|
||||
class PaddleClassesModule(types.ModuleType):
|
||||
__file__ = "_classes.py"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(PADDLE_CLASSES_MODULE_NAME)
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
namespace = ClassesNameSpace(name)
|
||||
# Insert to __dict__ to avoid repeatedly __getattr__ overhead
|
||||
setattr(self, name, namespace)
|
||||
return namespace
|
||||
|
||||
def import_module(self, module):
|
||||
return import_module(module)
|
||||
|
||||
def load_library(self, path):
|
||||
return load_library(path)
|
||||
|
||||
|
||||
classes = PaddleClassesModule()
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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 paddle.base import core
|
||||
|
||||
__all__ = []
|
||||
|
||||
for name in dir(core.eager.ops.legacy):
|
||||
globals()[name] = getattr(core.eager.ops.legacy, name)
|
||||
__all__.append(name)
|
||||
@@ -0,0 +1,155 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
# The file has been adapted from pytorch project
|
||||
# Licensed under BSD-style license -
|
||||
# https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import ctypes
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
from functools import cached_property
|
||||
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
||||
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
import paddle
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
_InputT = ParamSpec("_InputT")
|
||||
_RetT = TypeVar("_RetT")
|
||||
|
||||
PADDLE_OPS_MODULE_NAME = "paddle.ops"
|
||||
|
||||
# Query `hasattr` only once.
|
||||
_SET_GLOBAL_FLAGS = hasattr(sys, "getdlopenflags") and hasattr(
|
||||
sys, "setdlopenflags"
|
||||
)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def dl_open_guard():
|
||||
"""
|
||||
Context manager to set the RTLD_GLOBAL dynamic linker flag while we open a
|
||||
shared library to load custom operators.
|
||||
"""
|
||||
if not _SET_GLOBAL_FLAGS:
|
||||
yield
|
||||
return
|
||||
old_flags = sys.getdlopenflags()
|
||||
sys.setdlopenflags(old_flags | ctypes.RTLD_GLOBAL)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
sys.setdlopenflags(old_flags)
|
||||
|
||||
|
||||
def import_module(module: str):
|
||||
return importlib.import_module(module)
|
||||
|
||||
|
||||
def load_library(path: str):
|
||||
"""
|
||||
Load a shared library at the specified path.
|
||||
"""
|
||||
path = os.path.realpath(path)
|
||||
with dl_open_guard():
|
||||
ctypes.CDLL(path)
|
||||
|
||||
|
||||
class PythonOpRegistry:
|
||||
def __init__(self):
|
||||
self._registry: dict[str, Callable[..., object]] = {}
|
||||
|
||||
def register(self, name: str, fn: Callable[..., object]):
|
||||
if name in self._registry:
|
||||
raise ValueError(f"Operator '{name}' is already registered.")
|
||||
self._registry[name] = fn
|
||||
|
||||
def has_operator(self, name: str) -> bool:
|
||||
return name in self._registry
|
||||
|
||||
def get_operator(self, name: str) -> Callable[..., object]:
|
||||
if name not in self._registry:
|
||||
raise ValueError(f"Operator '{name}' is not registered.")
|
||||
return self._registry[name]
|
||||
|
||||
|
||||
PYTHON_OP_REGISTRY = PythonOpRegistry()
|
||||
|
||||
|
||||
class OverloadedOpFunction(Generic[_InputT, _RetT]):
|
||||
def __init__(self, namespace: str, name: str):
|
||||
self.namespace = namespace
|
||||
self.name = name
|
||||
|
||||
@cached_property
|
||||
def callable_fn(self) -> Callable[_InputT, _RetT]:
|
||||
if PYTHON_OP_REGISTRY.has_operator(f"{self.namespace}::{self.name}"):
|
||||
return PYTHON_OP_REGISTRY.get_operator( # type: ignore
|
||||
f"{self.namespace}::{self.name}"
|
||||
)
|
||||
return paddle.base.core.torch_compat._get_operation(
|
||||
f"{self.namespace}::{self.name}"
|
||||
)
|
||||
|
||||
def __getattr__(self, name: str) -> Callable[_InputT, _RetT]:
|
||||
if name == "default":
|
||||
return self.callable_fn
|
||||
raise AttributeError(
|
||||
f"'{self.namespace}.{self.name}' has no attribute '{name}'"
|
||||
)
|
||||
|
||||
def __call__(self, *args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT:
|
||||
return self.callable_fn(*args, **kwargs)
|
||||
|
||||
|
||||
class OpNameSpace(types.ModuleType):
|
||||
def __init__(self, name):
|
||||
super().__init__(f"{PADDLE_OPS_MODULE_NAME}.{name}")
|
||||
self.name = name
|
||||
|
||||
def __getattr__(self, name: str) -> OverloadedOpFunction[..., Any]:
|
||||
if name == "__file__":
|
||||
return PADDLE_OPS_MODULE_NAME # type: ignore
|
||||
return OverloadedOpFunction(self.name, name)
|
||||
|
||||
|
||||
class PaddleOpsModule(types.ModuleType):
|
||||
__file__ = "_ops.py"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(PADDLE_OPS_MODULE_NAME)
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
namespace = OpNameSpace(name)
|
||||
# Insert to __dict__ to avoid repeatedly __getattr__ overhead
|
||||
setattr(self, name, namespace)
|
||||
return namespace
|
||||
|
||||
def import_module(self, module):
|
||||
return import_module(module)
|
||||
|
||||
def load_library(self, path):
|
||||
return load_library(path)
|
||||
|
||||
|
||||
ops = PaddleOpsModule()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
# 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 paddle.base import core
|
||||
|
||||
__all__ = []
|
||||
|
||||
for name in dir(core.pir.ops):
|
||||
globals()[name] = getattr(core.pir.ops, name)
|
||||
__all__.append(name)
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2024 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.
|
||||
|
||||
# Basic
|
||||
from .basic import (
|
||||
NestedList as NestedList,
|
||||
NestedNumericSequence as NestedNumericSequence,
|
||||
NestedSequence as NestedSequence,
|
||||
NestedStructure as NestedStructure,
|
||||
Numeric as Numeric,
|
||||
NumericSequence as NumericSequence,
|
||||
ParamAttrLike as ParamAttrLike,
|
||||
TensorIndex as TensorIndex,
|
||||
TensorLike as TensorLike,
|
||||
TensorOrTensors as TensorOrTensors,
|
||||
unreached as unreached,
|
||||
)
|
||||
|
||||
# Device
|
||||
from .device_like import (
|
||||
PlaceLike as PlaceLike,
|
||||
)
|
||||
|
||||
# DType
|
||||
from .dtype_like import DTypeLike as DTypeLike
|
||||
|
||||
# DataLayout
|
||||
from .layout import (
|
||||
DataLayout0D as DataLayout0D,
|
||||
DataLayout1D as DataLayout1D,
|
||||
DataLayout1DVariant as DataLayout1DVariant,
|
||||
DataLayout2D as DataLayout2D,
|
||||
DataLayout3D as DataLayout3D,
|
||||
DataLayoutImage as DataLayoutImage,
|
||||
DataLayoutND as DataLayoutND,
|
||||
)
|
||||
|
||||
# Shape
|
||||
from .shape import (
|
||||
ShapeLike as ShapeLike,
|
||||
Size1 as Size1,
|
||||
Size2 as Size2,
|
||||
Size3 as Size3,
|
||||
Size4 as Size4,
|
||||
Size5 as Size5,
|
||||
Size6 as Size6,
|
||||
SizeN as SizeN,
|
||||
)
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (c) 2024 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 collections.abc import Sequence
|
||||
from types import EllipsisType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
TypeAlias,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from typing_extensions import Never
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import ParamAttr, Tensor
|
||||
from paddle.nn.initializer import Initializer
|
||||
from paddle.regularizer import WeightDecayRegularizer
|
||||
|
||||
|
||||
Numeric: TypeAlias = Union[int, float, bool, complex, np.number, "Tensor"]
|
||||
TensorLike: TypeAlias = Union[npt.NDArray[Any], "Tensor", Numeric]
|
||||
_TensorIndexItem: TypeAlias = Union[
|
||||
None, bool, int, slice, "Tensor", EllipsisType
|
||||
]
|
||||
TensorIndex: TypeAlias = (
|
||||
_TensorIndexItem | tuple[_TensorIndexItem, ...] | list[_TensorIndexItem]
|
||||
)
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
NestedSequence = _T | Sequence["NestedSequence[_T]"]
|
||||
NestedList = _T | list["NestedList[_T]"]
|
||||
NestedStructure = (
|
||||
_T | dict[str, "NestedStructure[_T]"] | Sequence["NestedStructure[_T]"]
|
||||
)
|
||||
NumericSequence = Sequence[Numeric]
|
||||
NestedNumericSequence: TypeAlias = NestedSequence[Numeric]
|
||||
TensorOrTensors: TypeAlias = Union["Tensor", Sequence["Tensor"]]
|
||||
|
||||
ParamAttrLike: TypeAlias = Union[
|
||||
"ParamAttr", "Initializer", "WeightDecayRegularizer", str, bool
|
||||
]
|
||||
|
||||
|
||||
def unreached() -> Never:
|
||||
"""Mark a code path as unreachable.
|
||||
Refer to https://typing.readthedocs.io/en/latest/source/unreachable.html#marking-code-as-unreachable
|
||||
"""
|
||||
raise RuntimeError("Unreachable code path")
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2024 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, TypeAlias, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle.base.core import Place
|
||||
|
||||
PlaceLike: TypeAlias = Union[
|
||||
"Place",
|
||||
str, # some string like "cpu", "gpu:0", etc.
|
||||
]
|
||||
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2024 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, TypeAlias, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import dtype
|
||||
|
||||
_DTypeLiteral: TypeAlias = Literal[
|
||||
"uint8",
|
||||
"int8",
|
||||
"int16",
|
||||
"int32",
|
||||
"int64",
|
||||
"float32",
|
||||
"float64",
|
||||
"float16",
|
||||
"bfloat16",
|
||||
"complex64",
|
||||
"complex128",
|
||||
"bool",
|
||||
]
|
||||
|
||||
_DTypeNumpy: TypeAlias = (
|
||||
type[
|
||||
np.uint8
|
||||
| np.int8
|
||||
| np.int16
|
||||
| np.int32
|
||||
| np.int64
|
||||
| np.float16
|
||||
| np.float32
|
||||
| np.float64
|
||||
| np.complex64
|
||||
| np.complex128
|
||||
| np.bool_
|
||||
]
|
||||
| np.dtype
|
||||
)
|
||||
|
||||
|
||||
DTypeLike: TypeAlias = Union["dtype", _DTypeNumpy, _DTypeLiteral]
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2024 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 Literal, TypeAlias
|
||||
|
||||
# Note: Do not conform to predefined naming style in pylint.
|
||||
DataLayout0D: TypeAlias = Literal["NC"]
|
||||
DataLayout1D: TypeAlias = Literal["NCL", "NLC"]
|
||||
DataLayout2D: TypeAlias = Literal["NCHW", "NHWC"]
|
||||
DataLayout3D: TypeAlias = Literal["NCDHW", "NDHWC"]
|
||||
|
||||
DataLayoutND: TypeAlias = (
|
||||
DataLayout0D | DataLayout1D | DataLayout2D | DataLayout3D
|
||||
)
|
||||
|
||||
DataLayout1DVariant: TypeAlias = Literal["NCW", "NWC"]
|
||||
DataLayoutImage: TypeAlias = Literal["HWC", "CHW"]
|
||||
@@ -0,0 +1,5 @@
|
||||
# Stub files for pybind11 C++ APIs
|
||||
|
||||
The files in this directory (`python/paddle/_typing/libs`) are auto generated from pybind11 C++ APIs.
|
||||
|
||||
Please do NOT edit these files, or the changes may be lost.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2024 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 collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, TypeAlias, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Tensor
|
||||
|
||||
_DynamicShapeLike: TypeAlias = Union[
|
||||
Sequence[Union[int, "Tensor", None]],
|
||||
"Tensor",
|
||||
]
|
||||
|
||||
|
||||
_StaticShapeLike: TypeAlias = Union[
|
||||
Sequence[int],
|
||||
"Tensor",
|
||||
]
|
||||
|
||||
ShapeLike: TypeAlias = _DynamicShapeLike | _StaticShapeLike
|
||||
|
||||
# for size parameters, eg, kernel_size, stride ...
|
||||
Size1: TypeAlias = int | tuple[int] | list[int]
|
||||
Size2: TypeAlias = int | tuple[int, int] | list[int]
|
||||
Size3: TypeAlias = int | tuple[int, int, int] | list[int]
|
||||
Size4: TypeAlias = int | tuple[int, int, int, int] | list[int]
|
||||
Size5: TypeAlias = int | tuple[int, int, int, int, int] | list[int]
|
||||
Size6: TypeAlias = int | tuple[int, int, int, int, int, int] | list[int]
|
||||
SizeN: TypeAlias = int | tuple[int, ...] | list[int]
|
||||
@@ -0,0 +1,113 @@
|
||||
# Copyright (c) 2020 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 paddle.base import core
|
||||
from paddle.base.framework import (
|
||||
_current_expected_place,
|
||||
_get_paddle_place,
|
||||
)
|
||||
|
||||
from . import ( # noqa: F401
|
||||
accuracy_compare,
|
||||
debugging,
|
||||
grad_scaler,
|
||||
)
|
||||
from .amp_lists import ( # noqa: F401
|
||||
black_list,
|
||||
white_list,
|
||||
)
|
||||
from .auto_cast import ( # noqa: F401
|
||||
amp_decorate,
|
||||
amp_guard,
|
||||
auto_cast,
|
||||
autocast,
|
||||
decorate,
|
||||
get_autocast_dtype,
|
||||
is_autocast_enabled,
|
||||
)
|
||||
from .grad_scaler import ( # noqa: F401
|
||||
AmpScaler,
|
||||
GradScaler,
|
||||
OptimizerState,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'auto_cast',
|
||||
'GradScaler',
|
||||
'decorate',
|
||||
'is_float16_supported',
|
||||
'is_bfloat16_supported',
|
||||
'is_autocast_enabled',
|
||||
'get_autocast_dtype',
|
||||
'get_autocast_cpu_dtype',
|
||||
'get_autocast_gpu_dtype',
|
||||
]
|
||||
|
||||
get_autocast_cpu_dtype = get_autocast_dtype
|
||||
get_autocast_gpu_dtype = get_autocast_dtype
|
||||
|
||||
|
||||
def is_float16_supported(device: str | None = None) -> bool:
|
||||
"""
|
||||
Determine whether the place supports float16 in the auto-mixed-precision training.
|
||||
|
||||
Args:
|
||||
device (str|None, optional): Specify the running device.
|
||||
It can be ``cpu``, ``gpu``, ``xpu``, ``gpu:x`` and ``xpu:x``,
|
||||
where ``x`` is the index of the GPUs or XPUs. if device is None, the device is the current device. Default: None.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> paddle.amp.is_float16_supported() # True or False
|
||||
False
|
||||
"""
|
||||
|
||||
device = (
|
||||
_current_expected_place()
|
||||
if device is None
|
||||
else _get_paddle_place(device)
|
||||
)
|
||||
|
||||
return core.is_float16_supported(device)
|
||||
|
||||
|
||||
def is_bfloat16_supported(device: str | None = None) -> bool:
|
||||
"""
|
||||
Determine whether the place supports bfloat16 in the auto-mixed-precision training.
|
||||
|
||||
Args:
|
||||
device (str|None, optional): Specify the running device.
|
||||
It can be ``cpu``, ``gpu``, ``xpu``, ``gpu:x`` and ``xpu:x``,
|
||||
where ``x`` is the index of the GPUs or XPUs. if device is None, the device is the current device. Default: None.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> paddle.amp.is_bfloat16_supported() # True or False
|
||||
True
|
||||
"""
|
||||
|
||||
device = (
|
||||
_current_expected_place()
|
||||
if device is None
|
||||
else _get_paddle_place(device)
|
||||
)
|
||||
|
||||
return core.is_bfloat16_supported(device)
|
||||
@@ -0,0 +1,700 @@
|
||||
# Copyright (c) 2023 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.
|
||||
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
# Judge whether the value is within the range indicated by fp16
|
||||
def is_infinite(value, dtype=np.float16):
|
||||
# return value > np.finfo(np.float16).max or value < np.finfo(np.float16).min
|
||||
array = np.array([value]).astype(dtype)
|
||||
return np.isinf(array) or np.isnan(array)
|
||||
|
||||
|
||||
# Judge whether the value of fp32 is equal to that of fp16
|
||||
def is_allclose(actual, expected, atol=1e-2, rtol=1e-2):
|
||||
return np.allclose(
|
||||
np.array([actual]), np.array([expected]), atol=atol, rtol=rtol
|
||||
)
|
||||
|
||||
|
||||
class TensorInfo:
|
||||
def __init__(self):
|
||||
self.device = None
|
||||
self.op_type = None
|
||||
self.tensor_name = None
|
||||
self.dtype = None
|
||||
self.numel = None
|
||||
self.max_value = None
|
||||
self.min_value = None
|
||||
self.mean_value = None
|
||||
self.has_inf = None
|
||||
self.has_nan = None
|
||||
self.num_zero = None
|
||||
|
||||
def __str__(self):
|
||||
return f"[TensorInfo] device={self.device}, op_type={self.op_type}, tensor_name={self.tensor_name}, dtype={self.dtype}, numel={self.numel}, num_inf={self.has_inf}, num_nan={self.has_nan}, num_zero={self.num_zero}, max_value={self.max_value:.6f}, min_value={self.min_value:.6f}, mean_value={self.mean_value:.6f}"
|
||||
|
||||
def key(
|
||||
self,
|
||||
):
|
||||
return self.op_type + "/" + self.tensor_name
|
||||
|
||||
def init_from_string(self, line):
|
||||
try:
|
||||
line_frags = line.strip().split(" ")
|
||||
for frag in line_frags:
|
||||
word_str = (
|
||||
frag.replace("[", "").replace("]", "").replace(",", "")
|
||||
)
|
||||
words = word_str.split("=")
|
||||
if words[0] == "op":
|
||||
self.op_type = words[1]
|
||||
elif words[0] == "device":
|
||||
self.device = words[1]
|
||||
elif words[0] == "tensor":
|
||||
self.tensor_name = words[1]
|
||||
elif words[0] == "dtype":
|
||||
self.dtype = words[1]
|
||||
elif words[0] == "numel":
|
||||
self.numel = np.int64(words[1])
|
||||
elif words[0] == "max":
|
||||
self.max_value = np.float32(words[1])
|
||||
elif words[0] == "min":
|
||||
self.min_value = np.float32(words[1])
|
||||
elif words[0] == "mean":
|
||||
self.mean_value = np.float32(words[1])
|
||||
elif words[0] == "num_inf":
|
||||
self.has_inf = int(words[1])
|
||||
elif words[0] == "num_nan":
|
||||
self.has_nan = int(words[1])
|
||||
elif words[0] == "num_zero":
|
||||
self.num_zero = np.int64(words[1])
|
||||
except Exception as e:
|
||||
print(f"!! Error parsing {line}")
|
||||
return self
|
||||
|
||||
|
||||
class MixedPrecisionTensorInfo:
|
||||
def __init__(
|
||||
self, fp32_tensor_info, fp16_tensor_info, fp32_idx=0, grad_scale=1.0
|
||||
):
|
||||
self.is_normal = True
|
||||
self.fp32_idx = fp32_idx
|
||||
|
||||
self.fp32_tensor_name = None
|
||||
self.fp32_dtype = None
|
||||
self.fp32_max_value = None
|
||||
self.fp32_min_value = None
|
||||
self.fp32_mean_value = None
|
||||
self.fp32_num_zero = None
|
||||
self.scaled_fp32_max_value = None
|
||||
self.scaled_fp32_min_value = None
|
||||
|
||||
self.fp16_tensor_name = None
|
||||
self.fp16_dtype = None
|
||||
self.fp16_max_value = None
|
||||
self.fp16_min_value = None
|
||||
self.fp16_mean_value = None
|
||||
self.fp16_num_zero = None
|
||||
self.fp16_has_inf = None
|
||||
self.fp16_has_nan = None
|
||||
|
||||
self.fp32_div_fp16_max_value = None
|
||||
self.fp32_div_fp16_min_value = None
|
||||
self.fp32_div_fp16_mean_value = None
|
||||
|
||||
if fp32_tensor_info is not None:
|
||||
self.op_type = fp32_tensor_info.op_type
|
||||
self.numel = fp32_tensor_info.numel
|
||||
self.fp32_num_zero = fp32_tensor_info.num_zero
|
||||
self.fp32_tensor_name = fp32_tensor_info.tensor_name
|
||||
self.fp32_dtype = fp32_tensor_info.dtype
|
||||
self.fp32_max_value = fp32_tensor_info.max_value
|
||||
self.fp32_min_value = fp32_tensor_info.min_value
|
||||
self.fp32_mean_value = fp32_tensor_info.mean_value
|
||||
if "GRAD" in self.fp32_tensor_name:
|
||||
self.scaled_fp32_max_value = (
|
||||
grad_scale * fp32_tensor_info.max_value
|
||||
)
|
||||
self.scaled_fp32_min_value = (
|
||||
grad_scale * fp32_tensor_info.min_value
|
||||
)
|
||||
|
||||
if fp16_tensor_info is not None:
|
||||
self.op_type = fp16_tensor_info.op_type
|
||||
self.numel = fp16_tensor_info.numel
|
||||
self.fp16_num_zero = fp16_tensor_info.num_zero
|
||||
self.fp16_tensor_name = fp16_tensor_info.tensor_name
|
||||
self.fp16_dtype = fp16_tensor_info.dtype
|
||||
self.fp16_max_value = fp16_tensor_info.max_value
|
||||
self.fp16_min_value = fp16_tensor_info.min_value
|
||||
self.fp16_mean_value = fp16_tensor_info.mean_value
|
||||
self.fp16_has_inf = fp16_tensor_info.has_inf
|
||||
self.fp16_has_nan = fp16_tensor_info.has_nan
|
||||
|
||||
if fp32_tensor_info is not None and fp16_tensor_info is not None:
|
||||
# Check whether the op name and data are equal
|
||||
assert fp32_tensor_info.op_type == fp16_tensor_info.op_type
|
||||
assert fp32_tensor_info.numel == fp16_tensor_info.numel, (
|
||||
f"Error:\n\tFP32 Tensor Info:{fp32_tensor_info}\n\tFP16 Tensor Info:{fp16_tensor_info}"
|
||||
)
|
||||
# Fp16 divided by fp32
|
||||
self.fp32_div_fp16_max_value = self._div(
|
||||
self.fp16_max_value, self.fp32_max_value
|
||||
)
|
||||
self.fp32_div_fp16_min_value = self._div(
|
||||
self.fp16_min_value, self.fp32_min_value
|
||||
)
|
||||
self.fp32_div_fp16_mean_value = self._div(
|
||||
self.fp16_mean_value, self.fp32_mean_value
|
||||
)
|
||||
|
||||
self._check_normal()
|
||||
|
||||
def __str__(self):
|
||||
def _float_str(value):
|
||||
return f"{value:.6f}" if value is not None else value
|
||||
|
||||
debug_str = f"[MixedPrecisionTensorInfo] op_type={self.op_type}, numel={self.numel}"
|
||||
debug_str += f"\n FP32: tensor_name={self.fp32_tensor_name}, dtype={self.fp32_dtype}, max_value={_float_str(self.fp32_max_value)}, min_value={_float_str(self.fp32_min_value)}, mean_value={_float_str(self.fp32_mean_value)}"
|
||||
debug_str += f"\n FP16: tensor_name={self.fp16_tensor_name}, dtype={self.fp16_dtype}, max_value={_float_str(self.fp16_max_value)}, min_value={_float_str(self.fp16_min_value)}, mean_value={_float_str(self.fp16_mean_value)}, has_inf={self.fp16_has_inf}, has_nan={self.fp16_has_nan}"
|
||||
return debug_str
|
||||
|
||||
def _div(self, a, b):
|
||||
if a is not None and b is not None:
|
||||
return a / b if b != 0 else 1
|
||||
return None
|
||||
|
||||
def get_tensor_name(self):
|
||||
if self.fp32_tensor_name is None:
|
||||
return self.fp16_tensor_name # + "#" + str(self.idx)
|
||||
elif self.fp16_tensor_name is None:
|
||||
return self.fp32_tensor_name + "#" + str(self.fp32_idx)
|
||||
else:
|
||||
return (
|
||||
self.fp16_tensor_name.replace(".cast_fp16", "/.cast_fp16/")
|
||||
+ "#"
|
||||
+ str(self.fp32_idx)
|
||||
)
|
||||
|
||||
def _check_normal(self):
|
||||
# When the OP meets the following conditions, it is abnormal data, and use --skip_normal_tensors to retain the data in Excel:
|
||||
# 1. The number of OP outputs exceeds the indication range of int32
|
||||
# 2. The output data exceeds the representation range of fp16
|
||||
# 3. Nan or inf appears in fp16 output data
|
||||
# 4. The maximum value of fp32 is not equal to the maximum value of fp16
|
||||
# 5. The minimum value of fp32 is not equal to the minimum value of fp16
|
||||
if self.numel is not None and self.numel > np.iinfo(np.int32).max:
|
||||
self.is_normal = False
|
||||
return
|
||||
|
||||
check_list = [
|
||||
self.fp32_max_value,
|
||||
self.fp32_min_value,
|
||||
self.scaled_fp32_max_value,
|
||||
self.scaled_fp32_min_value,
|
||||
self.fp16_max_value,
|
||||
self.fp16_min_value,
|
||||
]
|
||||
|
||||
for value in check_list:
|
||||
if value is not None and is_infinite(value):
|
||||
self.is_normal = False
|
||||
return
|
||||
|
||||
if self.fp16_has_inf is not None and self.fp16_has_inf:
|
||||
self.is_normal = False
|
||||
return
|
||||
if self.fp16_has_nan is not None and self.fp16_has_nan:
|
||||
self.is_normal = False
|
||||
return
|
||||
|
||||
if (
|
||||
self.scaled_fp32_max_value is not None
|
||||
and self.fp16_max_value is not None
|
||||
and not is_allclose(self.fp16_max_value, self.scaled_fp32_max_value)
|
||||
):
|
||||
self.is_normal = False
|
||||
return
|
||||
if (
|
||||
self.scaled_fp32_min_value is not None
|
||||
and self.fp16_min_value is not None
|
||||
and not is_allclose(self.fp16_min_value, self.scaled_fp32_min_value)
|
||||
):
|
||||
self.is_normal = False
|
||||
return
|
||||
|
||||
|
||||
class ExcelWriter:
|
||||
def __init__(self, log_fp32_dir, log_fp16_dir, output_path):
|
||||
self.log_fp32_dir = log_fp32_dir
|
||||
self.log_fp16_dir = log_fp16_dir
|
||||
|
||||
try:
|
||||
import xlsxwriter as xlw
|
||||
except ImportError:
|
||||
print(
|
||||
"import xlsxwriter failed. please run 'pip install xlsxwriter==3.0.9' to install it"
|
||||
)
|
||||
|
||||
self.workbook = xlw.Workbook(output_path)
|
||||
self.title_format = self.workbook.add_format(
|
||||
{
|
||||
'bold': True,
|
||||
'border': 1,
|
||||
'font_color': 'black',
|
||||
'bg_color': '#6495ED',
|
||||
'align': 'center',
|
||||
}
|
||||
)
|
||||
self.tensor_name_format = self.workbook.add_format(
|
||||
{'bold': True, 'bg_color': '#F5F5F5'}
|
||||
)
|
||||
self.red_bg_cell_format = self.workbook.add_format(
|
||||
{'bold': True, 'bg_color': 'red'}
|
||||
)
|
||||
self.yellow_bg_cell_format = self.workbook.add_format(
|
||||
{'bold': True, 'bg_color': 'yellow'}
|
||||
)
|
||||
self.orange_bg_cell_format = self.workbook.add_format(
|
||||
{'bold': True, 'bg_color': 'orange'}
|
||||
)
|
||||
|
||||
def close(self):
|
||||
self.workbook.close()
|
||||
self.workbook = None
|
||||
|
||||
def _write_dtype(self, worksheet, value, row, col):
|
||||
if value is None:
|
||||
worksheet.write(row, col, "--")
|
||||
else:
|
||||
if value == "fp16":
|
||||
worksheet.write(row, col, value, self.yellow_bg_cell_format)
|
||||
else:
|
||||
worksheet.write(row, col, value)
|
||||
|
||||
def _write_tensor_name(self, worksheet, mp_tensor_info, row, col):
|
||||
tensor_name = mp_tensor_info.get_tensor_name()
|
||||
if (
|
||||
mp_tensor_info.fp32_tensor_name is not None
|
||||
and mp_tensor_info.fp16_tensor_name
|
||||
):
|
||||
worksheet.write(row, col, tensor_name, self.tensor_name_format)
|
||||
else:
|
||||
worksheet.write(row, col, tensor_name)
|
||||
|
||||
def _write_maxmin_value(
|
||||
self, worksheet, value, row, col, check_finite=True
|
||||
):
|
||||
if value is None:
|
||||
worksheet.write(row, col, "--")
|
||||
else:
|
||||
if abs(value) < 1e-5:
|
||||
value_str = f"{value:.6E}"
|
||||
else:
|
||||
value_str = f"{value:.6f}"
|
||||
if check_finite and is_infinite(value, np.float16):
|
||||
worksheet.write(row, col, value_str, self.red_bg_cell_format)
|
||||
else:
|
||||
worksheet.write(row, col, value_str)
|
||||
|
||||
def _write_tensor_num_zero(
|
||||
self, worksheet, value, row, col, check_finite=True
|
||||
):
|
||||
if value is None:
|
||||
worksheet.write(row, col, "--")
|
||||
else:
|
||||
value_str = f"{value:>10d}"
|
||||
worksheet.write(row, col, value_str)
|
||||
|
||||
def _write_infinite_status(self, worksheet, value, row, col):
|
||||
if value is None:
|
||||
worksheet.write(row, col, "--")
|
||||
else:
|
||||
if value == 1:
|
||||
worksheet.write(row, col, value, self.red_bg_cell_format)
|
||||
else:
|
||||
worksheet.write(row, col, value)
|
||||
|
||||
def _write_fp32divfp16_value(self, worksheet, value, row, col, loss_scale):
|
||||
def _in_range(value, scale=1):
|
||||
return value > scale * 0.95 and value < scale * 1.05
|
||||
|
||||
if value is None:
|
||||
worksheet.write(row, col, "--")
|
||||
else:
|
||||
value_str = f"{value:.6f}"
|
||||
if _in_range(value, scale=1) or _in_range(value, loss_scale):
|
||||
worksheet.write(row, col, value_str)
|
||||
else:
|
||||
worksheet.write(row, col, value_str, self.orange_bg_cell_format)
|
||||
|
||||
def _write_titles(self, worksheet, loss_scale, row):
|
||||
column_width_dict = {
|
||||
"op_type": 24,
|
||||
"tensor_name": 60,
|
||||
"numel": 10,
|
||||
"num_zero": 10,
|
||||
"infinite": 8,
|
||||
"dtype": 8,
|
||||
"max_value": 16,
|
||||
"min_value": 16,
|
||||
"mean_value": 16,
|
||||
"num_inf": 8,
|
||||
"num_nan": 8,
|
||||
}
|
||||
title_names = ["op_type", "tensor_name", "numel", "infinite"]
|
||||
if self.log_fp16_dir is None:
|
||||
# only fp32 values
|
||||
worksheet.merge_range("E1:H1", "fp32", self.title_format)
|
||||
worksheet.merge_range(
|
||||
"I1:J1", f"fp32 (scale={loss_scale})", self.title_format
|
||||
)
|
||||
title_names.extend(
|
||||
[
|
||||
"dtype",
|
||||
"max_value",
|
||||
"min_value",
|
||||
"mean_value",
|
||||
"max_value",
|
||||
"min_value",
|
||||
]
|
||||
)
|
||||
elif self.log_fp32_dir is None:
|
||||
# only fp16 values
|
||||
worksheet.merge_range(
|
||||
"E1:J1", f"fp16 (scale={loss_scale})", self.title_format
|
||||
)
|
||||
title_names.extend(
|
||||
[
|
||||
"dtype",
|
||||
"max_value",
|
||||
"min_value",
|
||||
"mean_value",
|
||||
"num_zero",
|
||||
"num_inf",
|
||||
"num_nan",
|
||||
]
|
||||
)
|
||||
else:
|
||||
# fp32 and fp16 values
|
||||
worksheet.merge_range("E1:H1", "fp32", self.title_format)
|
||||
worksheet.merge_range(
|
||||
"I1:N1", f"fp16 (scale={loss_scale})", self.title_format
|
||||
)
|
||||
worksheet.merge_range("O1:Q1", "fp16 / fp32", self.title_format)
|
||||
title_names.extend(
|
||||
[
|
||||
"dtype",
|
||||
"max_value",
|
||||
"min_value",
|
||||
"mean_value",
|
||||
"num_zero",
|
||||
"dtype",
|
||||
"max_value",
|
||||
"min_value",
|
||||
"mean_value",
|
||||
"num_zero",
|
||||
"num_inf",
|
||||
"num_nan",
|
||||
"max_value",
|
||||
"min_value",
|
||||
"mean_value",
|
||||
]
|
||||
)
|
||||
|
||||
for col in range(len(title_names)):
|
||||
col_char = chr(ord("A") + col)
|
||||
worksheet.set_column(
|
||||
col_char + ":" + col_char, column_width_dict[title_names[col]]
|
||||
)
|
||||
for col in range(len(title_names)):
|
||||
worksheet.write(row, col, title_names[col], self.title_format)
|
||||
|
||||
def add_worksheet(
|
||||
self, mp_tensor_info_list, sheetname, loss_scale, skip_normal_tensors
|
||||
):
|
||||
assert self.workbook is not None
|
||||
|
||||
worksheet = self.workbook.add_worksheet(sheetname)
|
||||
row = 1
|
||||
|
||||
self._write_titles(worksheet, loss_scale, row)
|
||||
row += 1
|
||||
|
||||
infinite_op_types = []
|
||||
for tensor_info in mp_tensor_info_list:
|
||||
if (
|
||||
not tensor_info.is_normal
|
||||
and tensor_info.op_type not in infinite_op_types
|
||||
):
|
||||
infinite_op_types.append(tensor_info.op_type)
|
||||
|
||||
if skip_normal_tensors and tensor_info.is_normal:
|
||||
continue
|
||||
|
||||
worksheet.write(row, 0, tensor_info.op_type)
|
||||
self._write_tensor_name(worksheet, tensor_info, row, 1)
|
||||
|
||||
if tensor_info.numel > np.iinfo(np.int32).max:
|
||||
worksheet.write(
|
||||
row, 2, tensor_info.numel, self.bad_value_format
|
||||
)
|
||||
else:
|
||||
worksheet.write(row, 2, tensor_info.numel)
|
||||
|
||||
if tensor_info.is_normal:
|
||||
worksheet.write(row, 3, "0")
|
||||
else:
|
||||
worksheet.write(row, 3, "1", self.red_bg_cell_format)
|
||||
|
||||
col = 4
|
||||
|
||||
if self.log_fp32_dir is not None:
|
||||
self._write_dtype(worksheet, tensor_info.fp32_dtype, row, col)
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.fp32_max_value, row, col + 1
|
||||
)
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.fp32_min_value, row, col + 2
|
||||
)
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.fp32_mean_value, row, col + 3
|
||||
)
|
||||
self._write_tensor_num_zero(
|
||||
worksheet, tensor_info.fp32_num_zero, row, col + 4
|
||||
)
|
||||
col += 5
|
||||
|
||||
if self.log_fp16_dir is None:
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.scaled_fp32_max_value, row, col
|
||||
)
|
||||
self._write_maxmin_value(
|
||||
worksheet,
|
||||
tensor_info.scaled_fp32_min_value,
|
||||
row,
|
||||
col + 1,
|
||||
)
|
||||
col += 2
|
||||
|
||||
if self.log_fp16_dir is not None:
|
||||
self._write_dtype(worksheet, tensor_info.fp16_dtype, row, col)
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.fp16_max_value, row, col + 1
|
||||
)
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.fp16_min_value, row, col + 2
|
||||
)
|
||||
self._write_maxmin_value(
|
||||
worksheet, tensor_info.fp16_mean_value, row, col + 3
|
||||
)
|
||||
self._write_tensor_num_zero(
|
||||
worksheet, tensor_info.fp32_num_zero, row, col + 4
|
||||
)
|
||||
col += 5
|
||||
|
||||
self._write_infinite_status(
|
||||
worksheet, tensor_info.fp16_has_inf, row, col
|
||||
)
|
||||
self._write_infinite_status(
|
||||
worksheet, tensor_info.fp16_has_nan, row, col + 1
|
||||
)
|
||||
col += 2
|
||||
|
||||
if self.log_fp32_dir is not None and self.log_fp16_dir is not None:
|
||||
self._write_fp32divfp16_value(
|
||||
worksheet,
|
||||
tensor_info.fp32_div_fp16_max_value,
|
||||
row,
|
||||
col,
|
||||
loss_scale,
|
||||
)
|
||||
self._write_fp32divfp16_value(
|
||||
worksheet,
|
||||
tensor_info.fp32_div_fp16_min_value,
|
||||
row,
|
||||
col + 1,
|
||||
loss_scale,
|
||||
)
|
||||
self._write_fp32divfp16_value(
|
||||
worksheet,
|
||||
tensor_info.fp32_div_fp16_mean_value,
|
||||
row,
|
||||
col + 2,
|
||||
loss_scale,
|
||||
)
|
||||
col += 3
|
||||
|
||||
row += 1
|
||||
|
||||
print(f"-- OP Types produce infinite outputs: {infinite_op_types}")
|
||||
|
||||
|
||||
def parse_lines(lines, specified_op_list=None):
|
||||
tensor_info_list = []
|
||||
|
||||
for i in range(len(lines)):
|
||||
if i % 10 == 0:
|
||||
print(
|
||||
f"-- Processing {i:-8d} / {len(lines):-8d} line",
|
||||
end="\r",
|
||||
)
|
||||
line = lines[i]
|
||||
if "[PRECISION]" in line:
|
||||
tensor_info = TensorInfo()
|
||||
tensor_info.init_from_string(line)
|
||||
if (
|
||||
tensor_info.tensor_name is not None
|
||||
and tensor_info.tensor_name != ""
|
||||
):
|
||||
has_tensor_name = True
|
||||
if (
|
||||
specified_op_list is None
|
||||
or tensor_info.op_type in specified_op_list
|
||||
):
|
||||
tensor_info_list.append(tensor_info)
|
||||
# print(tensor_info)
|
||||
return tensor_info_list
|
||||
|
||||
|
||||
def parse_log(log_dir, filename, specified_op_list=None):
|
||||
if log_dir is None or filename is None:
|
||||
return None
|
||||
|
||||
complete_filename = log_dir + "/" + filename
|
||||
tensor_info_list = None
|
||||
has_tensor_name = False
|
||||
|
||||
try:
|
||||
with open(complete_filename, 'r') as f:
|
||||
lines = f.readlines()
|
||||
tensor_info_list = parse_lines(lines, specified_op_list)
|
||||
except FileNotFoundError:
|
||||
print("the file ", complete_filename, "is not found")
|
||||
return None, has_tensor_name
|
||||
return tensor_info_list, has_tensor_name
|
||||
|
||||
|
||||
def merge_tensor_info_list(
|
||||
fp32_tensor_info_list, fp16_tensor_info_list, grad_scale
|
||||
):
|
||||
mp_tensor_info_list = []
|
||||
if fp16_tensor_info_list is not None:
|
||||
fp32_tensor_info_dict = {}
|
||||
fp32_write_count = {}
|
||||
if fp32_tensor_info_list is not None:
|
||||
for tensor_info in fp32_tensor_info_list:
|
||||
tensor_info_key = tensor_info.key()
|
||||
count = fp32_write_count.get(tensor_info_key, 0)
|
||||
fp32_write_count[tensor_info_key] = count + 1
|
||||
fp32_tensor_info_dict[tensor_info_key + "#" + str(count)] = (
|
||||
tensor_info
|
||||
)
|
||||
|
||||
fp32_read_count = {}
|
||||
for i in range(len(fp16_tensor_info_list)):
|
||||
if i % 10 == 0:
|
||||
print(
|
||||
f"-- Processing {i:-8d} / {len(fp16_tensor_info_list):-8d} FP16 Tensor Info",
|
||||
end="\r",
|
||||
)
|
||||
fp16_tensor_info = fp16_tensor_info_list[i]
|
||||
fp32_tensor_info_key = (
|
||||
fp16_tensor_info.key()
|
||||
.replace(".cast_fp16", "")
|
||||
.replace(".cast_fp32", "")
|
||||
)
|
||||
count = fp32_read_count.get(fp32_tensor_info_key, 0)
|
||||
fp32_tensor_info = fp32_tensor_info_dict.get(
|
||||
fp32_tensor_info_key + "#" + str(count), None
|
||||
)
|
||||
if fp32_tensor_info is not None:
|
||||
fp32_read_count[fp32_tensor_info_key] = count + 1
|
||||
mp_tensor_info = MixedPrecisionTensorInfo(
|
||||
fp32_tensor_info, fp16_tensor_info, count, grad_scale
|
||||
)
|
||||
mp_tensor_info_list.append(mp_tensor_info)
|
||||
# print(mp_tensor_info)
|
||||
elif fp32_tensor_info_list is not None:
|
||||
fp32_count = {}
|
||||
for i in range(len(fp32_tensor_info_list)):
|
||||
if i % 10 == 0:
|
||||
print(
|
||||
f"-- Processing {i:-8d} / {len(fp32_tensor_info_list):-8d} FP32 Tensor Info",
|
||||
end="\r",
|
||||
)
|
||||
tensor_info = fp32_tensor_info_list[i]
|
||||
tensor_info_key = tensor_info.key()
|
||||
count = fp32_count.get(tensor_info_key, 0)
|
||||
fp32_count[tensor_info_key] = count + 1
|
||||
mp_tensor_info = MixedPrecisionTensorInfo(
|
||||
tensor_info, None, count, grad_scale
|
||||
)
|
||||
mp_tensor_info_list.append(mp_tensor_info)
|
||||
|
||||
return mp_tensor_info_list
|
||||
|
||||
|
||||
def compare_accuracy(
|
||||
dump_path,
|
||||
another_dump_path,
|
||||
output_filename,
|
||||
loss_scale=1,
|
||||
dump_all_tensors=False,
|
||||
):
|
||||
excel_writer = ExcelWriter(dump_path, another_dump_path, output_filename)
|
||||
grad_scale = loss_scale
|
||||
workerlog_filenames = []
|
||||
filenames = os.listdir(dump_path)
|
||||
for name in filenames:
|
||||
if "worker_" in name:
|
||||
workerlog_filenames.append(name)
|
||||
print(
|
||||
f"-- There are {len(workerlog_filenames)} workerlogs under {dump_path}: {workerlog_filenames}"
|
||||
)
|
||||
|
||||
for filename in sorted(workerlog_filenames):
|
||||
print(f"-- [Step 1/4] Parsing FP32 logs under {dump_path}/{filename}")
|
||||
fp32_tensor_info_list, fp32_has_tensor_name = parse_log(
|
||||
dump_path, filename, None
|
||||
)
|
||||
print(
|
||||
f"-- [Step 2/4] Parsing FP16 logs under {another_dump_path}/{filename}"
|
||||
)
|
||||
fp16_tensor_info_list, fp16_has_tensor_name = parse_log(
|
||||
another_dump_path, filename, None
|
||||
)
|
||||
|
||||
print(f"-- [Step 3/4] Merge FP32 and FP16 tensor info for {filename}")
|
||||
mp_tensor_info_list = merge_tensor_info_list(
|
||||
fp32_tensor_info_list, fp16_tensor_info_list, grad_scale
|
||||
)
|
||||
print(
|
||||
f"-- [Step 4/4] Add worksheet for mixed precision tensor info of {filename}"
|
||||
)
|
||||
excel_writer.add_worksheet(
|
||||
mp_tensor_info_list,
|
||||
filename,
|
||||
loss_scale,
|
||||
False,
|
||||
)
|
||||
|
||||
print(f"-- Write to {output_filename}")
|
||||
|
||||
print()
|
||||
excel_writer.close()
|
||||
@@ -0,0 +1,139 @@
|
||||
# Copyright (c) 2023 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.
|
||||
|
||||
# The set of ops that support fp16 and bf16 calculation and are considered numerically-
|
||||
# safe and performance-critical. These ops are always converted to fp16 or bf16.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
WHITE_LIST = {
|
||||
'conv2d',
|
||||
'einsum',
|
||||
'matmul',
|
||||
'matmul_v2',
|
||||
'linear_v2',
|
||||
'max_pool2d_with_index',
|
||||
'mul',
|
||||
'fused_gemm_epilogue',
|
||||
"fused_rotary_position_embedding",
|
||||
"flash_attn",
|
||||
}
|
||||
|
||||
# The set of ops that support fp16, and bf16 was unsupported.
|
||||
ONLY_FP16_WHITE_LIST = {
|
||||
'fake_quantize_dequantize_abs_max',
|
||||
'fake_quantize_dequantize_moving_average_abs_max',
|
||||
'fused_attention',
|
||||
'fused_feedforward',
|
||||
}
|
||||
|
||||
FP16_WHITE_LIST = WHITE_LIST | ONLY_FP16_WHITE_LIST
|
||||
|
||||
# The set of ops that support fp16 calculation and are considered numerically-
|
||||
# dangerous and whose effects may also be observed in downstream ops.
|
||||
FP16_BLACK_LIST = {
|
||||
'tan',
|
||||
'acos',
|
||||
'asin',
|
||||
'sinh',
|
||||
'cosh',
|
||||
'atanh',
|
||||
'tanh_shrink',
|
||||
'erfinv',
|
||||
'exp',
|
||||
'expm1',
|
||||
'log',
|
||||
'log10',
|
||||
'log2',
|
||||
'reciprocal',
|
||||
'rsqrt',
|
||||
'pow',
|
||||
'square',
|
||||
'reduce_sum',
|
||||
'mean',
|
||||
'reduce_mean',
|
||||
'reduce_prod',
|
||||
'cumprod',
|
||||
'cumsum',
|
||||
'dist',
|
||||
'pnorm',
|
||||
'frobenius_norm',
|
||||
'renorm',
|
||||
'group_norm',
|
||||
'layer_norm',
|
||||
'softmax',
|
||||
'softmin',
|
||||
'softplus',
|
||||
'log_softmax',
|
||||
'softmax_with_cross_entropy',
|
||||
'sigmoid_cross_entropy_with_logits',
|
||||
'c_softmax_with_cross_entropy',
|
||||
'c_softmax_with_multi_label_cross_entropy',
|
||||
'cross_entropy',
|
||||
'cross_entropy2',
|
||||
'nll_loss',
|
||||
'huber_loss',
|
||||
'triplet_margin_loss',
|
||||
'log_loss',
|
||||
'hsigmoid_loss',
|
||||
'margin_cross_entropy',
|
||||
}
|
||||
|
||||
# FP16/BF16 performance of grad op is worse than that of FP32. Use FP32 by default.
|
||||
EXTRA_BLACK_LIST = {
|
||||
'linear_interp_v2',
|
||||
'nearest_interp_v2',
|
||||
'bilinear_interp_v2',
|
||||
'bicubic_interp_v2',
|
||||
'trilinear_interp_v2',
|
||||
'lookup_table',
|
||||
'lookup_table_v2',
|
||||
'scatter',
|
||||
}
|
||||
|
||||
BF16_WHITE_LIST = WHITE_LIST
|
||||
BF16_BLACK_LIST = FP16_BLACK_LIST
|
||||
|
||||
|
||||
# At OD level, ops in WHITE_LIST will use FP16/BF16 and the others will use FP32.
|
||||
def white_list() -> dict[str, dict[str, set[str]]]:
|
||||
white_list = {
|
||||
"float16": {
|
||||
"OD": FP16_WHITE_LIST,
|
||||
"O1": FP16_WHITE_LIST,
|
||||
"O2": FP16_WHITE_LIST,
|
||||
},
|
||||
"bfloat16": {
|
||||
"OD": BF16_WHITE_LIST,
|
||||
"O1": BF16_WHITE_LIST,
|
||||
"O2": BF16_WHITE_LIST,
|
||||
},
|
||||
}
|
||||
return white_list
|
||||
|
||||
|
||||
def black_list() -> dict[str, dict[str, set[str]]]:
|
||||
black_list = {
|
||||
"float16": {
|
||||
"OD": set(),
|
||||
"O1": FP16_BLACK_LIST | EXTRA_BLACK_LIST,
|
||||
"O2": EXTRA_BLACK_LIST,
|
||||
},
|
||||
"bfloat16": {
|
||||
"OD": set(),
|
||||
"O1": BF16_BLACK_LIST | EXTRA_BLACK_LIST,
|
||||
"O2": EXTRA_BLACK_LIST,
|
||||
},
|
||||
}
|
||||
return black_list
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,731 @@
|
||||
# Copyright (c) 2023 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
|
||||
|
||||
import contextlib
|
||||
import random
|
||||
from enum import Enum
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
import paddle
|
||||
from paddle import _C_ops
|
||||
from paddle.base import core
|
||||
|
||||
from ..framework import LayerHelper, in_dynamic_or_pir_mode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Generator, Sequence
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
_InputT = ParamSpec("_InputT")
|
||||
_RetT = TypeVar("_RetT")
|
||||
__all__ = [
|
||||
"DebugMode",
|
||||
"TensorCheckerConfig",
|
||||
"check_numerics",
|
||||
"enable_operator_stats_collection",
|
||||
"disable_operator_stats_collection",
|
||||
"collect_operator_stats",
|
||||
"enable_tensor_checker",
|
||||
"disable_tensor_checker",
|
||||
"compare_accuracy",
|
||||
"check_layer_numerics",
|
||||
]
|
||||
|
||||
|
||||
class DebugMode(Enum):
|
||||
"""
|
||||
The DebugMode is a feature that helps to present the state of the TensorCheckerConfig. Each DebugMode has a specific meaning, which is explained below:
|
||||
|
||||
- DebugMode.CHECK_NAN_INF_AND_ABORT: This mode prints or saves information about Tensors that contain NaN/Inf and interrupts the program.
|
||||
|
||||
- DebugMode.CHECK_NAN_INF: This mode prints or saves critical information about Tensors that contain NaN/Inf but allows the program to continue running.
|
||||
|
||||
- DebugMode.CHECK_ALL_FOR_OVERFLOW: This mode checks the output of the FP32 operator and prints or saves information about key Tensors that exceed the FP16 representation range, such as overflow or underflow.
|
||||
|
||||
- DebugMode.CHECK_ALL: This mode prints or saves output Tensor key information for all operators.
|
||||
|
||||
"""
|
||||
|
||||
CHECK_NAN_INF_AND_ABORT = 0
|
||||
CHECK_NAN_INF = 1
|
||||
CHECK_ALL_FOR_OVERFLOW = 2
|
||||
CHECK_ALL = 3
|
||||
# CHECK_ALL_AND_ABORT = 4
|
||||
# DUMP_ALL = 5
|
||||
|
||||
|
||||
def check_layer_numerics(
|
||||
func: Callable[_InputT, _RetT],
|
||||
) -> Callable[_InputT, _RetT]:
|
||||
"""
|
||||
This decorator is used to check the numerical values of the layer's input and output data.
|
||||
|
||||
Args:
|
||||
func (callable): The function to be decorated.
|
||||
|
||||
Returns:
|
||||
callable: The decorated function.
|
||||
|
||||
Raises:
|
||||
None.
|
||||
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> class MyLayer(paddle.nn.Layer):
|
||||
... def __init__(self, dtype):
|
||||
... super().__init__()
|
||||
... self._w = self.create_parameter([2, 3], dtype=dtype)
|
||||
... self._b = self.create_parameter([2, 3], dtype=dtype)
|
||||
...
|
||||
... @paddle.amp.debugging.check_layer_numerics
|
||||
... def forward(self, x):
|
||||
... # return 1/x * self._w + self._b open it you will see the error log
|
||||
... return x @ self._w + self._b
|
||||
>>> dtype = 'float32'
|
||||
>>> x = paddle.rand([10, 2, 2], dtype=dtype) # type: ignore[call-overload]
|
||||
>>> model = MyLayer(dtype)
|
||||
>>> x[0] = float(0)
|
||||
>>> loss = model(x)
|
||||
>>> adam = paddle.optimizer.Adam(parameters=model.parameters())
|
||||
>>> loss.backward()
|
||||
>>> adam.step()
|
||||
|
||||
>>> # error log
|
||||
>>> # [PRECISION] [ERROR] in [device=gpu:0, op=divide, tensor=, dtype=fp32], numel=40, num_nan=0, num_inf=4, num_zero=0, max=inf, min=1.048930e+00, mean=inf
|
||||
>>> # Traceback (most recent call last):
|
||||
>>> # File "tmp.py", line 16, in <module>
|
||||
>>> # loss = model(x)
|
||||
>>> # File "/paddle/nn/layer/layers.py", line 1254, in __call__
|
||||
>>> # return self.forward(*inputs, **kwargs)
|
||||
>>> # File "/paddle/amp/debugging.py", line 116, in wrapper
|
||||
>>> # out_data = func(self, *modified_args, **kwargs)
|
||||
>>> # File "test.py", line 10, in forward
|
||||
>>> # return 1/x * self._w+ self._b
|
||||
>>> # RuntimeError: (PreconditionNotMet) There are NAN or INF (num_nan=0, num_inf=4, num_zero=0) in [device=gpu:0, op=divide, tensor=, dtype=fp32].
|
||||
"""
|
||||
|
||||
def wrapper(self, *args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT:
|
||||
if args:
|
||||
# Set temp data and temp.gradient = False
|
||||
start_data = args[0]
|
||||
if not isinstance(start_data, paddle.Tensor):
|
||||
raise RuntimeError("First input of this layer must be tensor.")
|
||||
start_data.stop_gradient = False
|
||||
modified_args = list(args) # Convert args to a mutable list
|
||||
# Set FLAGS_check_nan_inf = 1
|
||||
modified_args[0] = _C_ops.enable_check_model_nan_inf(start_data, 1)
|
||||
# Call the forward function
|
||||
out_data = func(self, *modified_args, **kwargs)
|
||||
# Set FLAGS_check_nan_inf = 0
|
||||
out = _C_ops.disable_check_model_nan_inf(out_data, 0)
|
||||
return out
|
||||
else:
|
||||
raise RuntimeError("No elements found in args.")
|
||||
out = func(self, *args, **kwargs)
|
||||
return out
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def set_checked_op_list(checked_op_list: Sequence[str] | None) -> None:
|
||||
# check checked_op_list
|
||||
if checked_op_list is not None:
|
||||
if isinstance(checked_op_list, (list, tuple)):
|
||||
check_op_list = ",".join(value for value in checked_op_list)
|
||||
paddle.base.core.set_checked_op_list(check_op_list)
|
||||
else:
|
||||
raise ValueError("checked_op_list must be list or tuple")
|
||||
|
||||
|
||||
def set_skipped_op_list(skipped_op_list: Sequence[str] | None) -> None:
|
||||
# check skipped_op_list
|
||||
if skipped_op_list is not None:
|
||||
if isinstance(skipped_op_list, (list, tuple)):
|
||||
skip_op_list = ",".join(value for value in skipped_op_list)
|
||||
paddle.base.core.set_skipped_op_list(skip_op_list)
|
||||
else:
|
||||
raise ValueError("skipped_op_list must be list or tuple")
|
||||
|
||||
|
||||
class TensorCheckerConfig:
|
||||
"""
|
||||
The purpose of this class is to collect the configuration for checking NaN and Inf values in the tensors of a module or operator. It takes the following arguments:
|
||||
|
||||
Args:
|
||||
enable(bool): Indicating whether to enable the detection of NaN and Inf values in tensors. The default value is False, which means that these tools will not be used.
|
||||
|
||||
debug_mode(DebugMode, optional): A parameter that determines the type of debugging to be used. Default is DebugMode.CHECK_NAN_INF_AND_ABORT.
|
||||
|
||||
output_dir(string|None, optional): The path to store collected data. If this parameter is set to None, the data will be printed to the terminal. Default is None.
|
||||
|
||||
checked_op_list(list|tuple|None, optional): Specifies a list of operators that need to be checked during program execution, for example, checked_op_list=['elementwise_add', 'conv2d'], indicating that the output results of elementwise_add and conv2d should be checked for nan/inf during program execution. Default is None.
|
||||
|
||||
skipped_op_list(list|tuple|None, optional): Specifies a list of operators that do not need to be checked during program execution, for example, skipped_op_list=['elementwise_add', 'conv2d'], indicating that the output results of elementwise_add and conv2d should not be checked for nan/inf during program execution. None is None.
|
||||
|
||||
debug_step(list|tuple|None, optional): A list or tuple used primarily for nan/inf checking during model training. For example, debug_step=[1,5] indicates that nan/inf checking should only be performed on model training iterations 1 to 5. Default is None.
|
||||
|
||||
stack_height_limit(int, optional): An integer value specifying the maximum depth of the call stack. This feature supports printing the call stack at the error location. Currently, only enabling or disabling call stack printing is supported. If you want to print the corresponding C++ call stack when NaN is detected in GPU Kernel, set stack_height_limit to 1, otherwise set it to 0. Default is 1.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> checker_config = paddle.amp.debugging.TensorCheckerConfig(
|
||||
... enable=True, debug_mode=paddle.amp.debugging.DebugMode.CHECK_NAN_INF
|
||||
... )
|
||||
>>> paddle.amp.debugging.enable_tensor_checker(checker_config)
|
||||
|
||||
>>> x = paddle.to_tensor([1, 0, 3], place=paddle.CPUPlace(), dtype='float32', stop_gradient=False)
|
||||
>>> y = paddle.to_tensor([0.2, 0, 0.5], place=paddle.CPUPlace(), dtype='float32')
|
||||
>>> res = paddle.pow(x, y)
|
||||
>>> paddle.autograd.backward(res, retain_graph=True)
|
||||
>>> paddle.amp.debugging.disable_tensor_checker()
|
||||
|
||||
>>> # [PRECISION] [ERROR] in [device=cpu, op=elementwise_pow_grad, tensor=, dtype=fp32], numel=3, num_nan=1, num_inf=0, num_zero=0, max=2.886751e-01, min=2.000000e-01, mean=-nan
|
||||
|
||||
>>> # when DebugMode.CHECK_NAN_INF_AND_ABORT and stack_height_limit = 1
|
||||
>>> # Traceback (most recent call last):
|
||||
>>> # res = paddle.pow(x, y)
|
||||
>>> # File "/usr/local/lib/python3.8/dist-packages/paddle/tensor/math.py", line 447, in pow
|
||||
>>> # return _C_ops.elementwise_pow(x, y)
|
||||
|
||||
"""
|
||||
|
||||
# For module debugging
|
||||
current_step_id: int = 0
|
||||
|
||||
enable: bool
|
||||
debug_mode: DebugMode
|
||||
output_dir: str | None
|
||||
checked_op_list: Sequence[str] | None
|
||||
skipped_op_list: Sequence[str] | None
|
||||
debug_step: Sequence[int] | None
|
||||
stack_height_limit: int
|
||||
start_step: int | None
|
||||
end_step: int | None
|
||||
seed: int
|
||||
initial_seed: int
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
enable: bool,
|
||||
debug_mode: DebugMode = DebugMode.CHECK_NAN_INF_AND_ABORT,
|
||||
output_dir: str | None = None,
|
||||
checked_op_list: Sequence[str] | None = None,
|
||||
skipped_op_list: Sequence[str] | None = None,
|
||||
debug_step: Sequence[int] | None = None,
|
||||
stack_height_limit: int = 1,
|
||||
):
|
||||
self.enable = enable
|
||||
self.debug_mode = debug_mode
|
||||
self.output_dir = output_dir
|
||||
|
||||
self.checked_op_list = checked_op_list
|
||||
self.skipped_op_list = skipped_op_list
|
||||
|
||||
self.debug_step = debug_step
|
||||
self.stack_height_limit = stack_height_limit
|
||||
|
||||
self.start_step = None
|
||||
self.end_step = None
|
||||
|
||||
self.seed = 123
|
||||
self.initial_seed = 123
|
||||
|
||||
# check debug_step
|
||||
if debug_step is not None:
|
||||
if isinstance(debug_step, (tuple, list)):
|
||||
assert (
|
||||
len(self.debug_step) == 2
|
||||
and self.debug_step[1] > self.debug_step[0]
|
||||
)
|
||||
self.start_step, self.end_step = self.debug_step
|
||||
self.start_step = max(self.start_step, 0)
|
||||
else:
|
||||
raise ValueError("debug_step must be list or tuple")
|
||||
|
||||
if core.is_compiled_with_cuda():
|
||||
for i in range(core.get_cuda_device_count()):
|
||||
self.initial_seed = core.default_cuda_generator(
|
||||
i
|
||||
).initial_seed()
|
||||
elif core.is_compiled_with_xpu():
|
||||
for i in range(core.get_xpu_device_count()):
|
||||
self.initial_seed = core.default_xpu_generator(i).initial_seed()
|
||||
|
||||
self.initial_seed = core.default_cpu_generator().initial_seed()
|
||||
|
||||
# check debug_mode
|
||||
if self.debug_mode.name not in DebugMode.__members__:
|
||||
raise ValueError(
|
||||
"debug_mode in DebugMode",
|
||||
self.debug_mode,
|
||||
DebugMode.__members__,
|
||||
)
|
||||
|
||||
set_checked_op_list(self.checked_op_list)
|
||||
|
||||
set_skipped_op_list(self.skipped_op_list)
|
||||
|
||||
if self.enable:
|
||||
self._set_seed(self.enable)
|
||||
|
||||
def _set_seed(self, flag: int) -> None:
|
||||
if self.initial_seed != self.seed:
|
||||
self.seed = self.initial_seed
|
||||
|
||||
if self.seed > np.iinfo(np.uint32).max or self.seed < 0:
|
||||
print("[Warning: Seed must be between 0 and 2**32 - 1")
|
||||
self.seed = 123
|
||||
|
||||
# get random seed
|
||||
paddle.seed(self.seed)
|
||||
np.random.seed(self.seed)
|
||||
random.seed(self.seed)
|
||||
|
||||
# info
|
||||
print("AMP Debugging TensorCheckerConfig: seed ", self.seed)
|
||||
|
||||
# set cudnn and cpu
|
||||
if core.is_compiled_with_cuda():
|
||||
paddle.set_flags({"FLAGS_cudnn_deterministic": flag})
|
||||
print(
|
||||
"AMP Debugging TensorCheckerConfig: FLAGS_cudnn_deterministic is ",
|
||||
flag,
|
||||
)
|
||||
|
||||
def _set_env(self, check_flag: int) -> None:
|
||||
paddle.set_flags({"FLAGS_check_nan_inf": check_flag})
|
||||
if check_flag:
|
||||
# set debug level
|
||||
paddle.set_flags(
|
||||
{"FLAGS_check_nan_inf_level": self.debug_mode.value}
|
||||
)
|
||||
|
||||
# set output_dir
|
||||
if self.output_dir is not None:
|
||||
paddle.base.core.set_nan_inf_debug_path(self.output_dir)
|
||||
|
||||
# set stack_height_limit
|
||||
if isinstance(self.stack_height_limit, (int)):
|
||||
paddle.base.core.set_nan_inf_stack_limit(
|
||||
self.stack_height_limit
|
||||
)
|
||||
else:
|
||||
raise ValueError("stack_height_limit must be int")
|
||||
|
||||
def update_and_check_step_id(self) -> bool:
|
||||
if self.enable:
|
||||
if self.start_step is not None and self.end_step is not None:
|
||||
if (
|
||||
self.start_step > TensorCheckerConfig.current_step_id
|
||||
or TensorCheckerConfig.current_step_id >= self.end_step
|
||||
):
|
||||
return False
|
||||
else:
|
||||
TensorCheckerConfig.current_step_id += 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def start_check_nan_inf(self) -> None:
|
||||
if self.enable:
|
||||
self._set_env(self.enable)
|
||||
|
||||
def stop_check_nan_inf(self) -> None:
|
||||
self._set_env(False)
|
||||
|
||||
|
||||
def check_numerics(
|
||||
tensor: Tensor,
|
||||
op_type: str,
|
||||
var_name: str,
|
||||
debug_mode: DebugMode = DebugMode.CHECK_NAN_INF_AND_ABORT,
|
||||
) -> tuple[Tensor, Tensor]:
|
||||
"""
|
||||
This function is used to debugging a tensor, finding the number of NaNs, Infs and zeros in the tensor.
|
||||
|
||||
Args:
|
||||
tensor(Tensor): The target tensor to check.
|
||||
op_type(str): The OP or API name which produce the target tensor.
|
||||
var_name(str): The name of target tensor.
|
||||
debug_mode(paddle.amp.debugging.DebugMode, optional): The mode of debugging to be used. Default is DebugMode.CHECK_NAN_INF_AND_ABORT.
|
||||
|
||||
Returns:
|
||||
(Tensor, Tensor): A tuple of tensors containing
|
||||
|
||||
- **stats** (Tensor): Returns the number of NaNs, Infs and zeros of input tensor. The shape is [3] and dtype is int64.
|
||||
- **values** (Tensor): Returns the maximum, minimum and mean value of input tensor. The shape is [3] and dtype is float.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> checker_config = paddle.amp.debugging.TensorCheckerConfig(
|
||||
... enable=True, debug_mode=paddle.amp.debugging.DebugMode.CHECK_NAN_INF
|
||||
... )
|
||||
|
||||
>>> x = paddle.to_tensor([1, 0, 3], place=paddle.CPUPlace(), dtype='float32')
|
||||
>>> y = paddle.to_tensor([0.2, 0, 0.5], place=paddle.CPUPlace(), dtype='float32')
|
||||
>>> res = paddle.pow(x, y)
|
||||
>>> paddle.amp.debugging.check_numerics(res, "pow", "res")
|
||||
|
||||
"""
|
||||
stack_height_limit = -1
|
||||
output_dir = ""
|
||||
|
||||
if in_dynamic_or_pir_mode():
|
||||
return _C_ops.check_numerics(
|
||||
tensor,
|
||||
op_type,
|
||||
var_name,
|
||||
debug_mode.value,
|
||||
stack_height_limit,
|
||||
output_dir,
|
||||
)
|
||||
|
||||
helper = LayerHelper("check_numerics", **locals())
|
||||
|
||||
stats = helper.create_variable_for_type_inference(dtype="int64")
|
||||
values = helper.create_variable_for_type_inference(dtype="float")
|
||||
|
||||
helper.append_op(
|
||||
type='check_numerics',
|
||||
inputs={
|
||||
'tensor': tensor,
|
||||
},
|
||||
attrs={
|
||||
'op_type': op_type,
|
||||
'var_name': var_name,
|
||||
'check_nan_inf_level': debug_mode.value,
|
||||
'stack_height_limit': stack_height_limit,
|
||||
'output_dir': output_dir,
|
||||
},
|
||||
outputs={'stats': [stats], 'values': [values]},
|
||||
)
|
||||
return stats, values
|
||||
|
||||
|
||||
def _get_operator_stats_flag() -> Any:
|
||||
flags = paddle.get_flags(["FLAGS_low_precision_op_list"])
|
||||
return flags["FLAGS_low_precision_op_list"]
|
||||
|
||||
|
||||
def _print_operator_stats(op_count_dict: dict[str, str | list[int]]) -> None:
|
||||
"""
|
||||
Parse and print the stats of operators, mainly including the calls of
|
||||
dtypes such as different fp32, fp16, bf16 and others.
|
||||
|
||||
Args:
|
||||
op_count_dict(dict): a dict to record the number of calls for different
|
||||
operator and dtype. An example is
|
||||
{'conv2d': '1,0,0,0', 'elementwise_add': '1,0,0,0'} or
|
||||
{'conv2d': [1, 0, 0, 0], 'elementwise_add': [1, 0, 0, 0]}.
|
||||
"""
|
||||
print("<{:-^120}>".format(" op list "))
|
||||
total_ops = 0
|
||||
print(
|
||||
"<{:-^40}".format(" Op Name "),
|
||||
"|",
|
||||
"{:-^17}".format(" FP16 Calls "),
|
||||
"|",
|
||||
"{:-^17}".format(" BF16 Calls "),
|
||||
"|",
|
||||
"{:-^17}".format(" FP32 Calls"),
|
||||
"|",
|
||||
"{:-^17}>".format(" Other Calls "),
|
||||
)
|
||||
if op_count_dict is not None and isinstance(op_count_dict, dict):
|
||||
for op_type in sorted(op_count_dict):
|
||||
# fp16, bf16, fp32, other
|
||||
value = op_count_dict[op_type]
|
||||
if isinstance(value, list):
|
||||
called = value
|
||||
elif isinstance(value, str):
|
||||
called = value.split(",")
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Input {value} is expected to be a list of str, but received {type(value)}."
|
||||
)
|
||||
print(
|
||||
f" {op_type:<40}| {called[0]:<17}| {called[1]:<17}| {called[2]:<17}| {called[3]:<17}"
|
||||
)
|
||||
total_ops += 1
|
||||
print("<{:-^120}>\n".format(" op count: " + str(total_ops) + " "))
|
||||
|
||||
|
||||
def enable_operator_stats_collection() -> None:
|
||||
"""
|
||||
Enable to collect the number of operators for different data types.
|
||||
The statistical data are categorized according to four data types, namely
|
||||
float32, float16, bfloat16 and others. This function is used in pair with
|
||||
the corresponding disable function.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +REQUIRES(env:GPU)
|
||||
>>> import paddle
|
||||
>>> paddle.device.set_device('gpu')
|
||||
|
||||
>>> conv = paddle.nn.Conv2D(3, 2, 3)
|
||||
>>> x = paddle.rand([10, 3, 32, 32])
|
||||
|
||||
>>> paddle.amp.debugging.enable_operator_stats_collection()
|
||||
>>> # AMP list including cast, conv2d, elementwise_add, reshape
|
||||
>>> with paddle.amp.auto_cast(enable=True, level='O2'):
|
||||
... out = conv(x)
|
||||
>>> # Print to the standard output.
|
||||
>>> paddle.amp.debugging.disable_operator_stats_collection()
|
||||
>>> # <------------------------------------------------------- op list -------------------------------------------------------->
|
||||
>>> # <--------------- Op Name ---------------- | -- FP16 Calls --- | -- BF16 Calls --- | --- FP32 Calls--- | -- Other Calls -->
|
||||
>>> # cast | 1 | 0 | 2 | 0
|
||||
>>> # conv2d | 1 | 0 | 0 | 0
|
||||
>>> # elementwise_add | 0 | 0 | 1 | 0
|
||||
>>> # reshape | 0 | 0 | 1 | 0
|
||||
>>> # <----------------------------------------------------- op count: 4 ------------------------------------------------------>
|
||||
|
||||
"""
|
||||
# Clear the previous stats.
|
||||
paddle.base.core.clear_low_precision_op_list()
|
||||
paddle.set_flags({'FLAGS_low_precision_op_list': 1})
|
||||
|
||||
|
||||
def disable_operator_stats_collection() -> None:
|
||||
"""
|
||||
Disable the collection the number of operators for different data types.
|
||||
This function is used in pair with the corresponding enable function.
|
||||
The statistical data are categorized according to four data types, namely
|
||||
float32, float16, bfloat16 and others, and will be printed after the
|
||||
function call.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> conv = paddle.nn.Conv2D(3, 2, 3)
|
||||
>>> x = paddle.rand([10, 3, 32, 32])
|
||||
|
||||
>>> paddle.amp.debugging.enable_operator_stats_collection()
|
||||
>>> # AMP list including cast, conv2d, elementwise_add, reshape
|
||||
>>> with paddle.amp.auto_cast(enable=True, level='O2'):
|
||||
... out = conv(x)
|
||||
>>> # Print to the standard output.
|
||||
>>> paddle.amp.debugging.disable_operator_stats_collection()
|
||||
>>> # <------------------------------------------------------- op list -------------------------------------------------------->
|
||||
>>> # <--------------- Op Name ---------------- | -- FP16 Calls --- | -- BF16 Calls --- | --- FP32 Calls--- | -- Other Calls -->
|
||||
>>> # cast | 1 | 0 | 2 | 0
|
||||
>>> # conv2d | 1 | 0 | 0 | 0
|
||||
>>> # elementwise_add | 0 | 0 | 1 | 0
|
||||
>>> # reshape | 0 | 0 | 1 | 0
|
||||
>>> # <----------------------------------------------------- op count: 4 ------------------------------------------------------>
|
||||
|
||||
"""
|
||||
if not _get_operator_stats_flag():
|
||||
return
|
||||
|
||||
op_count_dict = paddle.base.core.get_low_precision_op_list()
|
||||
_print_operator_stats(op_count_dict)
|
||||
paddle.set_flags({'FLAGS_low_precision_op_list': 0})
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def collect_operator_stats() -> Generator[None, None, None]:
|
||||
"""
|
||||
The context switcher to enable to collect the number of operators for
|
||||
different data types. The statistical data are categorized according
|
||||
to four data types, namely float32, float16, bfloat16 and others, and
|
||||
will be printed when exiting the context.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> conv = paddle.nn.Conv2D(3, 2, 3)
|
||||
>>> x = paddle.rand([10, 3, 32, 32])
|
||||
|
||||
>>> with paddle.amp.debugging.collect_operator_stats():
|
||||
... # AMP list including cast, conv2d, elementwise_add, reshape
|
||||
... with paddle.amp.auto_cast(enable=True, level='O2'):
|
||||
... out = conv(x)
|
||||
>>> # Print to the standard output.
|
||||
>>> # <------------------------------------------------------- op list -------------------------------------------------------->
|
||||
>>> # <--------------- Op Name ---------------- | -- FP16 Calls --- | -- BF16 Calls --- | --- FP32 Calls--- | -- Other Calls -->
|
||||
>>> # cast | 1 | 0 | 2 | 0
|
||||
>>> # conv2d | 1 | 0 | 0 | 0
|
||||
>>> # elementwise_add | 0 | 0 | 1 | 0
|
||||
>>> # reshape | 0 | 0 | 1 | 0
|
||||
>>> # <----------------------------------------------------- op count: 4 ------------------------------------------------------>
|
||||
|
||||
"""
|
||||
enable_operator_stats_collection()
|
||||
yield
|
||||
disable_operator_stats_collection()
|
||||
|
||||
|
||||
def compare_accuracy(
|
||||
dump_path: str,
|
||||
another_dump_path: str,
|
||||
output_filename: str,
|
||||
loss_scale: float = 1,
|
||||
dump_all_tensors: bool = False,
|
||||
) -> None:
|
||||
r"""
|
||||
This is a precision comparison tool that can be used to compare log data of float16 and float32.
|
||||
|
||||
Args:
|
||||
dump_path(str): The path of the running log, such as the log for execution using the float32 data type.
|
||||
another_dump_path(str): the path of another running log ,such as the log for execution using the float16 data type.
|
||||
output_filename(str): the excel file name of compare output.
|
||||
loss_scale(float, optional): the loss_scale during the training phase. Default is 1.
|
||||
dump_all_tensors(bool, optional): dump all tensor, It is currently not support. Default is False.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.base import core
|
||||
>>> try:
|
||||
... import xlsxwriter as xlw
|
||||
... except ImportError:
|
||||
... import subprocess
|
||||
...
|
||||
... subprocess.check_call(
|
||||
... ['python', '-m', 'pip', 'install', 'xlsxwriter==3.0.9'],
|
||||
... )
|
||||
... import xlsxwriter as xlw
|
||||
...
|
||||
... if core.is_compiled_with_cuda():
|
||||
... paddle.set_flags({"FLAGS_check_nan_inf": 1, "FLAGS_check_nan_inf_level": 3})
|
||||
... path = "workerlog_log_dir"
|
||||
... paddle.base.core.set_nan_inf_debug_path(path)
|
||||
... x = paddle.to_tensor([2, 3, 4, 0], dtype="float32")
|
||||
... y = paddle.to_tensor([1, 5, 2, 0], dtype="float32")
|
||||
... z1 = x + y
|
||||
... out_excel = "compare_accuracy_out_excel.csv"
|
||||
... paddle.amp.debugging.compare_accuracy(
|
||||
... path,
|
||||
... path,
|
||||
... out_excel,
|
||||
... loss_scale=1,
|
||||
... dump_all_tensors=False,
|
||||
... )
|
||||
"""
|
||||
assert dump_all_tensors is False, "It is currently not supported."
|
||||
paddle.amp.accuracy_compare.compare_accuracy(
|
||||
dump_path,
|
||||
another_dump_path,
|
||||
output_filename,
|
||||
loss_scale,
|
||||
dump_all_tensors=False,
|
||||
)
|
||||
|
||||
|
||||
def enable_tensor_checker(checker_config: TensorCheckerConfig) -> None:
|
||||
"""
|
||||
The enable_tensor_checker(checker_config) function enables model-level accuracy checking and is used in combination with disables_tensor_checker() to achieve model-level precision checking by checking the output Tensors of all operators within the specified range.
|
||||
|
||||
Args:
|
||||
checker_config(TensorCheckerConfig): Checker_config is to collect the configuration for checking NaN and Inf values in the tensors of a module or operator.
|
||||
|
||||
Note:
|
||||
If disable_tensor_checker() is called before backward(), the gradient operator will not be checked.
|
||||
If disable_tensor_checker() is called before optimizer.step(), the optimizer and other weight update related operators will not be checked.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> checker_config = paddle.amp.debugging.TensorCheckerConfig(
|
||||
... enable=True, debug_mode=paddle.amp.debugging.DebugMode.CHECK_NAN_INF
|
||||
... )
|
||||
>>> paddle.amp.debugging.enable_tensor_checker(checker_config)
|
||||
|
||||
>>> x = paddle.to_tensor([1, 0, 3], place=paddle.CPUPlace(), dtype='float32', stop_gradient=False)
|
||||
>>> y = paddle.to_tensor([0.2, 0, 0.5], place=paddle.CPUPlace(), dtype='float32')
|
||||
>>> res = paddle.pow(x, y)
|
||||
>>> paddle.autograd.backward(res, retain_graph=True)
|
||||
>>> paddle.amp.debugging.disable_tensor_checker()
|
||||
>>> # [PRECISION] [ERROR] in [device=cpu, op=elementwise_pow_grad, tensor=, dtype=fp32], numel=3, num_nan=1, num_inf=0, num_zero=0, max=2.886751e-01, min=2.000000e-01, mean=-nan
|
||||
|
||||
>>> # when DebugMode.CHECK_NAN_INF_AND_ABORT and stack_height_limit = 1
|
||||
>>> # Traceback (most recent call last):
|
||||
>>> # File "tp.py", line 8, in <module>
|
||||
>>> # res = paddle.pow(x, y)
|
||||
>>> # File "/usr/local/lib/python3.8/dist-packages/paddle/tensor/math.py", line 447, in pow
|
||||
>>> # return _C_ops.elementwise_pow(x, y)
|
||||
|
||||
"""
|
||||
if checker_config.update_and_check_step_id():
|
||||
checker_config.start_check_nan_inf()
|
||||
else:
|
||||
checker_config.stop_check_nan_inf()
|
||||
|
||||
|
||||
def disable_tensor_checker() -> None:
|
||||
"""
|
||||
disable_tensor_checker() is used to disable accuracy checking, and is used together with enable_tensor_checker(config) to achieve model-level precision checking by checking the output Tensors of all operators within the specified range.
|
||||
|
||||
Note:
|
||||
If disable_tensor_checker() is called before backward(), the gradient operator will not be checked;
|
||||
If disable_tensor_checker() is called before optimizer.step(), the optimizer and other weight update related operators will not be checked.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> checker_config = paddle.amp.debugging.TensorCheckerConfig(
|
||||
... enable=True, debug_mode=paddle.amp.debugging.DebugMode.CHECK_NAN_INF
|
||||
... )
|
||||
>>> paddle.amp.debugging.enable_tensor_checker(checker_config)
|
||||
|
||||
>>> x = paddle.to_tensor([1, 0, 3], place=paddle.CPUPlace(), dtype='float32', stop_gradient=False)
|
||||
>>> y = paddle.to_tensor([0.2, 0, 0.5], place=paddle.CPUPlace(), dtype='float32')
|
||||
>>> res = paddle.pow(x, y)
|
||||
>>> paddle.autograd.backward(res, retain_graph=True)
|
||||
>>> paddle.amp.debugging.disable_tensor_checker()
|
||||
>>> # [PRECISION] [ERROR] in [device=cpu, op=elementwise_pow_grad, tensor=, dtype=fp32], numel=3, num_nan=1, num_inf=0, num_zero=0, max=2.886751e-01, min=2.000000e-01, mean=-nan
|
||||
|
||||
>>> # when DebugMode.CHECK_NAN_INF_AND_ABORT and stack_height_limit = 1
|
||||
>>> # Traceback (most recent call last):
|
||||
>>> # res = paddle.pow(x, y)
|
||||
>>> # File "/usr/local/lib/python3.8/dist-packages/paddle/tensor/math.py", line 447, in pow
|
||||
>>> # return _C_ops.elementwise_pow(x, y)
|
||||
|
||||
"""
|
||||
paddle.set_flags({"FLAGS_check_nan_inf": 0})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2025 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 .api_tracer import start_api_tracer, stop_api_tracer
|
||||
|
||||
__all__ = [
|
||||
'api_tracer',
|
||||
'start_api_tracer',
|
||||
'stop_api_tracer',
|
||||
]
|
||||
@@ -0,0 +1,297 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import inspect
|
||||
import math
|
||||
import threading
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
import paddle
|
||||
|
||||
_trace_guard = threading.local()
|
||||
_originals = {} # {api_path: (parent_obj, method_name, original_fn)}
|
||||
_hooked_apis = []
|
||||
|
||||
|
||||
class HookAPIMap:
|
||||
pass
|
||||
|
||||
|
||||
class ConfigDump:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def open_file(self, path):
|
||||
self.file = open(path, "a+")
|
||||
|
||||
def dump_config(self, api, input_args, input_kwargs, outputs):
|
||||
result = api + "("
|
||||
for value in input_args:
|
||||
tmp = self.dump_item_str(api, value)
|
||||
if tmp == "":
|
||||
return
|
||||
result = result + tmp + ", "
|
||||
for key, value in input_kwargs.items():
|
||||
tmp = self.dump_item_str(api, value)
|
||||
if tmp == "":
|
||||
return
|
||||
result = result + key + "=" + tmp + ", "
|
||||
|
||||
result = result + ")"
|
||||
# self.file.write(") -> ")
|
||||
# if isinstance(outputs, (list, tuple)):
|
||||
# for output in outputs:
|
||||
# self.file.write(self.dump_item_str(api, output) + ", ")
|
||||
# else:
|
||||
# self.file.write(self.dump_item_str(api, outputs) + ", ")
|
||||
|
||||
self.file.write(result)
|
||||
self.file.write("\n")
|
||||
self.file.flush()
|
||||
|
||||
def dump_item_str(self, api, item):
|
||||
type_mapping = {
|
||||
np.int16: int,
|
||||
np.int32: int,
|
||||
np.int64: int,
|
||||
np.float16: float,
|
||||
np.float32: float,
|
||||
np.float64: float,
|
||||
np.integer: int,
|
||||
np.floating: float,
|
||||
np.bool_: bool,
|
||||
np.complexfloating: complex,
|
||||
np.str_: str,
|
||||
np.bytes_: bytes,
|
||||
# np.unicode_: str,
|
||||
}
|
||||
for numpy_type, builtin_type in type_mapping.items():
|
||||
if isinstance(item, numpy_type):
|
||||
item = builtin_type(item)
|
||||
break
|
||||
|
||||
if isinstance(item, paddle.Tensor):
|
||||
result = (
|
||||
"Tensor(" + str(item.shape) + ',"' + str(item.dtype)[7:] + '"'
|
||||
)
|
||||
if item.place.is_cpu_place():
|
||||
result = result + ",place=" + str(item.place)
|
||||
if not item.is_contiguous():
|
||||
result = (
|
||||
result + ",is_contiguous=False,strides=" + str(item.strides)
|
||||
)
|
||||
return result + ")"
|
||||
elif isinstance(item, paddle.base.core.DataType):
|
||||
return "Dtype(" + str(item)[7:] + ")"
|
||||
elif isinstance(item, paddle.base.core.VarDesc.VarType):
|
||||
return "VarType(" + str(item)[7:] + ")"
|
||||
elif isinstance(item, paddle.base.libpaddle.Place):
|
||||
return str(item)
|
||||
elif isinstance(item, list):
|
||||
result = "list["
|
||||
for sub_item in item:
|
||||
tmp = self.dump_item_str(api, sub_item)
|
||||
if tmp == "":
|
||||
return ""
|
||||
result = result + tmp + ","
|
||||
result = result + "]"
|
||||
return result
|
||||
elif isinstance(item, tuple):
|
||||
result = "tuple("
|
||||
for sub_item in item:
|
||||
tmp = self.dump_item_str(api, sub_item)
|
||||
if tmp == "":
|
||||
return ""
|
||||
result = result + tmp + ","
|
||||
result = result + ")"
|
||||
return result
|
||||
elif isinstance(item, slice):
|
||||
start_str = (
|
||||
str(int(item.start.numpy()))
|
||||
if isinstance(item.start, paddle.Tensor)
|
||||
else str(item.start)
|
||||
)
|
||||
stop_str = (
|
||||
str(int(item.stop.numpy()))
|
||||
if isinstance(item.stop, paddle.Tensor)
|
||||
else str(item.stop)
|
||||
)
|
||||
step_str = (
|
||||
str(int(item.step.numpy()))
|
||||
if isinstance(item.step, paddle.Tensor)
|
||||
else str(item.step)
|
||||
)
|
||||
return "slice(" + start_str + "," + stop_str + "," + step_str + ")"
|
||||
elif isinstance(item, complex):
|
||||
return (
|
||||
"complex("
|
||||
+ self.dump_item_str(api, item.real)
|
||||
+ ","
|
||||
+ self.dump_item_str(api, item.imag)
|
||||
+ ")"
|
||||
)
|
||||
elif item is None:
|
||||
return "None"
|
||||
elif isinstance(
|
||||
item, (paddle.base.Variable, paddle.base.libpaddle.pir.Value)
|
||||
):
|
||||
return ""
|
||||
elif item == math.inf:
|
||||
return "math.inf"
|
||||
elif item == -math.inf:
|
||||
return "-math.inf"
|
||||
elif item == math.nan:
|
||||
return "math.nan"
|
||||
elif item == -math.nan:
|
||||
return "-math.nan"
|
||||
elif isinstance(item, (bool, int, float)):
|
||||
return str(item)
|
||||
elif isinstance(item, str):
|
||||
return '"' + item + '"'
|
||||
elif isinstance(item, type):
|
||||
return (
|
||||
"type("
|
||||
+ str(item)[str(item).index("'") + 1 : str(item).rindex("'")]
|
||||
+ ")"
|
||||
)
|
||||
elif isinstance(item, np.ndarray):
|
||||
return str(item)[1:-1]
|
||||
elif isinstance(item, np.dtype):
|
||||
return "Dtype(" + str(item) + ")"
|
||||
elif item == Ellipsis:
|
||||
return "Ellipsis"
|
||||
else:
|
||||
print(
|
||||
"[api_tracer error] : dump_item_str ",
|
||||
api,
|
||||
", item = ",
|
||||
item,
|
||||
", type(item) = ",
|
||||
type(item),
|
||||
)
|
||||
return ""
|
||||
|
||||
|
||||
config_dump = ConfigDump()
|
||||
|
||||
|
||||
class APITemplate:
|
||||
def __init__(self, api_name):
|
||||
self.api_name = api_name
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if getattr(_trace_guard, 'in_hook', False):
|
||||
return getattr(HookAPIMap, self.api_name)(*args, **kwargs)
|
||||
_trace_guard.in_hook = True
|
||||
try:
|
||||
output = getattr(HookAPIMap, self.api_name)(*args, **kwargs)
|
||||
try:
|
||||
config_dump.dump_config(self.api_name, args, kwargs, output)
|
||||
except Exception as err:
|
||||
print(
|
||||
"[api_tracer error] : config_dump.dump_config ",
|
||||
self.api_name,
|
||||
str(err),
|
||||
)
|
||||
return output
|
||||
finally:
|
||||
_trace_guard.in_hook = False
|
||||
|
||||
|
||||
def wrapped_api(api_name):
|
||||
def api_template(*args, **kwargs):
|
||||
return APITemplate(api_name)(*args, **kwargs)
|
||||
|
||||
return api_template
|
||||
|
||||
|
||||
def expand_wildcard(api_pattern):
|
||||
if not api_pattern.endswith('.*'):
|
||||
return [api_pattern]
|
||||
module_path = api_pattern[:-2]
|
||||
try:
|
||||
module = eval(module_path)
|
||||
except Exception as e:
|
||||
print(f"[api_tracer error] : expand_wildcard {api_pattern}, {e}")
|
||||
return []
|
||||
apis = []
|
||||
for name in dir(module):
|
||||
if module_path.endswith('_C_ops'):
|
||||
if name.startswith('__'):
|
||||
continue
|
||||
elif name.startswith('_'):
|
||||
continue
|
||||
try:
|
||||
attr = getattr(module, name)
|
||||
if (
|
||||
callable(attr)
|
||||
and not inspect.isclass(attr)
|
||||
and not inspect.ismodule(attr)
|
||||
):
|
||||
apis.append(f"{module_path}.{name}")
|
||||
except Exception:
|
||||
pass
|
||||
return apis
|
||||
|
||||
|
||||
def start_api_tracer(api_path, save_config_path):
|
||||
print(paddle.__version__)
|
||||
with open(api_path, "r") as f:
|
||||
raw_apis = yaml.safe_load(f).get("apis") or []
|
||||
|
||||
sample_apis = []
|
||||
for api in raw_apis:
|
||||
sample_apis.extend(expand_wildcard(api))
|
||||
sample_apis = list(dict.fromkeys(sample_apis))
|
||||
print(f"[api_tracer] Expanded to {len(sample_apis)} APIs")
|
||||
|
||||
for api in sample_apis:
|
||||
parent_package, method_name = api.rsplit(".", maxsplit=1)
|
||||
try:
|
||||
parent = eval(parent_package)
|
||||
original = getattr(parent, method_name)
|
||||
_originals[api] = (parent, method_name, original)
|
||||
setattr(HookAPIMap, api, original)
|
||||
setattr(parent, method_name, wrapped_api(api))
|
||||
_hooked_apis.append(api)
|
||||
except Exception as err:
|
||||
print(
|
||||
"[api_tracer error] : start_api_tracer ",
|
||||
api,
|
||||
str(err),
|
||||
)
|
||||
|
||||
config_dump.open_file(save_config_path)
|
||||
|
||||
|
||||
def stop_api_tracer():
|
||||
for api in _hooked_apis:
|
||||
entry = _originals.pop(api, None)
|
||||
if entry:
|
||||
parent, method_name, original = entry
|
||||
try:
|
||||
setattr(parent, method_name, original)
|
||||
except Exception as err:
|
||||
print(f"[api_tracer error] : stop_api_tracer {api} {err}")
|
||||
_hooked_apis.clear()
|
||||
if (
|
||||
hasattr(config_dump, 'file')
|
||||
and config_dump.file
|
||||
and not config_dump.file.closed
|
||||
):
|
||||
config_dump.file.flush()
|
||||
config_dump.file.close()
|
||||
print("[api_tracer] Stopped, all hooks removed.")
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import ap
|
||||
|
||||
|
||||
class CompileCommandGenerator:
|
||||
def __init__(self):
|
||||
self.file_ext = "cu"
|
||||
self.op_type2generate_func = ap.OrderedDict(
|
||||
[
|
||||
['matmul', self.generate_matmul_compile_command],
|
||||
]
|
||||
)
|
||||
|
||||
def __call__(self, op_type, tpl_dirname, library_name):
|
||||
return self.op_type2generate_func[op_type](tpl_dirname, library_name)
|
||||
|
||||
def generate_matmul_compile_command(self, tpl_dirname, library_name):
|
||||
matmul_source_dir = f"{tpl_dirname}/matmul"
|
||||
|
||||
compile_cmd = "nvcc -std=c++20 -O3 -Xcompiler=-fPIC -arch=sm_80 --expt-relaxed-constexpr"
|
||||
compile_cmd = compile_cmd + " -I ${AP_CUTLASS_DIR}/include"
|
||||
compile_cmd = compile_cmd + " -I ${AP_CUTLASS_DIR}/tools/util/include"
|
||||
compile_cmd = compile_cmd + " -I " + matmul_source_dir
|
||||
compile_cmd = (
|
||||
compile_cmd
|
||||
+ " -DCUTLASS_ENABLE_TENSOR_CORE_MMA=1 -DCUTLASS_DEBUG_TRACE_LEVEL=0"
|
||||
)
|
||||
compile_cmd = (
|
||||
compile_cmd + " -DAP_ENABLE_AUTOTUNE=1 -DAP_ENABLE_DEBUG=0"
|
||||
)
|
||||
compile_cmd = (
|
||||
compile_cmd
|
||||
+ f" --shared {library_name}.{self.file_ext} -o lib{library_name}.so"
|
||||
)
|
||||
return compile_cmd
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import ap
|
||||
|
||||
|
||||
class CompileCommandGenerator:
|
||||
def __init__(self):
|
||||
self.file_ext = "cu"
|
||||
self.op_type2generate_func = ap.OrderedDict(
|
||||
[
|
||||
['matmul', self.generate_matmul_compile_command],
|
||||
]
|
||||
)
|
||||
|
||||
def __call__(self, op_type, tpl_dirname, library_name):
|
||||
return self.op_type2generate_func[op_type](tpl_dirname, library_name)
|
||||
|
||||
def generate_matmul_compile_command(self, tpl_dirname, library_name):
|
||||
matmul_source_dir = f"{tpl_dirname}/matmul"
|
||||
|
||||
compile_cmd = (
|
||||
"hipcc -std=c++17 -O3 -fPIC --offload-arch=gfx928 -Wno-return-type"
|
||||
)
|
||||
compile_cmd = compile_cmd + " -I ${AP_CUTLASS_DIR}/include"
|
||||
compile_cmd = compile_cmd + " -I ${AP_CUTLASS_DIR}/tools/util/include"
|
||||
compile_cmd = compile_cmd + " -I " + matmul_source_dir
|
||||
compile_cmd = (
|
||||
compile_cmd + " -DAP_ENABLE_AUTOTUNE=0 -DAP_ENABLE_DEBUG=0"
|
||||
)
|
||||
compile_cmd = (
|
||||
compile_cmd
|
||||
+ f" --shared {library_name}.{self.file_ext} -o lib{library_name}.so"
|
||||
)
|
||||
return compile_cmd
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import matmul_variadic_ptn # noqa: F401
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
|
||||
class DrrPass:
|
||||
def make_drr_ctx(self):
|
||||
drr_ctx = DrrCtx() # noqa: F821
|
||||
drr_ctx.set_drr_pass_type(self.drr_pass_type())
|
||||
drr_ctx.init_source_pattern(self.source_pattern)
|
||||
drr_ctx.init_constraint_func(self.constraint)
|
||||
drr_ctx.init_result_pattern(self.result_pattern)
|
||||
return drr_ctx
|
||||
|
||||
def constraint(self, o, t):
|
||||
return True
|
||||
|
||||
def drr_pass_type(self):
|
||||
return "abstract_drr_pass_type"
|
||||
|
||||
|
||||
class register_drr_pass:
|
||||
def __init__(self, pass_name, nice):
|
||||
self.pass_name = pass_name
|
||||
self.nice = nice
|
||||
|
||||
def __call__(self, drr_pass_cls):
|
||||
Registry.abstract_drr_pass( # noqa: F821
|
||||
self.pass_name, self.nice, drr_pass_cls
|
||||
)
|
||||
return drr_pass_cls
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
|
||||
class DrrPass:
|
||||
def make_drr_ctx(self):
|
||||
drr_ctx = DrrCtx() # noqa: F821
|
||||
drr_ctx.set_drr_pass_type(self.drr_pass_type())
|
||||
drr_ctx.init_source_pattern(self.source_pattern)
|
||||
drr_ctx.init_constraint_func(self.constraint)
|
||||
drr_ctx.init_result_pattern(self.result_pattern)
|
||||
return drr_ctx
|
||||
|
||||
def constraint(self, o, t):
|
||||
return True
|
||||
|
||||
def drr_pass_type(self):
|
||||
return "access_topo_drr_pass_type"
|
||||
|
||||
|
||||
class register_drr_pass:
|
||||
def __init__(self, pass_name, tag):
|
||||
self.pass_name = pass_name
|
||||
self.tag = tag
|
||||
|
||||
def __call__(self, drr_pass_cls):
|
||||
Registry.access_topo_drr_pass( # noqa: F821
|
||||
self.pass_name, self.tag, drr_pass_cls
|
||||
)
|
||||
return drr_pass_cls
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
|
||||
class CodeGenValue:
|
||||
def __init__(self, pir_type, var_name):
|
||||
self.pir_type = pir_type
|
||||
self.var_name = var_name
|
||||
self.const_value = None
|
||||
|
||||
def get_dtype(self):
|
||||
def convert_to_dtype(pir_dtype, shape, data_layout):
|
||||
return pir_dtype.convert_to_dtype()
|
||||
|
||||
return self.pir_type.match(t_dtensor=convert_to_dtype)
|
||||
|
||||
def is_dense_tensor_type(self):
|
||||
return self.pir_type.get_type_name() == "t_dtensor"
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
|
||||
class IndexCodeGenValue:
|
||||
def __init__(self, iter_var_names):
|
||||
self.iter_var_names = iter_var_names
|
||||
self.const_data = None
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import access_topo_drr
|
||||
import ap
|
||||
import pir
|
||||
|
||||
|
||||
class InsertReshapeBeforeYieldPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.yield_op = o.ap_native_op("cf.yield")
|
||||
o.yield_op([t.output], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
t.declare_internal_native_ir_value("reshaped_output")
|
||||
o.reshape_op = o.ap_native_op("cinn_op.reshape")
|
||||
o.reshape_op.shape = lambda o, t: pir.a_array(
|
||||
[pir.a_i32(ap.DataValue.int32("-1"))]
|
||||
)
|
||||
o.reshape_op([t.output], [t.reshaped_output])
|
||||
o.yield_op = o.ap_native_op("cf.yield")
|
||||
o.yield_op([t.reshaped_output], [])
|
||||
@@ -0,0 +1,137 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
import index_drr_pass_util
|
||||
import ir_tools
|
||||
import op_index_translator_util
|
||||
|
||||
|
||||
class IndexProgramTranslatorMap:
|
||||
def __init__(
|
||||
self,
|
||||
index_func_unique_id2index_program,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
items = index_func_unique_id2index_program.items()
|
||||
self.index_func_unique_id2translator = ap.OrderedDict(
|
||||
ap.map(
|
||||
lambda i: [items[i][0], self.make_translator(i, items[i][1])],
|
||||
range(len(items)),
|
||||
)
|
||||
)
|
||||
|
||||
def get_offset_var_name(
|
||||
self,
|
||||
index_func_unique_id,
|
||||
mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx,
|
||||
):
|
||||
translator = self.index_func_unique_id2translator[index_func_unique_id]
|
||||
ret = translator.translate(
|
||||
mut_kernel_arg_id_registry=mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx=mut_lir_code_gen_ctx,
|
||||
)
|
||||
return ret.iter_var_names[0]
|
||||
|
||||
def make_translator(self, program_id, index_program):
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
drr_pass = index_drr_pass_util.InsertReshapeBeforeYieldPass()
|
||||
pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(drr_pass)
|
||||
)
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(index_program)
|
||||
return IndexProgramTranslator(
|
||||
index_program,
|
||||
program_id=program_id,
|
||||
kernel_arg_translator=self.kernel_arg_translator,
|
||||
index_op_translator_maker=op_index_translator_util.OpIndexTranslatorFactory(),
|
||||
anchor_iter_var_names=self.anchor_iter_var_names,
|
||||
)
|
||||
|
||||
|
||||
class IndexProgramTranslator:
|
||||
def __init__(
|
||||
self,
|
||||
index_program,
|
||||
program_id,
|
||||
kernel_arg_translator,
|
||||
index_op_translator_maker,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.program_id = program_id
|
||||
self.program_property = index_program.copy_to_const_program_data()
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_op_translator_maker = index_op_translator_maker
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
self.ir_value_index2translated_value = ap.MutableList()
|
||||
|
||||
def PushNone(x):
|
||||
self.ir_value_index2translated_value.append(None)
|
||||
|
||||
ap.map(PushNone, self.program_property.values)
|
||||
|
||||
def translate(
|
||||
self,
|
||||
mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx,
|
||||
):
|
||||
def TranslateOp(op_property):
|
||||
self._translate_op(
|
||||
op_property, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
)
|
||||
|
||||
ap.map(TranslateOp, self.program_property.ops)
|
||||
return self.ir_value_index2translated_value[-1]
|
||||
|
||||
def _translate_op(
|
||||
self, op_property, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
index_op_translator = self.index_op_translator_maker(
|
||||
index_program_id=self.program_id,
|
||||
op_property=op_property,
|
||||
input_properties=ap.map(
|
||||
self._get_value_property, op_property.input_value_indexes
|
||||
),
|
||||
output_properties=ap.map(
|
||||
self._get_value_property, op_property.output_value_indexes
|
||||
),
|
||||
kernel_arg_translator=self.kernel_arg_translator,
|
||||
anchor_iter_var_names=self.anchor_iter_var_names,
|
||||
)
|
||||
inputs = ap.map(
|
||||
self._get_translated_value, op_property.input_value_indexes
|
||||
)
|
||||
outputs = index_op_translator(
|
||||
inputs,
|
||||
mut_kernel_arg_id_registry=mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx=mut_lir_code_gen_ctx,
|
||||
)
|
||||
ap.map(
|
||||
self._set_translated_value,
|
||||
zip(op_property.output_value_indexes, outputs),
|
||||
)
|
||||
|
||||
def _get_value_property(self, i):
|
||||
return self.program_property.values[i]
|
||||
|
||||
def _get_translated_value(self, i):
|
||||
return self.ir_value_index2translated_value[i]
|
||||
|
||||
def _set_translated_value(self, pair):
|
||||
self.ir_value_index2translated_value[pair[0]] = pair[1]
|
||||
@@ -0,0 +1,88 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
|
||||
|
||||
class KernelArgIdNameRegistry:
|
||||
def __init__(self, code_gen_ctx, tensor_match_ctx, name_prefix):
|
||||
self.code_gen_ctx = code_gen_ctx
|
||||
self.tensor_match_ctx = tensor_match_ctx
|
||||
self.name_prefix = name_prefix
|
||||
self.generated_kernel_arg_id2unique_name = ap.MutableOrderedDict()
|
||||
self.all_kernel_arg_id2unique_name = ap.MutableOrderedDict()
|
||||
self.in_tensor_data_ptr_seq_no = 0
|
||||
self.out_tensor_data_ptr_seq_no = 0
|
||||
self.dim_expr_seq_no = 0
|
||||
|
||||
def get_or_create_kernel_arg_id_manul_var_name(
|
||||
self, kernel_arg_id, cpp_var_name
|
||||
):
|
||||
create = lambda: cpp_var_name
|
||||
return self.all_kernel_arg_id2unique_name.get_or_create(
|
||||
kernel_arg_id, create
|
||||
)
|
||||
|
||||
def get_in_tensor_data_ptr_var_name(self, in_ir_value_name):
|
||||
ir_value = getattr(self.tensor_match_ctx, in_ir_value_name)
|
||||
kernel_arg_id = self.code_gen_ctx.in_tensor_data_ptr_kernel_arg_id(
|
||||
ir_value
|
||||
)
|
||||
create = self._get_creator(
|
||||
kernel_arg_id, self._create_in_tensor_data_ptr_var_name
|
||||
)
|
||||
return self.generated_kernel_arg_id2unique_name.get_or_create(
|
||||
kernel_arg_id, create
|
||||
)
|
||||
|
||||
def _get_creator(self, kernel_arg_id, backend_creator):
|
||||
return lambda: self.all_kernel_arg_id2unique_name.get_or_create(
|
||||
kernel_arg_id, backend_creator
|
||||
)
|
||||
|
||||
def _create_in_tensor_data_ptr_var_name(self):
|
||||
name = f"{self.name_prefix}in_ptr_{self.in_tensor_data_ptr_seq_no}"
|
||||
self.in_tensor_data_ptr_seq_no = self.in_tensor_data_ptr_seq_no + 1
|
||||
return name
|
||||
|
||||
def get_out_tensor_data_ptr_var_name(self, out_ir_value_name):
|
||||
ir_value = getattr(self.tensor_match_ctx, out_ir_value_name)
|
||||
kernel_arg_id = self.code_gen_ctx.out_tensor_data_ptr_kernel_arg_id(
|
||||
ir_value
|
||||
)
|
||||
create = self._get_creator(
|
||||
kernel_arg_id, self._create_out_tensor_data_ptr_var_name
|
||||
)
|
||||
return self.generated_kernel_arg_id2unique_name.get_or_create(
|
||||
kernel_arg_id, create
|
||||
)
|
||||
|
||||
def _create_out_tensor_data_ptr_var_name(self):
|
||||
name = f"{self.name_prefix}out_ptr_{self.out_tensor_data_ptr_seq_no}"
|
||||
self.out_tensor_data_ptr_seq_no = self.out_tensor_data_ptr_seq_no + 1
|
||||
return name
|
||||
|
||||
def get_dim_expr_var_name(self, dim_expr):
|
||||
kernel_arg_id = self.code_gen_ctx.dim_expr_kernel_arg_id(dim_expr)
|
||||
create = self._get_creator(
|
||||
kernel_arg_id, self._create_dim_expr_var_name
|
||||
)
|
||||
return self.generated_kernel_arg_id2unique_name.get_or_create(
|
||||
kernel_arg_id, create
|
||||
)
|
||||
|
||||
def _create_dim_expr_var_name(self):
|
||||
name = f"{self.name_prefix}dim_{self.dim_expr_seq_no}"
|
||||
self.dim_expr_seq_no = self.dim_expr_seq_no + 1
|
||||
return name
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
|
||||
class KernelArgTranslator:
|
||||
def __init__(self, param_struct_name):
|
||||
self.param_struct_name = param_struct_name
|
||||
|
||||
def get_kernel_arg_name(self, var_name):
|
||||
return var_name
|
||||
|
||||
def get_param_struct_field_name(self, var_name):
|
||||
return var_name
|
||||
|
||||
def get_param_struct_init_name(self, var_name):
|
||||
return f"{self.param_struct_name}.{var_name}"
|
||||
|
||||
def get_use_name(self, var_name):
|
||||
return f"{self.param_struct_name}.{var_name}"
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
|
||||
|
||||
class CudaLikeIrCodeGenCtx:
|
||||
def __init__(self, compute_dtype):
|
||||
self.stmts = ap.MutableList()
|
||||
self.dtype2type_name = ap.OrderedDict(
|
||||
[
|
||||
[ap.DataType.float, "float"],
|
||||
[ap.DataType.float16, "half"],
|
||||
[ap.DataType.bfloat16, "nv_bfloat16"],
|
||||
[ap.DataType.int32, "int"],
|
||||
[ap.DataType.int64, "int64_t"],
|
||||
]
|
||||
)
|
||||
self.compute_dtype = compute_dtype
|
||||
self.compute_dtype_name = self.dtype2type_name[self.compute_dtype]
|
||||
self.type_cast_str_list = [
|
||||
"",
|
||||
f"static_cast<{self.compute_dtype_name}>",
|
||||
]
|
||||
|
||||
def assign(self, dst, src):
|
||||
self.stmts.append(f"{dst.var_name} = {src.var_name};")
|
||||
|
||||
def let(self, var, val_name):
|
||||
var_dtype_name = self.dtype2type_name[var.get_dtype()]
|
||||
is_same = self.compute_dtype == var.get_dtype()
|
||||
type_name = (
|
||||
f"{var_dtype_name}" if is_same else f"{self.compute_dtype_name}"
|
||||
)
|
||||
type_cast_str = (
|
||||
"" if is_same else f"static_cast<{self.compute_dtype_name}>"
|
||||
)
|
||||
self.stmts.append(
|
||||
f"{type_name} {var.var_name} = {type_cast_str}({val_name});"
|
||||
)
|
||||
|
||||
def store(self, dtype, dst, offset_var_name, src):
|
||||
is_same = dtype == self.dtype2type_name[self.compute_dtype]
|
||||
type_cast_str = "" if is_same else f"static_cast<{dtype}>"
|
||||
self.stmts.append(f"{dst}[{offset_var_name}] = {type_cast_str}({src});")
|
||||
|
||||
def get_stmts_joined_str(self, indent):
|
||||
return f"\n{indent}".join([*self.stmts])
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
cutlass
|
||||
generate_configs.py
|
||||
hytlass
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include "cutlass/cutlass.h"
|
||||
#define GPUStream_t cudaStream_t
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/hytlass.h"
|
||||
namespace cutlass = hytlass;
|
||||
#define GPUStream_t hipStream_t
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
namespace cutlass_patch {
|
||||
|
||||
struct BatchedMatrixCoord {
|
||||
int batch;
|
||||
int row;
|
||||
int column;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
BatchedMatrixCoord() : batch(0), row(0), column(0) {}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
BatchedMatrixCoord(int b, int r, int c) : batch(b), row(r), column(c) {}
|
||||
};
|
||||
|
||||
}; // namespace cutlass_patch
|
||||
@@ -0,0 +1,496 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// auto-generated by generate_configs.py
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/gemm_coord.h"
|
||||
|
||||
namespace ap {
|
||||
|
||||
constexpr int kNumConfigsHalf = 23;
|
||||
constexpr int kNumConfigsFloat = 13;
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct SwizzleWrapper {
|
||||
using Type =
|
||||
cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<SwizzleFactor>;
|
||||
};
|
||||
|
||||
// template <int SwizzleFactor>
|
||||
// struct SwizzleWrapper<SwizzleFactor, true> {
|
||||
// using Type =
|
||||
// cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle;
|
||||
// };
|
||||
|
||||
#define AP_AUTOTUNE(func, stream_ptr, count, ...) \
|
||||
{ \
|
||||
using FuncType = decltype(func<0>); \
|
||||
static int selected_config_id = -1; \
|
||||
static std::vector<std::function<FuncType>> matmul_functions = \
|
||||
[]<std::size_t... Is>(std::index_sequence<Is...>) { \
|
||||
return std::vector<std::function<FuncType>>{func<Is>...}; \
|
||||
} \
|
||||
(std::make_index_sequence<count>()); \
|
||||
\
|
||||
if (selected_config_id == -1) { \
|
||||
selected_config_id = \
|
||||
ap::ProfileBestConfig(matmul_functions, stream_ptr, ##__VA_ARGS__); \
|
||||
} \
|
||||
\
|
||||
matmul_functions[selected_config_id](__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define AP_AUTOTUNE_half(func, stream_ptr, ...) \
|
||||
AP_AUTOTUNE(func, stream_ptr, ap::kNumConfigsHalf, __VA_ARGS__)
|
||||
#define AP_AUTOTUNE_float(func, stream_ptr, ...) \
|
||||
AP_AUTOTUNE(func, stream_ptr, ap::kNumConfigsFloat, __VA_ARGS__)
|
||||
#define AP_AUTOTUNE_bfloat16(func, stream_ptr, ...) \
|
||||
AP_AUTOTUNE_half(func, stream_ptr, __VA_ARGS__)
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched, int Id = 0>
|
||||
struct GemmTuningConfigs {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 2;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = Id;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 1> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 1;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 2> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 2;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 3> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 64, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 32, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 3;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 4> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 4;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 5> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 5;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 6> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 6;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 7> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 64, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 7;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 8> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 8;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 9> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 9;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 10> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 32, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 32, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 10;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 11> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 11;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 12> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 12;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 13> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 64, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 13;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 14> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 14;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 15> {
|
||||
using TShape = cutlass::gemm::GemmShape<32, 64, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<16, 32, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 15;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 16> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 32, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 16;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 17> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 17;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 18> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 64>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 18;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 19> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 6;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 19;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 20> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 32, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 6;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 20;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 21> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 32, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 32, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 7;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 21;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 22> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 32, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 16>;
|
||||
static constexpr int kNumStages = 10;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 22;
|
||||
};
|
||||
|
||||
// Specialization for float
|
||||
template <int SwizzleFactor, bool Batched, int Id>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, Id> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 64, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 32, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = Id;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 1> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 32, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 1;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 2> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 2;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 3> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 256, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 3;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 4> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 256, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 4;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 5> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 32, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 5;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 6> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 6;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 7> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 7;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 8> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 64, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 32, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 8;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 9> {
|
||||
using TShape = cutlass::gemm::GemmShape<256, 64, 32>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 32, 32>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 9;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 10> {
|
||||
using TShape = cutlass::gemm::GemmShape<64, 128, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 10;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 11> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 64, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<64, 32, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 11;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 12> {
|
||||
using TShape = cutlass::gemm::GemmShape<128, 128, 16>;
|
||||
using WShape = cutlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = cutlass::gemm::GemmShape<16, 8, 8>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 12;
|
||||
};
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,273 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda.h>
|
||||
#include <cuda_bf16.h>
|
||||
#include <cuda_fp16.h>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/gemm_coord.h"
|
||||
#include "cutlass/layout/matrix.h"
|
||||
|
||||
#include "cutlass/epilogue/thread/linear_combination_bias_elementwise.h"
|
||||
#include "cutlass/util/device_memory.h"
|
||||
|
||||
#include "cutlass/gemm/device/gemm_universal.h"
|
||||
#include "cutlass/gemm/device/gemm_universal_with_broadcast.h"
|
||||
|
||||
#include "cutlass_patch/batched_matrix_coord.h"
|
||||
#include "cutlass_patch/cuda/default_config_id.h"
|
||||
#include "cutlass_patch/epilogue/thread/linear_combination_unary.h"
|
||||
#include "cutlass_patch/epilogue/thread/linear_combination_variadic.h"
|
||||
#include "cutlass_patch/gemm/device/gemm_universal_with_variadic.h"
|
||||
|
||||
#include "params.h" // NOLINT
|
||||
|
||||
#define CHECK_CUTLASS(status) \
|
||||
{ \
|
||||
cutlass::Status error = status; \
|
||||
if (error != cutlass::Status::kSuccess) { \
|
||||
std::cerr << "Got cutlass error: " << cutlassGetStatusString(error) \
|
||||
<< " at: " << __LINE__ << std::endl; \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace ap {
|
||||
using bfloat16 = nv_bfloat16;
|
||||
|
||||
template <typename T, int N>
|
||||
using Array = cutlass::Array<T, N>;
|
||||
|
||||
using MatrixCoord = cutlass_patch::BatchedMatrixCoord;
|
||||
|
||||
// Convert CUDA data type to cutlass data type
|
||||
template <typename T>
|
||||
struct CutlassDataType {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CutlassDataType<half> {
|
||||
using Type = cutlass::half_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CutlassDataType<__nv_bfloat16> {
|
||||
using Type = cutlass::bfloat16_t;
|
||||
};
|
||||
|
||||
// Convert to cutlass layout
|
||||
template <bool Transposed>
|
||||
struct MatrixLayout {
|
||||
using Type = cutlass::layout::RowMajor;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MatrixLayout<true> {
|
||||
using Type = cutlass::layout::ColumnMajor;
|
||||
};
|
||||
|
||||
// Operation performed by GEMM
|
||||
template <typename ElementT>
|
||||
struct GemmOperation {
|
||||
using Type = cutlass::arch::OpMultiplyAdd;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct GemmOperation<float> {
|
||||
using Type = cutlass::arch::OpMultiplyAddFastF32;
|
||||
};
|
||||
|
||||
static cutlass::gemm::GemmUniversalMode GetGemmMode(int batch_count) {
|
||||
return batch_count > 1 ? cutlass::gemm::GemmUniversalMode::kBatched
|
||||
: cutlass::gemm::GemmUniversalMode::kGemm;
|
||||
}
|
||||
|
||||
static void *GetWorkspace(size_t workspace_size) {
|
||||
static cutlass::device_memory::allocation<uint8_t> workspace;
|
||||
if (workspace.size() < workspace_size) {
|
||||
workspace.reset(workspace_size);
|
||||
}
|
||||
return workspace.get();
|
||||
}
|
||||
|
||||
template <typename GemmFunc>
|
||||
cutlass::Status SetMaxDynamicSharedMemorySize() {
|
||||
cudaError_t cudart_result;
|
||||
|
||||
// If requires more than 48KB: configure for extended, dynamic shared memory
|
||||
if constexpr (GemmFunc::kSharedStorageSize >= (48 << 10)) {
|
||||
cudart_result =
|
||||
cudaFuncSetAttribute(cutlass::Kernel2<typename GemmFunc::GemmKernel>,
|
||||
cudaFuncAttributeMaxDynamicSharedMemorySize,
|
||||
GemmFunc::kSharedStorageSize);
|
||||
if (cudart_result != cudaSuccess) {
|
||||
CUTLASS_TRACE_HOST("cudaFuncSetAttribute() returned error "
|
||||
<< cudaGetErrorString(cudart_result));
|
||||
return cutlass::Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
|
||||
#if AP_ENABLE_DEBUG
|
||||
// Update SM occupancy member
|
||||
int sm_occupancy = -1;
|
||||
cudart_result = cudaOccupancyMaxActiveBlocksPerMultiprocessorWithFlags(
|
||||
&sm_occupancy,
|
||||
cutlass::Kernel2<typename GemmFunc::GemmKernel>,
|
||||
GemmFunc::GemmKernel::kThreadCount,
|
||||
GemmFunc::kSharedStorageSize,
|
||||
cudaOccupancyDisableCachingOverride);
|
||||
if (cudart_result != cudaSuccess) {
|
||||
CUTLASS_TRACE_HOST(
|
||||
"cudaOccupancyMaxActiveBlocksPerMultiprocessorWithFlags() returned "
|
||||
"error "
|
||||
<< cudaGetErrorString(cudart_result));
|
||||
return cutlass::Status::kErrorInternal;
|
||||
}
|
||||
CUTLASS_TRACE_HOST("sm_occupancy: (" << sm_occupancy
|
||||
<< ") "
|
||||
"smem_size: ("
|
||||
<< GemmFunc::kSharedStorageSize
|
||||
<< ") "
|
||||
"GemmKernel::kThreadCount: ("
|
||||
<< GemmFunc::GemmKernel::kThreadCount
|
||||
<< ")");
|
||||
#endif
|
||||
return cutlass::Status::kSuccess;
|
||||
}
|
||||
|
||||
template <typename ElementT,
|
||||
typename ElementComputeT,
|
||||
template <typename T>
|
||||
class VariadicFunctor,
|
||||
int AlignA = 128 / cutlass::sizeof_bits<ElementT>::value,
|
||||
int AlignB = 128 / cutlass::sizeof_bits<ElementT>::value,
|
||||
int ConfigId = DefaultConfig::kConfigId,
|
||||
int SwizzleFactor = DefaultConfig::kSwizzleFactor,
|
||||
bool Batched = DefaultConfig::kBatched>
|
||||
void MatmulAddVariadic(
|
||||
const GemmEpilogueParams ¶ms,
|
||||
const typename VariadicFunctor<ElementComputeT>::Arguments &variadic_args) {
|
||||
using ElementAccumulator =
|
||||
typename CutlassDataType<ElementComputeT>::Type; // <- data type of
|
||||
// accumulator
|
||||
using ElementComputeEpilogue =
|
||||
ElementAccumulator; // <- data type of epilogue operations
|
||||
using ElementInputA =
|
||||
typename CutlassDataType<ElementT>::Type; // <- data type of elements in
|
||||
// input matrix A
|
||||
using ElementInputB =
|
||||
typename CutlassDataType<ElementT>::Type; // <- data type of elements in
|
||||
// input matrix B
|
||||
using ElementOutput =
|
||||
typename CutlassDataType<ElementT>::Type; // <- data type of elements in
|
||||
// output matrix D
|
||||
|
||||
constexpr int AlignC = AlignB;
|
||||
|
||||
// Epilogue operation as LinearCombination:
|
||||
// alpha * accumulator + beta * source
|
||||
using EpilogueOutputOp =
|
||||
cutlass_patch::epilogue::thread::LinearCombinationVariadic<
|
||||
VariadicFunctor,
|
||||
ElementOutput,
|
||||
AlignC,
|
||||
ElementAccumulator,
|
||||
ElementComputeEpilogue,
|
||||
cutlass::epilogue::thread::ScaleType::NoBetaScaling>; // <- alpha x
|
||||
// AB + bias
|
||||
|
||||
using GemmFunc = cutlass_patch::gemm::device::GemmUniversalWithVariadic<
|
||||
ElementInputA,
|
||||
cutlass::layout::RowMajor,
|
||||
ElementInputB,
|
||||
cutlass::layout::RowMajor,
|
||||
ElementOutput,
|
||||
cutlass::layout::RowMajor,
|
||||
ElementAccumulator,
|
||||
cutlass::arch::OpClassTensorOp,
|
||||
cutlass::arch::Sm80,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
TShape,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
WShape,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
IShape,
|
||||
EpilogueOutputOp,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
SwizzleThreadBlock,
|
||||
GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::kNumStages,
|
||||
AlignA,
|
||||
AlignB,
|
||||
typename GemmOperation<ElementT>::Type>;
|
||||
|
||||
CHECK_CUTLASS(SetMaxDynamicSharedMemorySize<GemmFunc>());
|
||||
|
||||
/// Arguments
|
||||
cutlass::gemm::GemmCoord problem_size{params.m, params.n, params.k};
|
||||
|
||||
const ElementInputA *input =
|
||||
reinterpret_cast<const ElementInputA *>(params.input);
|
||||
const ElementInputB *weight =
|
||||
reinterpret_cast<const ElementInputB *>(params.weight);
|
||||
const ElementOutput *bias =
|
||||
reinterpret_cast<const ElementOutput *>(params.bias);
|
||||
ElementOutput *output = reinterpret_cast<ElementOutput *>(params.output);
|
||||
|
||||
ElementComputeEpilogue alpha = static_cast<ElementComputeEpilogue>(1);
|
||||
ElementComputeEpilogue beta = bias ? static_cast<ElementComputeEpilogue>(1)
|
||||
: static_cast<ElementComputeEpilogue>(0);
|
||||
|
||||
typename GemmFunc::Arguments arguments{
|
||||
GetGemmMode(params.batch_count),
|
||||
problem_size, // <- problem size of matrix multiplication
|
||||
params.batch_count, // <- batch_count or k-dimension split factor
|
||||
{alpha, beta, variadic_args}, // <- epilogue params, alpha, beta
|
||||
input, // <- input, ptr_A, A, shape={M, K}
|
||||
weight, // <- input, ptr_B, B, shape={K, N}
|
||||
bias, // <- input, ptr_C, shape={M, N} or {1, N}
|
||||
output, // <- output, ptr_D, Z, shape={M, N}
|
||||
params.shape_args.batch_stride_A,
|
||||
params.shape_args.batch_stride_B,
|
||||
params.shape_args.batch_stride_C,
|
||||
params.shape_args.batch_stride_D,
|
||||
params.shape_args.lda,
|
||||
params.shape_args.ldb,
|
||||
params.shape_args.ldc_bias,
|
||||
params.shape_args.ldd};
|
||||
|
||||
size_t workspace_size = GemmFunc::get_workspace_size(arguments);
|
||||
void *workspace = workspace_size > 0 ? GetWorkspace(workspace_size) : nullptr;
|
||||
|
||||
GemmFunc device_gemm;
|
||||
|
||||
cudaStream_t *stream_ptr =
|
||||
reinterpret_cast<cudaStream_t *>(params.stream_ptr);
|
||||
|
||||
CHECK_CUTLASS(device_gemm.can_implement(arguments));
|
||||
CHECK_CUTLASS(device_gemm.initialize(arguments, workspace, *stream_ptr));
|
||||
|
||||
//
|
||||
// Run the GEMM
|
||||
//
|
||||
CHECK_CUTLASS(device_gemm(*stream_ptr));
|
||||
#if AP_ENABLE_DEBUG
|
||||
CHECK_CUDA(cudaStreamSynchronize(*stream_ptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "all_tuning_configs.h" // NOLINT
|
||||
|
||||
namespace ap {
|
||||
|
||||
struct DefaultConfig {
|
||||
static constexpr int kConfigId = 0;
|
||||
static constexpr int kSwizzleFactor = 1;
|
||||
static constexpr bool kBatched = false;
|
||||
};
|
||||
|
||||
} // namespace ap
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
/*! \file
|
||||
\brief Functor performing linear combination operations used by epilogues.
|
||||
It is modified from LinearCombinationGeneric.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/epilogue/thread/scale_type.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/array.h"
|
||||
#include "hytlass/epilogue/thread/scale_type.h"
|
||||
#include "hytlass/functional.h"
|
||||
#include "hytlass/numeric_conversion.h"
|
||||
#include "hytlass/numeric_types.h"
|
||||
#endif
|
||||
|
||||
namespace cutlass_patch {
|
||||
namespace epilogue {
|
||||
namespace thread {
|
||||
|
||||
template <class UnaryOp, class = void>
|
||||
struct GenericUnaryTraits {
|
||||
static constexpr bool IsArgumentsNeeded = false;
|
||||
struct Arguments {};
|
||||
};
|
||||
|
||||
template <class UnaryOp>
|
||||
struct GenericUnaryTraits<UnaryOp,
|
||||
decltype(typename UnaryOp::Arguments(), void())> {
|
||||
static constexpr bool IsArgumentsNeeded = true;
|
||||
using Arguments = typename UnaryOp::Arguments;
|
||||
};
|
||||
|
||||
/// Applies a linear combination operator followed by an unary function to an
|
||||
/// array of elements.
|
||||
///
|
||||
/// D = unary_op(alpha * accumulator + beta * source)
|
||||
///
|
||||
template <
|
||||
template <typename T>
|
||||
class UnaryOp,
|
||||
typename ElementOutput_, ///< Data type used to load and store tensors
|
||||
int ElementsPerAccess, ///< Number of elements computed per operation
|
||||
///< Usually it is 128/sizeof_bits<ElementOutput_>,
|
||||
///< but we use 64 or 32 sometimes when there are
|
||||
///< not enough data to store
|
||||
typename ElementAccumulator_ = ElementOutput_, ///< Accumulator data type
|
||||
typename ElementCompute_ =
|
||||
ElementOutput_, ///< Data type used to compute linear combination
|
||||
cutlass::epilogue::thread::ScaleType::Kind Scale =
|
||||
cutlass::epilogue::thread::ScaleType::Default, ///< Control Alpha and
|
||||
///< Beta scaling
|
||||
cutlass::FloatRoundStyle Round = cutlass::FloatRoundStyle::round_to_nearest,
|
||||
bool IsHeavy = false>
|
||||
class LinearCombinationUnary {
|
||||
public:
|
||||
using ElementOutput = ElementOutput_;
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
using ElementCompute = ElementCompute_;
|
||||
using UnaryArguments =
|
||||
typename GenericUnaryTraits<UnaryOp<ElementCompute>>::Arguments;
|
||||
|
||||
static bool const kIsHeavy = IsHeavy;
|
||||
static int const kElementsPerAccess = ElementsPerAccess;
|
||||
static int const kCount = ElementsPerAccess;
|
||||
static const cutlass::epilogue::thread::ScaleType::Kind kScale = Scale;
|
||||
|
||||
using FragmentOutput = cutlass::Array<ElementOutput, kElementsPerAccess>;
|
||||
using FragmentAccumulator =
|
||||
cutlass::Array<ElementAccumulator, kElementsPerAccess>;
|
||||
using FragmentSource = cutlass::Array<ElementOutput, kElementsPerAccess>;
|
||||
using FragmentCompute = cutlass::Array<ElementCompute, kElementsPerAccess>;
|
||||
|
||||
static cutlass::FloatRoundStyle const kRound = Round;
|
||||
|
||||
/// Host-constructable parameters structure
|
||||
struct Params {
|
||||
ElementCompute alpha; ///< scales accumulators
|
||||
ElementCompute beta; ///< scales source tensor
|
||||
ElementCompute const *alpha_ptr; ///< pointer to accumulator scalar - if
|
||||
///< not null, loads it from memory
|
||||
ElementCompute const *beta_ptr; ///< pointer to source scalar - if not
|
||||
///< null, loads it from memory
|
||||
UnaryArguments unary_args;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params()
|
||||
: alpha(ElementCompute(1)),
|
||||
beta(ElementCompute(0)),
|
||||
alpha_ptr(nullptr),
|
||||
beta_ptr(nullptr) {}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(ElementCompute alpha,
|
||||
ElementCompute beta = ElementCompute(0),
|
||||
UnaryArguments unary_args_ = UnaryArguments{})
|
||||
: alpha(alpha),
|
||||
beta(beta),
|
||||
alpha_ptr(nullptr),
|
||||
beta_ptr(nullptr),
|
||||
unary_args(unary_args_) {}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(ElementCompute const *alpha_ptr,
|
||||
ElementCompute const *beta_ptr = nullptr)
|
||||
: alpha(0), beta(0), alpha_ptr(alpha_ptr), beta_ptr(beta_ptr) {}
|
||||
};
|
||||
|
||||
private:
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
Params params_;
|
||||
bool skip_elementwise_;
|
||||
|
||||
public:
|
||||
/// Constructs the function object, possibly loading from pointers in host
|
||||
/// memory
|
||||
CUTLASS_HOST_DEVICE
|
||||
explicit LinearCombinationUnary(Params const ¶ms) {
|
||||
params_ = params;
|
||||
params_.alpha = (params.alpha_ptr ? *params.alpha_ptr : params.alpha);
|
||||
params_.beta = (params.beta_ptr ? *params.beta_ptr : params.beta);
|
||||
skip_elementwise_ = false;
|
||||
}
|
||||
|
||||
/// Returns true if source is needed
|
||||
CUTLASS_HOST_DEVICE
|
||||
bool is_source_needed() const {
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::NoBetaScaling)
|
||||
return params_.beta != ElementCompute(0);
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::OnlyAlphaScaling)
|
||||
return false;
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::Nothing) return false;
|
||||
|
||||
return params_.beta != ElementCompute(0);
|
||||
}
|
||||
|
||||
/// Functionally required for serial reduction in the epilogue
|
||||
CUTLASS_HOST_DEVICE
|
||||
void set_k_partition(int k_partition, int k_partition_count) {
|
||||
if (k_partition) {
|
||||
params_.beta = ElementCompute(1);
|
||||
}
|
||||
|
||||
if (k_partition != k_partition_count - 1) {
|
||||
skip_elementwise_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes linear scaling: D = alpha * accumulator + beta * source
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput operator()(FragmentAccumulator const &accumulator,
|
||||
FragmentOutput const &source) const {
|
||||
// Convert source to internal compute numeric type
|
||||
cutlass::NumericArrayConverter<ElementCompute,
|
||||
ElementOutput,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
source_converter;
|
||||
cutlass::NumericArrayConverter<ElementCompute,
|
||||
ElementAccumulator,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
accumulator_converter;
|
||||
|
||||
FragmentCompute converted_source = source_converter(source);
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
// Perform binary operations
|
||||
FragmentCompute intermediate;
|
||||
|
||||
cutlass::multiplies<FragmentCompute> mul_add_source;
|
||||
cutlass::multiply_add<FragmentCompute> mul_add_accumulator;
|
||||
UnaryOp<ElementCompute> unary_op;
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::NoBetaScaling) {
|
||||
intermediate = converted_source;
|
||||
// D = alpha * Accum + X
|
||||
intermediate = mul_add_accumulator(
|
||||
params_.alpha, converted_accumulator, intermediate);
|
||||
} else if (Scale == cutlass::epilogue::thread::ScaleType::Nothing) {
|
||||
intermediate = converted_accumulator;
|
||||
} else {
|
||||
// X = beta * C + uniform
|
||||
intermediate = mul_add_source(params_.beta, converted_source);
|
||||
// D = alpha * Accum + X
|
||||
intermediate = mul_add_accumulator(
|
||||
params_.alpha, converted_accumulator, intermediate);
|
||||
}
|
||||
|
||||
if constexpr (GenericUnaryTraits<
|
||||
UnaryOp<ElementCompute>>::IsArgumentsNeeded) {
|
||||
if (!skip_elementwise_) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = unary_op(intermediate[i], params_.unary_args);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!skip_elementwise_) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = unary_op(intermediate[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to destination numeric type
|
||||
cutlass::NumericArrayConverter<ElementOutput,
|
||||
ElementCompute,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
destination_converter;
|
||||
|
||||
return destination_converter(intermediate);
|
||||
}
|
||||
|
||||
/// Computes linear scaling: D = alpha * accumulator
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput operator()(FragmentAccumulator const &accumulator) const {
|
||||
// Convert source to internal compute numeric type
|
||||
cutlass::NumericArrayConverter<ElementCompute,
|
||||
ElementAccumulator,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
accumulator_converter;
|
||||
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
// Perform binary operations
|
||||
FragmentCompute intermediate;
|
||||
|
||||
cutlass::multiplies<FragmentCompute> mul_add_accumulator;
|
||||
UnaryOp<ElementCompute> unary_op;
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::Nothing) {
|
||||
intermediate = converted_accumulator;
|
||||
} else {
|
||||
// D = alpha * Accum
|
||||
intermediate = mul_add_accumulator(params_.alpha, converted_accumulator);
|
||||
}
|
||||
|
||||
if constexpr (GenericUnaryTraits<
|
||||
UnaryOp<FragmentCompute>>::IsArgumentsNeeded) {
|
||||
if (!skip_elementwise_) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = unary_op(intermediate[i], params_.unary_args);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!skip_elementwise_) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = unary_op(intermediate[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to destination numeric type
|
||||
cutlass::NumericArrayConverter<ElementOutput,
|
||||
ElementCompute,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
destination_converter;
|
||||
|
||||
return destination_converter(intermediate);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace thread
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass_patch
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
/*! \file
|
||||
\brief Functor performing linear combination operations used by epilogues.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/epilogue/thread/scale_type.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/array.h"
|
||||
#include "hytlass/epilogue/thread/scale_type.h"
|
||||
#include "hytlass/functional.h"
|
||||
#include "hytlass/numeric_conversion.h"
|
||||
#include "hytlass/numeric_types.h"
|
||||
#endif
|
||||
|
||||
#include "cutlass_patch/batched_matrix_coord.h"
|
||||
#include "cutlass_patch/trace_device.h"
|
||||
|
||||
namespace cutlass_patch {
|
||||
namespace epilogue {
|
||||
namespace thread {
|
||||
|
||||
template <class VariadicOp, class = void>
|
||||
struct GenericVariadicTraits {
|
||||
static constexpr bool IsArgumentsNeeded = false;
|
||||
struct Arguments {};
|
||||
};
|
||||
|
||||
template <class VariadicOp>
|
||||
struct GenericVariadicTraits<VariadicOp,
|
||||
decltype(typename VariadicOp::Arguments(),
|
||||
void())> {
|
||||
static constexpr bool IsArgumentsNeeded = true;
|
||||
using Arguments = typename VariadicOp::Arguments;
|
||||
};
|
||||
|
||||
/// Applies a linear combination operator to an array of elements.
|
||||
///
|
||||
/// D = VariadicOp(alpha * accumulator + beta * source)
|
||||
///
|
||||
template <
|
||||
template <typename T>
|
||||
class VariadicOp,
|
||||
typename ElementOutput_, ///< Data type used to load and store tensors
|
||||
int ElementsPerAccess, ///< Number of elements computed per operation.
|
||||
///< Usually it is 128/sizeof_bits<ElementOutput_>,
|
||||
///< but we use 64 or 32 sometimes when there are
|
||||
///< not enough data to store
|
||||
typename ElementAccumulator_ = ElementOutput_, ///< Accumulator data type
|
||||
typename ElementCompute_ =
|
||||
ElementOutput_, ///< Data type used to compute linear combination
|
||||
cutlass::epilogue::thread::ScaleType::Kind Scale =
|
||||
cutlass::epilogue::thread::ScaleType::Default, ///< Control Alpha and
|
||||
///< Beta scaling
|
||||
cutlass::FloatRoundStyle Round = cutlass::FloatRoundStyle::round_to_nearest,
|
||||
bool IsHeavy = false>
|
||||
class LinearCombinationVariadic {
|
||||
public:
|
||||
using ElementOutput = ElementOutput_;
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
using ElementCompute = ElementCompute_;
|
||||
using VariadicArguments =
|
||||
typename GenericVariadicTraits<VariadicOp<ElementCompute>>::Arguments;
|
||||
|
||||
static bool const kIsHeavy = IsHeavy;
|
||||
static int const kElementsPerAccess = ElementsPerAccess;
|
||||
static int const kCount = ElementsPerAccess;
|
||||
static const cutlass::epilogue::thread::ScaleType::Kind kScale = Scale;
|
||||
|
||||
using FragmentOutput = cutlass::Array<ElementOutput, kElementsPerAccess>;
|
||||
using FragmentAccumulator =
|
||||
cutlass::Array<ElementAccumulator, kElementsPerAccess>;
|
||||
using FragmentSource = cutlass::Array<ElementOutput, kElementsPerAccess>;
|
||||
using FragmentCompute = cutlass::Array<ElementCompute, kElementsPerAccess>;
|
||||
|
||||
static cutlass::FloatRoundStyle const kRound = Round;
|
||||
|
||||
/// Host-constructable parameters structure
|
||||
struct Params {
|
||||
ElementCompute alpha; ///< scales accumulators
|
||||
ElementCompute beta; ///< scales source tensor
|
||||
ElementCompute const *alpha_ptr; ///< pointer to accumulator scalar - if
|
||||
///< not null, loads it from memory
|
||||
ElementCompute const *beta_ptr; ///< pointer to source scalar - if not
|
||||
///< null, loads it from memory
|
||||
VariadicArguments variadic_args;
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params()
|
||||
: alpha(ElementCompute(1)),
|
||||
beta(ElementCompute(0)),
|
||||
alpha_ptr(nullptr),
|
||||
beta_ptr(nullptr) {}
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(ElementCompute alpha,
|
||||
ElementCompute beta,
|
||||
VariadicArguments variadic_args_ = VariadicArguments{})
|
||||
: alpha(alpha),
|
||||
beta(beta),
|
||||
alpha_ptr(nullptr),
|
||||
beta_ptr(nullptr),
|
||||
variadic_args(variadic_args_) {}
|
||||
};
|
||||
|
||||
private:
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
Params params_;
|
||||
bool skip_elementwise_;
|
||||
|
||||
public:
|
||||
/// Constructs the function object, possibly loading from pointers in host
|
||||
/// memory
|
||||
CUTLASS_HOST_DEVICE
|
||||
LinearCombinationVariadic(Params const ¶ms) {
|
||||
params_ = params;
|
||||
params_.alpha = (params.alpha_ptr ? *params.alpha_ptr : params.alpha);
|
||||
params_.beta = (params.beta_ptr ? *params.beta_ptr : params.beta);
|
||||
skip_elementwise_ = false;
|
||||
}
|
||||
|
||||
/// Returns true if source is needed
|
||||
CUTLASS_HOST_DEVICE
|
||||
bool is_source_needed() const {
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::NoBetaScaling)
|
||||
return params_.beta != ElementCompute(0);
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::OnlyAlphaScaling)
|
||||
return false;
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::Nothing) return false;
|
||||
|
||||
return params_.beta != ElementCompute(0);
|
||||
}
|
||||
|
||||
/// Functionally required for serial reduction in the epilogue
|
||||
CUTLASS_HOST_DEVICE
|
||||
void set_k_partition(int k_partition, int k_partition_count) {
|
||||
if (k_partition) {
|
||||
params_.beta = ElementCompute(1);
|
||||
}
|
||||
|
||||
if (k_partition != k_partition_count - 1) {
|
||||
skip_elementwise_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes linear scaling with source: D = alpha * accumulator + beta *
|
||||
/// source
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput operator()(FragmentAccumulator const &accumulator,
|
||||
FragmentSource const &source,
|
||||
int row_offset,
|
||||
int column_offset) const {
|
||||
CUTLASS_TRACE_DEVICE(
|
||||
"kElementsPerAccess: %d, row_offset: %d, column_offset: %d",
|
||||
kElementsPerAccess,
|
||||
row_offset,
|
||||
column_offset);
|
||||
|
||||
// Convert source to internal compute numeric type
|
||||
cutlass::NumericArrayConverter<ElementCompute,
|
||||
ElementOutput,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
source_converter;
|
||||
cutlass::NumericArrayConverter<ElementCompute,
|
||||
ElementAccumulator,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
accumulator_converter;
|
||||
|
||||
FragmentCompute converted_source = source_converter(source);
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
// Perform binary operations
|
||||
FragmentCompute intermediate;
|
||||
|
||||
cutlass::multiplies<FragmentCompute> mul_add_source;
|
||||
cutlass::multiply_add<FragmentCompute> mul_add_accumulator;
|
||||
VariadicOp<ElementCompute> variadic_op;
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::NoBetaScaling) {
|
||||
intermediate = converted_source;
|
||||
// D = alpha * Accum + X
|
||||
intermediate = mul_add_accumulator(
|
||||
params_.alpha, converted_accumulator, intermediate);
|
||||
} else if (Scale == cutlass::epilogue::thread::ScaleType::Nothing) {
|
||||
intermediate = converted_accumulator;
|
||||
} else {
|
||||
// X = beta * C + uniform
|
||||
intermediate = mul_add_source(params_.beta, converted_source);
|
||||
// D = alpha * Accum + X
|
||||
intermediate = mul_add_accumulator(
|
||||
params_.alpha, converted_accumulator, intermediate);
|
||||
}
|
||||
|
||||
if constexpr (GenericVariadicTraits<
|
||||
VariadicOp<ElementCompute>>::IsArgumentsNeeded) {
|
||||
if (!skip_elementwise_) {
|
||||
#if CUTLASS_EPILOGUE_ENABLE_VECTORIZE
|
||||
intermediate = variadic_op.Compute<kElementsPerAccess>(
|
||||
intermediate,
|
||||
params_.variadic_args,
|
||||
BatchedMatrixCoord(blockIdx.z, row_offset, column_offset));
|
||||
#else
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = variadic_op(
|
||||
intermediate[i],
|
||||
params_.variadic_args,
|
||||
BatchedMatrixCoord(blockIdx.z, row_offset, column_offset + i));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (!skip_elementwise_) {
|
||||
#if CUTLASS_EPILOGUE_ENABLE_VECTORIZE
|
||||
intermediate = variadic_op.Compute<kElementsPerAccess>(intermediate);
|
||||
#else
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = variadic_op(intermediate[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to destination numeric type
|
||||
cutlass::NumericArrayConverter<ElementOutput,
|
||||
ElementCompute,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
destination_converter;
|
||||
|
||||
return destination_converter(intermediate);
|
||||
}
|
||||
|
||||
/// Computes linear scaling: D = alpha * accumulator
|
||||
CUTLASS_HOST_DEVICE
|
||||
FragmentOutput operator()(FragmentAccumulator const &accumulator,
|
||||
int row_offset,
|
||||
int column_offset) const {
|
||||
CUTLASS_TRACE_DEVICE(
|
||||
"kElementsPerAccess: %d, row_offset: %d, column_offset: %d",
|
||||
kElementsPerAccess,
|
||||
row_offset,
|
||||
column_offset);
|
||||
|
||||
// Convert source to internal compute numeric type
|
||||
cutlass::NumericArrayConverter<ElementCompute,
|
||||
ElementAccumulator,
|
||||
kElementsPerAccess,
|
||||
Round>
|
||||
accumulator_converter;
|
||||
|
||||
FragmentCompute converted_accumulator = accumulator_converter(accumulator);
|
||||
|
||||
// Perform binary operations
|
||||
FragmentCompute intermediate;
|
||||
|
||||
cutlass::multiplies<FragmentCompute> mul_accumulator;
|
||||
VariadicOp<ElementCompute> variadic_op;
|
||||
|
||||
if (Scale == cutlass::epilogue::thread::ScaleType::Nothing) {
|
||||
intermediate = converted_accumulator;
|
||||
} else {
|
||||
// D = alpha * Accum
|
||||
intermediate = mul_accumulator(params_.alpha, converted_accumulator);
|
||||
}
|
||||
|
||||
if constexpr (GenericVariadicTraits<
|
||||
VariadicOp<FragmentCompute>>::IsArgumentsNeeded) {
|
||||
if (!skip_elementwise_) {
|
||||
#if CUTLASS_EPILOGUE_ENABLE_VECTORIZE
|
||||
intermediate = variadic_op.Compute<kElementsPerAccess>(
|
||||
intermediate,
|
||||
params_.variadic_args,
|
||||
BatchedMatrixCoord(blockIdx.z, row_offset, column_offset));
|
||||
#else
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = variadic_op(
|
||||
intermediate[i],
|
||||
params_.variadic_args,
|
||||
BatchedMatrixCoord(blockIdx.z, row_offset, column_offset + i));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (!skip_elementwise_) {
|
||||
#if CUTLASS_EPILOGUE_ENABLE_VECTORIZE
|
||||
intermediate = variadic_op.Compute<kElementsPerAccess>(intermediate);
|
||||
#else
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < kElementsPerAccess; ++i) {
|
||||
intermediate[i] = variadic_op(intermediate[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to destination numeric type
|
||||
cutlass::NumericArrayConverter<ElementOutput, ElementCompute, kCount, Round>
|
||||
destination_converter;
|
||||
|
||||
return destination_converter(intermediate);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace thread
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass_patch
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
/*! \file
|
||||
\brief Epilogue for threadblock scoped GEMMs using Tensor Ops.
|
||||
|
||||
The epilogue rearranges the result of a matrix product through shared memory
|
||||
to match canonical tensor layouts in global memory. Epilogues support
|
||||
conversion and reduction operations.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
|
||||
#include "cutlass/epilogue/threadblock/default_epilogue_tensor_op.h"
|
||||
#include "cutlass/epilogue/threadblock/default_epilogue_volta_tensor_op.h"
|
||||
#include "cutlass/epilogue/threadblock/epilogue.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/array.h"
|
||||
#include "hytlass/numeric_types.h"
|
||||
|
||||
#include "hytlass/gemm/gemm.h"
|
||||
|
||||
#include "hytlass/epilogue/threadblock/default_epilogue_tensor_op.h"
|
||||
#include "hytlass/epilogue/threadblock/default_epilogue_volta_tensor_op.h"
|
||||
#include "hytlass/epilogue/threadblock/epilogue.h"
|
||||
|
||||
#include "hytlass/layout/permute.h"
|
||||
#endif
|
||||
|
||||
#include "cutlass_patch/epilogue/threadblock/epilogue_with_variadic.h"
|
||||
// #include "cutlass/epilogue/threadblock/epilogue_streamk_with_broadcast.h"
|
||||
|
||||
namespace cutlass_patch {
|
||||
namespace epilogue {
|
||||
namespace threadblock {
|
||||
|
||||
/// Defines sensible defaults for epilogues for SimtOps.
|
||||
template <typename Shape,
|
||||
typename WarpMmaSimt,
|
||||
typename ElementOutput,
|
||||
typename OutputOp,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = cutlass::layout::NoPermute,
|
||||
cutlass::conv::StrideSupport StrideSupport =
|
||||
cutlass::conv::StrideSupport::kUnity,
|
||||
int Rank = 4>
|
||||
struct DefaultEpilogueWithVariadicSimt {
|
||||
static cutlass::conv::StrideSupport const kStrideSupport = StrideSupport;
|
||||
static int const kRank = Rank;
|
||||
|
||||
static bool const UseCUDAStore =
|
||||
cutlass::platform::is_same<ElementOutput, double>::value;
|
||||
|
||||
/// Use defaults related to the existing epilogue
|
||||
using Base = cutlass::epilogue::threadblock::
|
||||
DefaultEpilogueSimt<Shape, WarpMmaSimt, OutputOp, ElementsPerAccess>;
|
||||
|
||||
using PackedOutputTileIterator =
|
||||
cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD,
|
||||
PermuteDLayout,
|
||||
UseCUDAStore>;
|
||||
|
||||
using StridedOutputTileIterator =
|
||||
cutlass::epilogue::threadblock::PredicatedTileIteratorConv<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD,
|
||||
PermuteDLayout,
|
||||
UseCUDAStore,
|
||||
kRank>;
|
||||
|
||||
//
|
||||
// Stores the result z = (y = GEMM(A, B, C), variadic)
|
||||
//
|
||||
using OutputTileIterator = typename cutlass::platform::conditional<
|
||||
StrideSupport == cutlass::conv::StrideSupport::kUnity,
|
||||
PackedOutputTileIterator,
|
||||
StridedOutputTileIterator>::type;
|
||||
|
||||
//
|
||||
// Define the epilogue
|
||||
//
|
||||
using Epilogue = cutlass_patch::epilogue::threadblock::EpilogueWithVariadic<
|
||||
Shape,
|
||||
WarpMmaSimt,
|
||||
Base::kPartitionsK,
|
||||
OutputTileIterator,
|
||||
typename Base::AccumulatorFragmentIterator,
|
||||
typename Base::WarpTileIterator,
|
||||
typename Base::SharedLoadIterator,
|
||||
OutputOp,
|
||||
typename Base::Padding>;
|
||||
};
|
||||
|
||||
/// Defines sensible defaults for strided dgrad epilogues for SimtOps.
|
||||
template <typename Shape,
|
||||
typename WarpMmaSimt,
|
||||
typename ElementOutput,
|
||||
typename OutputOp,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = cutlass::layout::NoPermute>
|
||||
struct DefaultEpilogueWithVariadicSimtStridedDgrad {
|
||||
/// Use defaults related to the existing epilogue
|
||||
using Base = cutlass::epilogue::threadblock::DefaultEpilogueSimtStridedDgrad<
|
||||
Shape,
|
||||
WarpMmaSimt,
|
||||
OutputOp,
|
||||
ElementsPerAccess>;
|
||||
|
||||
//
|
||||
// Stores the result z = (y = GEMM(A, B, C), variadic)
|
||||
//
|
||||
using OutputTileIterator =
|
||||
cutlass::epilogue::threadblock::PredicatedTileIteratorStridedDgrad<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput>;
|
||||
|
||||
//
|
||||
// Define the epilogue
|
||||
//
|
||||
using Epilogue = cutlass_patch::epilogue::threadblock::EpilogueWithVariadic<
|
||||
Shape,
|
||||
WarpMmaSimt,
|
||||
Base::kPartitionsK,
|
||||
OutputTileIterator,
|
||||
typename Base::AccumulatorFragmentIterator,
|
||||
typename Base::WarpTileIterator,
|
||||
typename Base::SharedLoadIterator,
|
||||
OutputOp,
|
||||
typename Base::Padding>;
|
||||
};
|
||||
|
||||
/// Defines sensible defaults for epilogues for TensorOps.
|
||||
template <typename Shape,
|
||||
typename WarpMmaTensorOp,
|
||||
int PartitionsK,
|
||||
typename ElementOutput,
|
||||
typename OutputOp,
|
||||
int ElementsPerAccess,
|
||||
bool ScatterD = false,
|
||||
typename PermuteDLayout = cutlass::layout::NoPermute>
|
||||
struct DefaultEpilogueWithVariadicTensorOp {
|
||||
/// Use defaults related to the existing epilogue
|
||||
using Base = cutlass::epilogue::threadblock::DefaultEpilogueTensorOp<
|
||||
Shape,
|
||||
WarpMmaTensorOp,
|
||||
PartitionsK,
|
||||
OutputOp,
|
||||
ElementsPerAccess>;
|
||||
|
||||
//
|
||||
// Stores the result z = (y = GEMM(A, B, C), variadic)
|
||||
//
|
||||
using OutputTileIterator =
|
||||
cutlass::epilogue::threadblock::PredicatedTileIterator<
|
||||
typename Base::OutputTileThreadMap,
|
||||
ElementOutput,
|
||||
ScatterD,
|
||||
PermuteDLayout>;
|
||||
|
||||
//
|
||||
// Define the epilogue
|
||||
//
|
||||
using Epilogue = cutlass_patch::epilogue::threadblock::EpilogueWithVariadic<
|
||||
Shape,
|
||||
WarpMmaTensorOp,
|
||||
PartitionsK,
|
||||
OutputTileIterator,
|
||||
typename Base::AccumulatorFragmentIterator,
|
||||
typename Base::WarpTileIterator,
|
||||
typename Base::SharedLoadIterator,
|
||||
OutputOp,
|
||||
typename Base::Padding,
|
||||
Base::kFragmentsPerIteration>;
|
||||
};
|
||||
|
||||
/// Defines sensible defaults for epilogues for VoltaTensorOps.
|
||||
template <typename Shape,
|
||||
typename WarpMmaTensorOp,
|
||||
int PartitionsK,
|
||||
typename ElementOutput,
|
||||
typename OutputOp,
|
||||
int ElementsPerAccess>
|
||||
struct DefaultEpilogueWithVariadicVoltaTensorOp {
|
||||
/// Use defaults related to the existing epilogue
|
||||
using Base = cutlass::epilogue::threadblock::DefaultEpilogueVoltaTensorOp<
|
||||
Shape,
|
||||
WarpMmaTensorOp,
|
||||
PartitionsK,
|
||||
OutputOp,
|
||||
ElementsPerAccess>;
|
||||
|
||||
//
|
||||
// Stores the result z = (y = GEMM(A, B, C), variadic)
|
||||
//
|
||||
using OutputTileIterator = cutlass::epilogue::threadblock::
|
||||
PredicatedTileIterator<typename Base::OutputTileThreadMap, ElementOutput>;
|
||||
|
||||
//
|
||||
// Define the epilogue
|
||||
//
|
||||
using Epilogue = cutlass_patch::epilogue::threadblock::EpilogueWithVariadic<
|
||||
Shape,
|
||||
WarpMmaTensorOp,
|
||||
PartitionsK,
|
||||
OutputTileIterator,
|
||||
typename Base::AccumulatorFragmentIterator,
|
||||
typename Base::WarpTileIterator,
|
||||
typename Base::SharedLoadIterator,
|
||||
OutputOp,
|
||||
typename Base::Padding>;
|
||||
};
|
||||
|
||||
} // namespace threadblock
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass_patch
|
||||
+666
@@ -0,0 +1,666 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
/*! \file
|
||||
\brief Epilogue for threadblock scoped GEMMs using Tensor Ops.
|
||||
|
||||
The epilogue rearranges the result of a matrix product through shared memory
|
||||
to match canonical tensor layouts in global memory. Epilogues support
|
||||
conversion and reduction operations.
|
||||
|
||||
The shared memory resource is time-sliced across warps.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include <cuda/std/cassert>
|
||||
|
||||
#include "cutlass/aligned_buffer.h"
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/layout/tensor.h"
|
||||
#include "cutlass/layout/vector.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/tensor_coord.h"
|
||||
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
|
||||
#include "cutlass/transform/pitch_linear_thread_map.h"
|
||||
#include "cutlass/transform/threadblock/regular_tile_iterator.h"
|
||||
|
||||
#include "cutlass/epilogue/threadblock/epilogue_base.h"
|
||||
#include "cutlass/epilogue/threadblock/epilogue_base_streamk.h"
|
||||
#include "cutlass/epilogue/threadblock/predicated_tile_iterator.h"
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/aligned_buffer.h"
|
||||
#include "hytlass/array.h"
|
||||
#include "hytlass/functional.h"
|
||||
#include "hytlass/layout/tensor.h"
|
||||
#include "hytlass/layout/vector.h"
|
||||
#include "hytlass/numeric_types.h"
|
||||
#include "hytlass/tensor_coord.h"
|
||||
|
||||
#include "hytlass/gemm/gemm.h"
|
||||
|
||||
#include "hytlass/transform/pitch_linear_thread_map.h"
|
||||
#include "hytlass/transform/threadblock/regular_tile_iterator.h"
|
||||
|
||||
#include "hytlass/epilogue/threadblock/epilogue_base.h"
|
||||
#include "hytlass/epilogue/threadblock/epilogue_base_streamk.h"
|
||||
#include "hytlass/epilogue/threadblock/predicated_tile_iterator.h"
|
||||
#endif
|
||||
|
||||
#include "cutlass_patch/trace_device.h"
|
||||
|
||||
namespace cutlass_patch {
|
||||
namespace epilogue {
|
||||
namespace threadblock {
|
||||
|
||||
/// Epilogue operator
|
||||
template <
|
||||
typename Shape_, ///< Shape of threadblock tile (concept: GemmShape)
|
||||
typename WarpMmaOperator_, ///< Warp-level MMA operator (concept:
|
||||
///< gemm::warp::MmaTensorOp)
|
||||
int PartitionsK, ///< Number of partitions of the K dimension
|
||||
typename OutputTileIterator_, ///< Tile iterator reading and writing
|
||||
///< output tensors
|
||||
typename AccumulatorFragmentIterator_, ///< Fragment iterator
|
||||
///< selecting accumulators
|
||||
typename WarpTileIterator_, ///< Warp-scoped tile iterator writing
|
||||
///< accumulators to SMEM
|
||||
typename SharedLoadIterator_, ///< Threadblock-scoped tile iterator
|
||||
///< loading from SMEM
|
||||
typename OutputOp_, ///< Output operator
|
||||
typename Padding_, ///< Padding added to SMEM allocation to avoid
|
||||
///< bank conflicts (concept: MatrixShape)
|
||||
int FragmentsPerPartition =
|
||||
1, ///< Used to coarsten the epilogue granularity
|
||||
int IterationsUnroll = ///< Used to reduce binary size when epilogue
|
||||
///< op is large
|
||||
(!cutlass::epilogue::threadblock::IsEpilogueFunctorHeavy<OutputOp_>::value)>
|
||||
class EpilogueWithVariadic
|
||||
: public cutlass::epilogue::threadblock::EpilogueBase<
|
||||
Shape_,
|
||||
typename WarpMmaOperator_::Shape,
|
||||
PartitionsK,
|
||||
AccumulatorFragmentIterator_,
|
||||
WarpTileIterator_,
|
||||
Padding_,
|
||||
FragmentsPerPartition>,
|
||||
public cutlass::epilogue::threadblock::EpilogueBaseStreamK<
|
||||
Shape_,
|
||||
PartitionsK,
|
||||
WarpMmaOperator_,
|
||||
AccumulatorFragmentIterator_> {
|
||||
public:
|
||||
using Base = cutlass::epilogue::threadblock::EpilogueBase<
|
||||
Shape_,
|
||||
typename WarpMmaOperator_::Shape,
|
||||
PartitionsK,
|
||||
AccumulatorFragmentIterator_,
|
||||
WarpTileIterator_,
|
||||
Padding_,
|
||||
FragmentsPerPartition>;
|
||||
|
||||
using BaseStreamK = cutlass::epilogue::threadblock::EpilogueBaseStreamK<
|
||||
Shape_,
|
||||
PartitionsK,
|
||||
WarpMmaOperator_,
|
||||
AccumulatorFragmentIterator_>;
|
||||
|
||||
using Shape = Shape_;
|
||||
using WarpMmaOperator = WarpMmaOperator_;
|
||||
static int const kPartitionsK = PartitionsK;
|
||||
using OutputTileIterator = OutputTileIterator_;
|
||||
using AccumulatorFragmentIterator = AccumulatorFragmentIterator_;
|
||||
using WarpTileIterator = WarpTileIterator_;
|
||||
using SharedLoadIterator = SharedLoadIterator_;
|
||||
using OutputOp = OutputOp_;
|
||||
using Padding = Padding_;
|
||||
using Layout = cutlass::layout::RowMajor;
|
||||
using LongIndex = typename Layout::LongIndex;
|
||||
|
||||
/// Number of warps per block
|
||||
using WarpCount = typename Base::WarpCount;
|
||||
|
||||
/// Number of threads per block
|
||||
static int const kBlockThreads = 32 * WarpCount::kCount;
|
||||
|
||||
/// Per-thread accumulator tile type
|
||||
using AccumulatorTile = typename Base::AccumulatorTile;
|
||||
|
||||
/// Numerical accumulation element type
|
||||
using ElementAccumulator = typename WarpMmaOperator::ElementC;
|
||||
|
||||
/// Fragment type used by the accumulator tile's fragment iterator
|
||||
using AccumulatorFragment = typename AccumulatorFragmentIterator::Fragment;
|
||||
|
||||
/// Output element
|
||||
using ElementOutput = typename OutputTileIterator::Element;
|
||||
|
||||
/// Output access size
|
||||
static int const kElementsPerAccess = OutputTileIterator::kElementsPerAccess;
|
||||
|
||||
/// Tensor reference to destination tensor
|
||||
using TensorRef = typename OutputTileIterator::TensorRef;
|
||||
|
||||
/// Tensor reference to sync tensor
|
||||
using SyncTensorRef =
|
||||
typename cutlass::TensorRef<int, cutlass::layout::PackedVectorLayout>;
|
||||
|
||||
/// Const tensor reference to source tensor
|
||||
using ConstTensorRef = typename OutputTileIterator::ConstTensorRef;
|
||||
|
||||
/// Vector type used by the global output iterator
|
||||
using OutputAccessType =
|
||||
cutlass::Array<typename OutputTileIterator::Element,
|
||||
OutputTileIterator::kElementsPerAccess>;
|
||||
|
||||
/// Vector type used by the shared output iterator
|
||||
using AccumulatorAccessType =
|
||||
cutlass::Array<typename WarpTileIterator::Element,
|
||||
OutputTileIterator::kElementsPerAccess>;
|
||||
|
||||
static int constexpr kSmemTiles = Base::kFragmentsPerIteration > 1
|
||||
? Base::kFragmentsPerIteration
|
||||
: kPartitionsK;
|
||||
|
||||
static int constexpr kSmemPointerOffset =
|
||||
Base::SharedStorage::StorageShape::kCount / kSmemTiles;
|
||||
|
||||
public:
|
||||
static_assert(
|
||||
SharedLoadIterator::Fragment::kElements ==
|
||||
OutputTileIterator::Fragment::kElements,
|
||||
"Mismatch between shared load iterator and output tile iterator.");
|
||||
|
||||
static_assert(OutputTileIterator::kElementsPerAccess,
|
||||
"OutputTileIterator::kElementsPerAccess must not be zero.");
|
||||
|
||||
static_assert(!(OutputTileIterator::Fragment::kElements %
|
||||
OutputTileIterator::kElementsPerAccess),
|
||||
"Divisibility");
|
||||
|
||||
static_assert(kPartitionsK == 1 || Base::kFragmentsPerIteration == 1,
|
||||
"One of these must be exactly 1.");
|
||||
|
||||
public:
|
||||
/// Aspect for when epilogue source is not needed
|
||||
struct SourceAspectNotNeeded {
|
||||
/// Constructor
|
||||
CUTLASS_DEVICE
|
||||
SourceAspectNotNeeded() {}
|
||||
|
||||
// No-op
|
||||
CUTLASS_DEVICE
|
||||
void load() {}
|
||||
|
||||
/// Invoke the output functor over each vector of output
|
||||
CUTLASS_DEVICE
|
||||
void apply_output_operator(
|
||||
const OutputTileIterator &output_iterator,
|
||||
typename OutputTileIterator::Fragment &output_fragment, // NOLINT
|
||||
OutputOp const &output_op,
|
||||
typename SharedLoadIterator::Fragment const &aligned_accum_fragment) {
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
|
||||
OutputAccessType *output_frag_ptr =
|
||||
reinterpret_cast<OutputAccessType *>(&output_fragment);
|
||||
|
||||
AccumulatorAccessType const *compute_frag_ptr =
|
||||
reinterpret_cast<AccumulatorAccessType const *>(
|
||||
&aligned_accum_fragment);
|
||||
|
||||
const int32_t thread_start_row = output_iterator.thread_start_row();
|
||||
const int32_t thread_start_column = output_iterator.thread_start_column();
|
||||
|
||||
const typename OutputTileIterator::Index extent_row =
|
||||
output_iterator.extent_row();
|
||||
const typename OutputTileIterator::Index extent_column =
|
||||
output_iterator.extent_column();
|
||||
|
||||
using ThreadMap = typename OutputTileIterator::ThreadMap;
|
||||
|
||||
typename OutputTileIterator::Mask mask;
|
||||
output_iterator.get_mask(mask);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int cluster = 0; cluster < ThreadMap::Iterations::kCluster;
|
||||
++cluster) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int group = 0; group < ThreadMap::Iterations::kGroup; ++group) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int row = 0; row < ThreadMap::Iterations::kRow; ++row) {
|
||||
int frag_row_idx =
|
||||
(row + ThreadMap::Iterations::kRow *
|
||||
(group + ThreadMap::Iterations::kGroup * cluster));
|
||||
|
||||
int row_offset = thread_start_row + row * ThreadMap::Delta::kRow +
|
||||
group * ThreadMap::Delta::kGroup +
|
||||
cluster * ThreadMap::Delta::kCluster;
|
||||
|
||||
bool row_guard = row_offset < extent_row;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int column = 0; column < ThreadMap::Iterations::kColumn;
|
||||
++column) {
|
||||
bool guard = row_guard && mask.predicates[column];
|
||||
if (!guard) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int column_offset =
|
||||
thread_start_column + column * ThreadMap::Delta::kColumn;
|
||||
int frag_offset =
|
||||
frag_row_idx * ThreadMap::Iterations::kColumn + column;
|
||||
|
||||
output_frag_ptr[frag_offset] = output_op(
|
||||
compute_frag_ptr[frag_offset], row_offset, column_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Aspect for when epilogue source is needed
|
||||
struct SourceAspectNeeded {
|
||||
OutputTileIterator source_iterator;
|
||||
|
||||
typename OutputTileIterator::Fragment source_fragment;
|
||||
|
||||
/// Invoke the output functor over each vector of output
|
||||
CUTLASS_DEVICE
|
||||
static void apply_output_operator(
|
||||
const OutputTileIterator &output_iterator,
|
||||
typename OutputTileIterator::Fragment &output_fragment, // NOLINT
|
||||
OutputOp const &output_op,
|
||||
typename SharedLoadIterator::Fragment const &aligned_accum_fragment,
|
||||
typename OutputTileIterator::Fragment const &source_fragment) {
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
|
||||
OutputAccessType *output_frag_ptr =
|
||||
reinterpret_cast<OutputAccessType *>(&output_fragment);
|
||||
|
||||
AccumulatorAccessType const *compute_frag_ptr =
|
||||
reinterpret_cast<AccumulatorAccessType const *>(
|
||||
&aligned_accum_fragment);
|
||||
|
||||
OutputAccessType const *source_frag_ptr =
|
||||
reinterpret_cast<OutputAccessType const *>(&source_fragment);
|
||||
|
||||
typename OutputTileIterator::Element const *source_ptr =
|
||||
reinterpret_cast<typename OutputTileIterator::Element const *>(
|
||||
&source_fragment);
|
||||
|
||||
const int32_t thread_start_row = output_iterator.thread_start_row();
|
||||
const int32_t thread_start_column = output_iterator.thread_start_column();
|
||||
|
||||
const typename OutputTileIterator::Index extent_row =
|
||||
output_iterator.extent_row();
|
||||
const typename OutputTileIterator::Index extent_column =
|
||||
output_iterator.extent_column();
|
||||
|
||||
using ThreadMap = typename OutputTileIterator::ThreadMap;
|
||||
|
||||
typename OutputTileIterator::Mask mask;
|
||||
output_iterator.get_mask(mask);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int cluster = 0; cluster < ThreadMap::Iterations::kCluster;
|
||||
++cluster) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int group = 0; group < ThreadMap::Iterations::kGroup; ++group) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int row = 0; row < ThreadMap::Iterations::kRow; ++row) {
|
||||
int frag_row_idx =
|
||||
(row + ThreadMap::Iterations::kRow *
|
||||
(group + ThreadMap::Iterations::kGroup * cluster));
|
||||
|
||||
int row_offset = thread_start_row + row * ThreadMap::Delta::kRow +
|
||||
group * ThreadMap::Delta::kGroup +
|
||||
cluster * ThreadMap::Delta::kCluster;
|
||||
|
||||
bool row_guard = row_offset < extent_row;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int column = 0; column < ThreadMap::Iterations::kColumn;
|
||||
++column) {
|
||||
bool guard = row_guard && mask.predicates[column];
|
||||
if (!guard) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int column_offset =
|
||||
thread_start_column + column * ThreadMap::Delta::kColumn;
|
||||
int frag_offset =
|
||||
frag_row_idx * ThreadMap::Iterations::kColumn + column;
|
||||
|
||||
output_frag_ptr[frag_offset] =
|
||||
output_op(compute_frag_ptr[frag_offset],
|
||||
source_frag_ptr[frag_offset],
|
||||
row_offset,
|
||||
column_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_DEVICE
|
||||
explicit SourceAspectNeeded(OutputTileIterator source_iterator)
|
||||
: source_iterator(source_iterator) {
|
||||
source_fragment.clear();
|
||||
}
|
||||
|
||||
// Load addend source fragment from global memory
|
||||
CUTLASS_DEVICE
|
||||
void load() {
|
||||
source_iterator.load(source_fragment);
|
||||
++source_iterator;
|
||||
}
|
||||
|
||||
/// Invoke the output functor over each vector of output
|
||||
CUTLASS_DEVICE
|
||||
void apply_output_operator(
|
||||
const OutputTileIterator &output_iterator,
|
||||
typename OutputTileIterator::Fragment &output_fragment, // NOLINT
|
||||
OutputOp const &output_op,
|
||||
typename SharedLoadIterator::Fragment const &aligned_accum_fragment) {
|
||||
apply_output_operator(output_iterator,
|
||||
output_fragment,
|
||||
output_op,
|
||||
aligned_accum_fragment,
|
||||
source_fragment);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
/// Loads fragment from shared memory aligned with output tensor
|
||||
SharedLoadIterator shared_load_iterator_;
|
||||
|
||||
/// Thread index in the threadblock
|
||||
int thread_idx;
|
||||
|
||||
/// Warp index in the threadblock
|
||||
int warp_idx;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
CUTLASS_DEVICE
|
||||
EpilogueWithVariadic(
|
||||
typename Base::SharedStorage
|
||||
&shared_storage, // NOLINT ///< Shared storage object
|
||||
int thread_idx, ///< ID of a thread within the threadblock
|
||||
int warp_idx, ///< ID of warp within threadblock
|
||||
int lane_idx) ///< Id of thread within warp
|
||||
: Base(shared_storage, thread_idx, warp_idx, lane_idx),
|
||||
BaseStreamK(thread_idx),
|
||||
shared_load_iterator_(shared_storage.reference(), thread_idx),
|
||||
thread_idx(thread_idx),
|
||||
warp_idx(warp_idx) {}
|
||||
|
||||
/// Aggregates the accumulator sets shared by peer blocks in the global
|
||||
/// workspace, performing epilogue computations, writing to output
|
||||
CUTLASS_DEVICE
|
||||
void reduce(int peer_idx_begin,
|
||||
int peer_idx_end,
|
||||
int reduce_fragment_idx,
|
||||
void *element_workspace,
|
||||
OutputOp const &output_op, ///< Output operator
|
||||
OutputTileIterator
|
||||
destination_iterator, ///< Tile iterator for destination
|
||||
OutputTileIterator
|
||||
source_iterator) { ///< Threadblock tile coordinate in GEMM
|
||||
///< (in units of threadblock tiles)
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
|
||||
// Reduce peer accumulator fragments into one fragment
|
||||
AccumulatorFragment accum_fragment;
|
||||
BaseStreamK::reduce(accum_fragment,
|
||||
peer_idx_begin,
|
||||
peer_idx_end,
|
||||
reduce_fragment_idx,
|
||||
element_workspace);
|
||||
|
||||
// Store fragment to shared memory
|
||||
this->warp_tile_iterator_.store(accum_fragment);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Initialize/load source-fragment data
|
||||
typename OutputTileIterator::Fragment source_fragment;
|
||||
source_fragment.clear();
|
||||
|
||||
if (output_op.is_source_needed()) {
|
||||
source_iterator += reduce_fragment_idx;
|
||||
source_iterator.load(source_fragment);
|
||||
}
|
||||
|
||||
// Load fragment from shared memory
|
||||
typename SharedLoadIterator::Fragment aligned_accum_fragment;
|
||||
shared_load_iterator_.load(aligned_accum_fragment);
|
||||
|
||||
// Add fragments shared by other k partitions
|
||||
if (kPartitionsK > 1) {
|
||||
cutlass::plus<typename SharedLoadIterator::Fragment> add_fragments;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 1; i < kPartitionsK; ++i) {
|
||||
typename SharedLoadIterator::Fragment aligned_addend_fragment;
|
||||
shared_load_iterator_.add_pointer_offset(kSmemPointerOffset);
|
||||
shared_load_iterator_.load(aligned_addend_fragment);
|
||||
aligned_accum_fragment =
|
||||
add_fragments(aligned_accum_fragment, aligned_addend_fragment);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the output result
|
||||
typename OutputTileIterator::Fragment output_fragment;
|
||||
|
||||
// Apply the output operator
|
||||
SourceAspectNeeded::apply_output_operator(
|
||||
output_fragment, output_op, aligned_accum_fragment, source_fragment);
|
||||
|
||||
// Store the final result
|
||||
destination_iterator += reduce_fragment_idx;
|
||||
destination_iterator.store(output_fragment);
|
||||
}
|
||||
|
||||
/// Perform the epilogue computations and stream the result to global memory.
|
||||
CUTLASS_DEVICE
|
||||
void operator()(OutputOp const &output_op, ///< Output operator
|
||||
OutputTileIterator
|
||||
destination_iterator, ///< Tile iterator for destination
|
||||
AccumulatorTile const &
|
||||
accumulators) { ///< Complete warp-level accumulator tile
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
operator()(
|
||||
output_op, destination_iterator, accumulators, SourceAspectNotNeeded());
|
||||
}
|
||||
|
||||
/// Perform the epilogue computations and stream the result to global memory.
|
||||
/// Implements two alternative codepaths, depending on whether the output op
|
||||
/// requires addend data to be loaded.
|
||||
CUTLASS_DEVICE
|
||||
void operator()(OutputOp const &output_op, ///< Output operator
|
||||
OutputTileIterator
|
||||
destination_iterator, ///< Tile iterator for destination
|
||||
AccumulatorTile const
|
||||
&accumulators, ///< Complete warp-level accumulator tile
|
||||
OutputTileIterator
|
||||
source_iterator) { ///< Tile iterator for addend source
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
if (output_op.is_source_needed()) {
|
||||
operator()(output_op,
|
||||
destination_iterator,
|
||||
accumulators,
|
||||
SourceAspectNeeded(source_iterator));
|
||||
} else {
|
||||
operator()(output_op,
|
||||
destination_iterator,
|
||||
accumulators,
|
||||
SourceAspectNotNeeded());
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform the epilogue computations and stream the result to global memory.
|
||||
/// Implements a single codepath, regardless of whether the output op requires
|
||||
/// addend data to be loaded
|
||||
CUTLASS_DEVICE
|
||||
void unified(OutputOp const &output_op, ///< Output operator
|
||||
OutputTileIterator
|
||||
destination_iterator, ///< Tile iterator for destination
|
||||
AccumulatorTile const
|
||||
&accumulators, ///< Complete warp-level accumulator tile
|
||||
OutputTileIterator
|
||||
source_iterator) { ///< Tile iterator for addend source
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
if (!output_op.is_source_needed()) {
|
||||
source_iterator.clear_mask();
|
||||
__syncthreads(); // Dummy (CUDA 11.0)
|
||||
}
|
||||
|
||||
operator()(output_op,
|
||||
destination_iterator,
|
||||
accumulators,
|
||||
SourceAspectNeeded(source_iterator));
|
||||
}
|
||||
|
||||
template <class Seq>
|
||||
struct acc2smem;
|
||||
|
||||
template <size_t... Seq>
|
||||
struct acc2smem<cutlass::index_sequence<Seq...>> {
|
||||
template <int Advance>
|
||||
CUTLASS_DEVICE static void helper(
|
||||
AccumulatorFragmentIterator accum_fragment_iterator,
|
||||
WarpTileIterator &warp_tile_iterator) { // NOLINT
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < Advance; i++) {
|
||||
++accum_fragment_iterator;
|
||||
}
|
||||
|
||||
typename AccumulatorFragmentIterator::Fragment accum_fragment;
|
||||
|
||||
accum_fragment_iterator.load(accum_fragment);
|
||||
++accum_fragment_iterator;
|
||||
warp_tile_iterator.store(accum_fragment);
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
static void push(size_t pos,
|
||||
AccumulatorFragmentIterator const &iterator_begin,
|
||||
WarpTileIterator &warp_tile_iterator) { // NOLINT
|
||||
int dummy[] = {(pos == Seq) &&
|
||||
(helper<Seq>(iterator_begin, warp_tile_iterator), 0)...};
|
||||
}
|
||||
};
|
||||
|
||||
/// Streams the result to global memory
|
||||
template <typename SourceAspect>
|
||||
CUTLASS_DEVICE void operator()(
|
||||
OutputOp const &output_op, ///< Output operator
|
||||
OutputTileIterator
|
||||
destination_iterator, ///< Tile iterator for destination
|
||||
AccumulatorTile const
|
||||
&accumulators, ///< Complete warp-level accumulator tile
|
||||
SourceAspect source) {
|
||||
CUTLASS_TRACE_DEVICE("");
|
||||
|
||||
// Iterator over warp-level accumulator fragment
|
||||
AccumulatorFragmentIterator accum_fragment_iterator(accumulators);
|
||||
|
||||
//
|
||||
// Iterate over accumulator tile
|
||||
//
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wcuda-compat"
|
||||
// Turn off clangs warning about loop unroll argument using parens.
|
||||
#endif
|
||||
|
||||
#pragma unroll(IterationsUnroll ? OutputTileIterator::kIterations : 1)
|
||||
for (int iter = 0; iter < OutputTileIterator::kIterations; ++iter) {
|
||||
//
|
||||
// Load the source
|
||||
//
|
||||
|
||||
source.load();
|
||||
//
|
||||
// Convert and store fragment
|
||||
//
|
||||
|
||||
__syncthreads();
|
||||
|
||||
acc2smem<cutlass::make_index_sequence<OutputTileIterator::kIterations>>::
|
||||
push(iter, accum_fragment_iterator, this->warp_tile_iterator_);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
//
|
||||
// Load fragments from shared memory
|
||||
//
|
||||
|
||||
typename SharedLoadIterator::Fragment
|
||||
aligned_accum_fragment[kPartitionsK];
|
||||
shared_load_iterator_.load(aligned_accum_fragment[0]);
|
||||
|
||||
if (kPartitionsK > 1) {
|
||||
cutlass::plus<typename SharedLoadIterator::Fragment> add_fragments;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 1; i < kPartitionsK; ++i) {
|
||||
shared_load_iterator_.add_pointer_offset(kSmemPointerOffset);
|
||||
shared_load_iterator_.load(aligned_accum_fragment[i]);
|
||||
aligned_accum_fragment[0] = add_fragments(aligned_accum_fragment[0],
|
||||
aligned_accum_fragment[i]);
|
||||
}
|
||||
|
||||
shared_load_iterator_.add_pointer_offset((1 - kPartitionsK) *
|
||||
kSmemPointerOffset);
|
||||
}
|
||||
|
||||
//
|
||||
// Compute the output result
|
||||
//
|
||||
|
||||
typename OutputTileIterator::Fragment output_fragment;
|
||||
source.apply_output_operator(destination_iterator,
|
||||
output_fragment,
|
||||
output_op,
|
||||
aligned_accum_fragment[0]);
|
||||
|
||||
//
|
||||
// Store the final result
|
||||
//
|
||||
|
||||
destination_iterator.store(output_fragment);
|
||||
++destination_iterator;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace threadblock
|
||||
} // namespace epilogue
|
||||
} // namespace cutlass_patch
|
||||
+416
@@ -0,0 +1,416 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
/*! \file
|
||||
\brief
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include "cutlass/arch/arch.h"
|
||||
#include "cutlass/arch/mma.h"
|
||||
#include "cutlass/device_kernel.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
#include "cutlass/gemm/kernel/gemm_universal.h"
|
||||
#include "cutlass/gemm/threadblock/threadblock_swizzle.h"
|
||||
|
||||
#include "cutlass/gemm/device/default_gemm_configuration.h"
|
||||
#include "cutlass/gemm/device/gemm_universal_base.h"
|
||||
#include "cutlass/gemm/kernel/default_gemm_universal.h"
|
||||
|
||||
#include "cutlass/layout/permute.h"
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/arch/arch.h"
|
||||
#include "hytlass/arch/mma.h"
|
||||
#include "hytlass/device_kernel.h"
|
||||
#include "hytlass/numeric_types.h"
|
||||
|
||||
#include "hytlass/gemm/gemm.h"
|
||||
#include "hytlass/gemm/kernel/gemm_universal.h"
|
||||
#include "hytlass/gemm/threadblock/threadblock_swizzle.h"
|
||||
|
||||
#include "hytlass/gemm/device/default_gemm_configuration.h"
|
||||
#include "hytlass/gemm/device/gemm_universal_base.h"
|
||||
#include "hytlass/gemm/kernel/default_gemm_universal.h"
|
||||
|
||||
#include "hytlass/layout/permute.h"
|
||||
#endif
|
||||
|
||||
#include "cutlass_patch/gemm/kernel/default_gemm_with_variadic.h"
|
||||
|
||||
namespace cutlass_patch {
|
||||
namespace gemm {
|
||||
namespace device {
|
||||
|
||||
/*!
|
||||
GemmUniversal with variadic epilogues.
|
||||
*/
|
||||
template <
|
||||
/// Element type for A matrix operand
|
||||
typename ElementA_,
|
||||
/// Layout type for A matrix operand
|
||||
typename LayoutA_,
|
||||
/// Element type for B matrix operand
|
||||
typename ElementB_,
|
||||
/// Layout type for B matrix operand
|
||||
typename LayoutB_,
|
||||
/// Element type for C and D matrix operands
|
||||
typename ElementC_,
|
||||
/// Layout type for C and D matrix operands
|
||||
typename LayoutC_,
|
||||
/// Element type for internal accumulation
|
||||
typename ElementAccumulator_ = ElementC_,
|
||||
/// Operator class tag
|
||||
typename OperatorClass_ = cutlass::arch::OpClassSimt,
|
||||
/// Tag indicating architecture to tune for. This is the minimum SM that
|
||||
/// supports the intended feature. The device kernel can be built
|
||||
/// targeting any SM larger than this number.
|
||||
typename ArchTag_ = cutlass::arch::Sm70,
|
||||
/// Threadblock-level tile size (concept: GemmShape)
|
||||
typename ThreadblockShape_ = typename cutlass::gemm::device::
|
||||
DefaultGemmConfiguration<OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::ThreadblockShape,
|
||||
/// Warp-level tile size (concept: GemmShape)
|
||||
typename WarpShape_ = typename cutlass::gemm::device::
|
||||
DefaultGemmConfiguration<OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::WarpShape,
|
||||
/// Instruction-level tile size (concept: GemmShape)
|
||||
typename InstructionShape_ = typename cutlass::gemm::device::
|
||||
DefaultGemmConfiguration<OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::InstructionShape,
|
||||
/// Epilogue output operator
|
||||
typename EpilogueOutputOp_ = typename cutlass::gemm::device::
|
||||
DefaultGemmConfiguration<OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::EpilogueOutputOp,
|
||||
/// Threadblock-level swizzling operator
|
||||
typename ThreadblockSwizzle_ =
|
||||
cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<>,
|
||||
/// Number of stages used in the pipelined mainloop
|
||||
int Stages = cutlass::gemm::device::DefaultGemmConfiguration<
|
||||
OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::kStages,
|
||||
/// Access granularity of A matrix in units of elements
|
||||
int AlignmentA = cutlass::gemm::device::DefaultGemmConfiguration<
|
||||
OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::kAlignmentA,
|
||||
/// Access granularity of B matrix in units of elements
|
||||
int AlignmentB = cutlass::gemm::device::DefaultGemmConfiguration<
|
||||
OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::kAlignmentB,
|
||||
/// Operation performed by GEMM
|
||||
typename Operator_ = typename cutlass::gemm::device::
|
||||
DefaultGemmConfiguration<OperatorClass_,
|
||||
ArchTag_,
|
||||
ElementA_,
|
||||
ElementB_,
|
||||
ElementC_,
|
||||
ElementAccumulator_>::Operator,
|
||||
/// Complex elementwise transformation on A operand
|
||||
cutlass::ComplexTransform TransformA = cutlass::ComplexTransform::kNone,
|
||||
/// Complex elementwise transformation on B operand
|
||||
cutlass::ComplexTransform TransformB = cutlass::ComplexTransform::kNone>
|
||||
class GemmUniversalWithVariadic
|
||||
: public cutlass::gemm::device::GemmUniversalBase<
|
||||
typename cutlass_patch::gemm::kernel::DefaultGemmWithVariadic<
|
||||
ElementA_,
|
||||
LayoutA_,
|
||||
TransformA,
|
||||
AlignmentA,
|
||||
ElementB_,
|
||||
LayoutB_,
|
||||
TransformB,
|
||||
AlignmentB,
|
||||
ElementC_,
|
||||
LayoutC_,
|
||||
ElementAccumulator_,
|
||||
OperatorClass_,
|
||||
ArchTag_,
|
||||
ThreadblockShape_,
|
||||
WarpShape_,
|
||||
InstructionShape_,
|
||||
EpilogueOutputOp_,
|
||||
ThreadblockSwizzle_,
|
||||
Stages,
|
||||
Operator_>::GemmKernel> {
|
||||
public:
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
using OperatorClass = OperatorClass_;
|
||||
using ArchTag = ArchTag_;
|
||||
using ThreadblockShape = ThreadblockShape_;
|
||||
using WarpShape = WarpShape_;
|
||||
using InstructionShape = InstructionShape_;
|
||||
using EpilogueOutputOp = EpilogueOutputOp_;
|
||||
using ThreadblockSwizzle = ThreadblockSwizzle_;
|
||||
using Operator = Operator_;
|
||||
static int const kStages = Stages;
|
||||
static int const kAlignmentA = AlignmentA;
|
||||
static int const kAlignmentB = AlignmentB;
|
||||
static int const kAlignmentC = EpilogueOutputOp::kCount;
|
||||
static cutlass::ComplexTransform const kTransformA = TransformA;
|
||||
static cutlass::ComplexTransform const kTransformB = TransformB;
|
||||
|
||||
using Base = cutlass::gemm::device::GemmUniversalBase<
|
||||
typename cutlass_patch::gemm::kernel::DefaultGemmWithVariadic<
|
||||
ElementA_,
|
||||
LayoutA_,
|
||||
TransformA,
|
||||
AlignmentA,
|
||||
ElementB_,
|
||||
LayoutB_,
|
||||
TransformB,
|
||||
AlignmentB,
|
||||
ElementC_,
|
||||
LayoutC_,
|
||||
ElementAccumulator_,
|
||||
OperatorClass_,
|
||||
ArchTag_,
|
||||
ThreadblockShape_,
|
||||
WarpShape_,
|
||||
InstructionShape_,
|
||||
EpilogueOutputOp_,
|
||||
ThreadblockSwizzle_,
|
||||
Stages,
|
||||
Operator_>::GemmKernel>;
|
||||
|
||||
using Arguments = typename Base::Arguments;
|
||||
using GemmKernel = typename Base::GemmKernel;
|
||||
};
|
||||
|
||||
/// Partial specialization for column-major output exchanges problem size and
|
||||
/// operand.
|
||||
template <
|
||||
/// Element type for A matrix operand
|
||||
typename ElementA_,
|
||||
/// Layout type for A matrix operand
|
||||
typename LayoutA_,
|
||||
/// Element type for B matrix operand
|
||||
typename ElementB_,
|
||||
/// Layout type for B matrix operand
|
||||
typename LayoutB_,
|
||||
/// Element type for C and D matrix operands
|
||||
typename ElementC_,
|
||||
/// Element type for internal accumulation
|
||||
typename ElementAccumulator_,
|
||||
/// Operator class tag
|
||||
typename OperatorClass_,
|
||||
/// Tag indicating architecture to tune for. This is the minimum SM that
|
||||
/// supports the intended feature. The device kernel can be built
|
||||
/// targeting any SM larger than this number.
|
||||
typename ArchTag_,
|
||||
/// Threadblock-level tile size (concept: GemmShape)
|
||||
typename ThreadblockShape_,
|
||||
/// Warp-level tile size (concept: GemmShape)
|
||||
typename WarpShape_,
|
||||
/// Instruction-level tile size (concept: GemmShape)
|
||||
typename InstructionShape_,
|
||||
/// Epilogue output operator
|
||||
typename EpilogueOutputOp_,
|
||||
/// Threadblock-level swizzling operator
|
||||
typename ThreadblockSwizzle_,
|
||||
/// Number of stages used in the pipelined mainloop
|
||||
int Stages,
|
||||
/// Access granularity of A matrix in units of elements
|
||||
int AlignmentA,
|
||||
/// Access granularity of B matrix in units of elements
|
||||
int AlignmentB,
|
||||
/// Operation performed by GEMM
|
||||
typename Operator_,
|
||||
/// Complex elementwise transformation on A operand
|
||||
cutlass::ComplexTransform TransformA,
|
||||
/// Complex elementwise transformation on B operand
|
||||
cutlass::ComplexTransform TransformB>
|
||||
class GemmUniversalWithVariadic<ElementA_,
|
||||
LayoutA_,
|
||||
ElementB_,
|
||||
LayoutB_,
|
||||
ElementC_,
|
||||
cutlass::layout::ColumnMajor, // partially
|
||||
// specialized on
|
||||
// LayoutC
|
||||
ElementAccumulator_,
|
||||
OperatorClass_,
|
||||
ArchTag_,
|
||||
ThreadblockShape_,
|
||||
WarpShape_,
|
||||
InstructionShape_,
|
||||
EpilogueOutputOp_,
|
||||
ThreadblockSwizzle_,
|
||||
Stages,
|
||||
AlignmentA,
|
||||
AlignmentB,
|
||||
Operator_,
|
||||
TransformA,
|
||||
TransformB> {
|
||||
public:
|
||||
using ElementA = ElementA_;
|
||||
using LayoutA = LayoutA_;
|
||||
using TensorRefA = cutlass::TensorRef<ElementA const, LayoutA>;
|
||||
using ElementB = ElementB_;
|
||||
using LayoutB = LayoutB_;
|
||||
using TensorRefB = cutlass::TensorRef<ElementB const, LayoutB>;
|
||||
using ElementC = ElementC_;
|
||||
using LayoutC = cutlass::layout::ColumnMajor;
|
||||
using TensorRefC = cutlass::TensorRef<ElementC const, LayoutC>;
|
||||
using TensorRefD = cutlass::TensorRef<ElementC, LayoutC>;
|
||||
using ElementAccumulator = ElementAccumulator_;
|
||||
using OperatorClass = OperatorClass_;
|
||||
using ArchTag = ArchTag_;
|
||||
using ThreadblockShape = ThreadblockShape_;
|
||||
using WarpShape = WarpShape_;
|
||||
using InstructionShape = InstructionShape_;
|
||||
using EpilogueOutputOp = EpilogueOutputOp_;
|
||||
using ThreadblockSwizzle = ThreadblockSwizzle_;
|
||||
using Operator = Operator_;
|
||||
static int const kStages = Stages;
|
||||
static int const kAlignmentA = AlignmentA;
|
||||
static int const kAlignmentB = AlignmentB;
|
||||
static cutlass::ComplexTransform const kTransformA = TransformA;
|
||||
static cutlass::ComplexTransform const kTransformB = TransformB;
|
||||
|
||||
using UnderlyingOperator = typename GemmUniversalWithVariadic<
|
||||
ElementB,
|
||||
typename cutlass::layout::LayoutTranspose<LayoutB>::type,
|
||||
ElementA,
|
||||
typename cutlass::layout::LayoutTranspose<LayoutA>::type,
|
||||
ElementC,
|
||||
cutlass::layout::RowMajor,
|
||||
ElementAccumulator,
|
||||
OperatorClass,
|
||||
ArchTag,
|
||||
ThreadblockShape,
|
||||
WarpShape,
|
||||
InstructionShape,
|
||||
EpilogueOutputOp,
|
||||
ThreadblockSwizzle,
|
||||
Stages,
|
||||
kAlignmentB,
|
||||
kAlignmentA,
|
||||
Operator,
|
||||
kTransformB,
|
||||
kTransformA>::Base;
|
||||
|
||||
using GemmKernel = typename UnderlyingOperator::GemmKernel;
|
||||
static int const kAlignmentC = EpilogueOutputOp::kCount;
|
||||
|
||||
/// Argument structure
|
||||
using Arguments = typename UnderlyingOperator::Arguments;
|
||||
|
||||
private:
|
||||
UnderlyingOperator underlying_operator_;
|
||||
|
||||
public:
|
||||
/// Constructs the GEMM.
|
||||
GemmUniversalWithVariadic() {}
|
||||
|
||||
/// Helper to construct a transposed equivalent for the underlying GEMM
|
||||
/// operator
|
||||
static Arguments to_underlying_arguments(Arguments const &args) {
|
||||
return args.transposed_problem();
|
||||
}
|
||||
|
||||
/// Determines whether the GEMM can execute the given problem.
|
||||
static cutlass::Status can_implement(Arguments const &args) {
|
||||
return UnderlyingOperator::can_implement(to_underlying_arguments(args));
|
||||
}
|
||||
|
||||
/// Gets the workspace size
|
||||
static size_t get_workspace_size(Arguments const &args) {
|
||||
return UnderlyingOperator::get_workspace_size(
|
||||
to_underlying_arguments(args));
|
||||
}
|
||||
|
||||
/// Computes the grid shape
|
||||
static dim3 get_grid_shape(Arguments const &args) {
|
||||
return UnderlyingOperator::get_grid_shape(to_underlying_arguments(args));
|
||||
}
|
||||
|
||||
/// Computes the maximum number of active blocks per multiprocessor
|
||||
static int maximum_active_blocks(int smem_capacity = -1) {
|
||||
return UnderlyingOperator::maximum_active_blocks(smem_capacity);
|
||||
}
|
||||
|
||||
/// Initializes GEMM state from arguments.
|
||||
cutlass::Status initialize(Arguments const &args,
|
||||
void *workspace = nullptr,
|
||||
GPUStream_t stream = nullptr) {
|
||||
return underlying_operator_.initialize(
|
||||
to_underlying_arguments(args), workspace, stream);
|
||||
}
|
||||
|
||||
/// Lightweight update given a subset of arguments
|
||||
cutlass::Status update(Arguments const &args, void *workspace = nullptr) {
|
||||
return underlying_operator_.update(to_underlying_arguments(args),
|
||||
workspace);
|
||||
}
|
||||
|
||||
/// Runs the kernel using initialized state.
|
||||
cutlass::Status run(GPUStream_t stream = nullptr) {
|
||||
return underlying_operator_.run(stream);
|
||||
}
|
||||
|
||||
/// Runs the kernel using initialized state.
|
||||
cutlass::Status operator()(GPUStream_t stream = nullptr) {
|
||||
return run(stream);
|
||||
}
|
||||
|
||||
/// Runs the kernel using initialized state.
|
||||
cutlass::Status operator()(Arguments const &args,
|
||||
void *workspace = nullptr,
|
||||
GPUStream_t stream = nullptr) {
|
||||
cutlass::Status status = initialize(args, workspace, stream);
|
||||
|
||||
if (status == cutlass::Status::kSuccess) {
|
||||
status = run(stream);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace device
|
||||
} // namespace gemm
|
||||
} // namespace cutlass_patch
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
/*! \file
|
||||
\brief
|
||||
Defines a GEMM with Reduction based on an existing UniversalGemm kernel.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass_patch/backend.h"
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include "cutlass/gemm/kernel/default_gemm_universal.h"
|
||||
#include "cutlass/gemm/kernel/gemm_universal.h"
|
||||
#elif defined(__HIPCC__)
|
||||
#include "hytlass/gemm/kernel/default_gemm_universal.h"
|
||||
#include "hytlass/gemm/kernel/gemm_universal.h"
|
||||
#endif
|
||||
|
||||
#include "cutlass_patch/epilogue/threadblock/default_epilogue_with_variadic.h"
|
||||
#include "cutlass_patch/epilogue/threadblock/epilogue_with_variadic.h"
|
||||
|
||||
namespace cutlass_patch {
|
||||
namespace gemm {
|
||||
namespace kernel {
|
||||
|
||||
template <
|
||||
/// Element type for A matrix operand
|
||||
typename ElementA_,
|
||||
/// Layout type for A matrix operand
|
||||
typename LayoutA_,
|
||||
/// Complex elementwise transformation on A operand
|
||||
cutlass::ComplexTransform TransformA,
|
||||
/// Access granularity of A matrix in units of elements
|
||||
int kAlignmentA,
|
||||
/// Element type for B matrix operand
|
||||
typename ElementB_,
|
||||
/// Layout type for B matrix operand
|
||||
typename LayoutB_,
|
||||
/// Complex elementwise transformation on B operand
|
||||
cutlass::ComplexTransform TransformB,
|
||||
/// Access granularity of B matrix in units of elements
|
||||
int kAlignmentB,
|
||||
/// Element type for C and D matrix operands
|
||||
typename ElementC_,
|
||||
/// Layout type for C and D matrix operands
|
||||
typename LayoutC_,
|
||||
/// Element type for internal accumulation
|
||||
typename ElementAccumulator,
|
||||
/// Operator class tag
|
||||
typename OperatorClass,
|
||||
/// Tag indicating architecture to tune for
|
||||
typename ArchTag,
|
||||
/// Threadblock-level tile size (concept: GemmShape)
|
||||
typename ThreadblockShape,
|
||||
/// Warp-level tile size (concept: GemmShape)
|
||||
typename WarpShape,
|
||||
/// Warp-level tile size (concept: GemmShape)
|
||||
typename InstructionShape,
|
||||
/// Epilogue output operator - must satisfy concept of
|
||||
/// 'EpilogueWithVariadicOp'
|
||||
typename EpilogueOutputOp,
|
||||
/// Threadblock-level swizzling operator
|
||||
typename ThreadblockSwizzle,
|
||||
/// Number of stages used in the pipelined mainloop
|
||||
int Stages,
|
||||
/// Operation performed by GEMM
|
||||
typename Operator,
|
||||
///
|
||||
typename Enable = void>
|
||||
struct DefaultGemmWithVariadic {
|
||||
using GemmBase = typename cutlass::gemm::kernel::DefaultGemmUniversal<
|
||||
ElementA_,
|
||||
LayoutA_,
|
||||
TransformA,
|
||||
kAlignmentA,
|
||||
ElementB_,
|
||||
LayoutB_,
|
||||
TransformB,
|
||||
kAlignmentB,
|
||||
ElementC_,
|
||||
LayoutC_,
|
||||
ElementAccumulator,
|
||||
OperatorClass,
|
||||
ArchTag,
|
||||
ThreadblockShape,
|
||||
WarpShape,
|
||||
InstructionShape,
|
||||
EpilogueOutputOp,
|
||||
ThreadblockSwizzle,
|
||||
Stages,
|
||||
Operator>::GemmKernel;
|
||||
|
||||
// Define epilogue
|
||||
using Epilogue = typename cutlass_patch::epilogue::threadblock::
|
||||
DefaultEpilogueWithVariadicTensorOp<
|
||||
typename GemmBase::Epilogue::Shape,
|
||||
typename GemmBase::Epilogue::WarpMmaOperator,
|
||||
GemmBase::Epilogue::kPartitionsK,
|
||||
ElementC_,
|
||||
EpilogueOutputOp,
|
||||
GemmBase::Epilogue::kElementsPerAccess>::Epilogue;
|
||||
|
||||
// Compose the GEMM kernel
|
||||
using GemmKernel = cutlass::gemm::kernel::
|
||||
GemmUniversal<typename GemmBase::Mma, Epilogue, ThreadblockSwizzle>;
|
||||
};
|
||||
|
||||
/// Partial specialization: ArchTag = cutlass::arch::Sm70
|
||||
///
|
||||
///
|
||||
template <
|
||||
/// Element type for A matrix operand
|
||||
typename ElementA_,
|
||||
/// Layout type for A matrix operand
|
||||
typename LayoutA_,
|
||||
/// Complex elementwise transformation on A operand
|
||||
cutlass::ComplexTransform TransformA,
|
||||
/// Access granularity of A matrix in units of elements
|
||||
int kAlignmentA,
|
||||
/// Element type for B matrix operand
|
||||
typename ElementB_,
|
||||
/// Layout type for B matrix operand
|
||||
typename LayoutB_,
|
||||
/// Complex elementwise transformation on B operand
|
||||
cutlass::ComplexTransform TransformB,
|
||||
/// Access granularity of B matrix in units of elements
|
||||
int kAlignmentB,
|
||||
/// Element type for C and D matrix operands
|
||||
typename ElementC_,
|
||||
/// Layout type for C and D matrix operands
|
||||
typename LayoutC_,
|
||||
/// Element type for internal accumulation
|
||||
typename ElementAccumulator,
|
||||
/// Operator class tag
|
||||
typename OperatorClass,
|
||||
/// Threadblock-level tile size (concept: GemmShape)
|
||||
typename ThreadblockShape,
|
||||
/// Warp-level tile size (concept: GemmShape)
|
||||
typename WarpShape,
|
||||
/// Warp-level tile size (concept: GemmShape)
|
||||
typename InstructionShape,
|
||||
/// Epilogue output operator - must satisfy concept of
|
||||
/// 'EpilogueWithVariadicOp'
|
||||
typename EpilogueOutputOp,
|
||||
/// Threadblock-level swizzling operator
|
||||
typename ThreadblockSwizzle,
|
||||
/// Number of stages used in the pipelined mainloop
|
||||
int Stages,
|
||||
/// Operation performed by GEMM
|
||||
typename Operator,
|
||||
///
|
||||
typename Enable>
|
||||
struct DefaultGemmWithVariadic<ElementA_,
|
||||
LayoutA_,
|
||||
TransformA,
|
||||
kAlignmentA,
|
||||
ElementB_,
|
||||
LayoutB_,
|
||||
TransformB,
|
||||
kAlignmentB,
|
||||
ElementC_,
|
||||
LayoutC_,
|
||||
ElementAccumulator,
|
||||
OperatorClass,
|
||||
cutlass::arch::Sm70,
|
||||
ThreadblockShape,
|
||||
WarpShape,
|
||||
InstructionShape,
|
||||
EpilogueOutputOp,
|
||||
ThreadblockSwizzle,
|
||||
Stages,
|
||||
Operator,
|
||||
Enable> {
|
||||
using GemmBase = typename cutlass::gemm::kernel::DefaultGemmUniversal<
|
||||
ElementA_,
|
||||
LayoutA_,
|
||||
TransformA,
|
||||
kAlignmentA,
|
||||
ElementB_,
|
||||
LayoutB_,
|
||||
TransformB,
|
||||
kAlignmentB,
|
||||
ElementC_,
|
||||
LayoutC_,
|
||||
ElementAccumulator,
|
||||
OperatorClass,
|
||||
cutlass::arch::Sm70,
|
||||
ThreadblockShape,
|
||||
WarpShape,
|
||||
InstructionShape,
|
||||
EpilogueOutputOp,
|
||||
ThreadblockSwizzle,
|
||||
Stages,
|
||||
Operator>::GemmKernel;
|
||||
|
||||
// Define epilogue
|
||||
using Epilogue = typename cutlass_patch::epilogue::threadblock::
|
||||
DefaultEpilogueWithVariadicVoltaTensorOp<
|
||||
typename GemmBase::Epilogue::Shape,
|
||||
typename GemmBase::Epilogue::WarpMmaOperator,
|
||||
GemmBase::Epilogue::kPartitionsK,
|
||||
ElementC_,
|
||||
EpilogueOutputOp,
|
||||
GemmBase::Epilogue::kElementsPerAccess>::Epilogue;
|
||||
|
||||
// Compose the GEMM kernel
|
||||
using GemmKernel = cutlass::gemm::kernel::
|
||||
GemmUniversal<typename GemmBase::Mma, Epilogue, ThreadblockSwizzle>;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace gemm
|
||||
} // namespace cutlass_patch
|
||||
@@ -0,0 +1,552 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hytlass/gemm_coord.h"
|
||||
|
||||
namespace ap {
|
||||
|
||||
constexpr int kNumConfigsHalf = 28;
|
||||
constexpr int kNumConfigsFloat = 13;
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct SwizzleWrapper {
|
||||
using Type =
|
||||
hytlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<SwizzleFactor>;
|
||||
};
|
||||
|
||||
#define AP_AUTOTUNE(func, stream_ptr, count, ...) \
|
||||
{ \
|
||||
using FuncType = decltype(func<0>); \
|
||||
static int selected_config_id = -1; \
|
||||
static std::vector<std::function<FuncType>> matmul_functions = \
|
||||
[]<std::size_t... Is>(std::index_sequence<Is...>) { \
|
||||
return std::vector<std::function<FuncType>>{func<Is>...}; \
|
||||
} \
|
||||
(std::make_index_sequence<count>()); \
|
||||
if (selected_config_id == -1) { \
|
||||
selected_config_id = \
|
||||
ap::ProfileBestConfig(matmul_functions, stream_ptr, ##__VA_ARGS__); \
|
||||
} \
|
||||
matmul_functions[selected_config_id](__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define AP_AUTOTUNE_half(func, stream_ptr, ...) \
|
||||
AP_AUTOTUNE(func, stream_ptr, ap::kNumConfigsHalf, __VA_ARGS__)
|
||||
#define AP_AUTOTUNE_float(func, stream_ptr, ...) \
|
||||
AP_AUTOTUNE(func, stream_ptr, ap::kNumConfigsFloat, __VA_ARGS__)
|
||||
#define AP_AUTOTUNE_bfloat16(func, stream_ptr, ...) \
|
||||
AP_AUTOTUNE_half(func, stream_ptr, __VA_ARGS__)
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched, int Id = 0>
|
||||
struct GemmTuningConfigs {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 2;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = Id;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 1> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 1;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 2> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 2;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 3> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 64, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 3;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 4> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 4;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 5> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 5;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 6> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 6;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 7> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 64, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 7;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 8> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 8;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 9> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 9;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 10> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 32, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 10;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 11> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 11;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 12> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 12;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 13> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 64, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 13;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 14> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 14;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 15> {
|
||||
using TShape = hytlass::gemm::GemmShape<32, 64, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<16, 32, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 15;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 16> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 16;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 17> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 17;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 18> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 5;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 18;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 19> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 6;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 19;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 20> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 6;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 20;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 21> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 10;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 21;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 22> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 256, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 2;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 22;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 23> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 256, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 23;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 24> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 24;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 25> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 25;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 26> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 128, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 26;
|
||||
};
|
||||
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, 27> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 64, 64>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 64>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 16>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 27;
|
||||
};
|
||||
|
||||
// Specialization for float
|
||||
template <int SwizzleFactor, bool Batched, int Id>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, Id> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 64, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = Id;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 1> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 32, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 1;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 2> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 2;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 3> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 256, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 3;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 4> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 256, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 4;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 5> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 5;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 6> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 6;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 7> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 7;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 8> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 64, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 8;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 9> {
|
||||
using TShape = hytlass::gemm::GemmShape<256, 64, 32>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 32>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 3;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 9;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 10> {
|
||||
using TShape = hytlass::gemm::GemmShape<64, 128, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 10;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 11> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 64, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<64, 32, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 11;
|
||||
};
|
||||
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, 12> {
|
||||
using TShape = hytlass::gemm::GemmShape<128, 128, 16>;
|
||||
using WShape = hytlass::gemm::GemmShape<32, 64, 16>;
|
||||
using IShape = hytlass::gemm::GemmShape<16, 16, 8>;
|
||||
static constexpr int kNumStages = 4;
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = 12;
|
||||
};
|
||||
|
||||
struct DefaultConfig {
|
||||
static constexpr int kConfigId = 0;
|
||||
static constexpr int kSwizzleFactor = 1;
|
||||
static constexpr bool kBatched = false;
|
||||
};
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,254 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hytlass/epilogue/thread/linear_combination_bias_elementwise.h"
|
||||
#include "hytlass/gemm/device/gemm_universal.h"
|
||||
#include "hytlass/gemm/device/gemm_universal_with_broadcast.h"
|
||||
#include "hytlass/util/device_memory.h"
|
||||
|
||||
#include "cutlass_patch/batched_matrix_coord.h"
|
||||
#include "cutlass_patch/epilogue/thread/linear_combination_unary.h"
|
||||
#include "cutlass_patch/epilogue/thread/linear_combination_variadic.h"
|
||||
#include "cutlass_patch/gemm/device/gemm_universal_with_variadic.h"
|
||||
#include "cutlass_patch/hip/all_tuning_configs.h"
|
||||
|
||||
#include "params.h" // NOLINT
|
||||
|
||||
#define CHECK_HYTLASS(status) \
|
||||
{ \
|
||||
auto error = status; \
|
||||
if (error != hytlass::Status::kSuccess) { \
|
||||
std::cerr << "HYTLASS error = " << int(error) << " (" \
|
||||
<< hytlassGetStatusString(error) << ")" \
|
||||
<< " at line " << __LINE__ << std::endl; \
|
||||
std::abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace ap {
|
||||
|
||||
using MatrixCoord = cutlass_patch::BatchedMatrixCoord;
|
||||
using bfloat16 = __hip_bfloat16;
|
||||
|
||||
// Operation performed by GEMM
|
||||
template <typename ElementT>
|
||||
struct GemmOperation {
|
||||
using Type = hytlass::arch::OpMultiplyAdd;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct GemmOperation<float> {
|
||||
using Type = hytlass::arch::OpMultiplyAddFastF32;
|
||||
};
|
||||
|
||||
static hytlass::gemm::GemmUniversalMode GetGemmMode(int batch_count) {
|
||||
return batch_count > 1 ? hytlass::gemm::GemmUniversalMode::kBatched
|
||||
: hytlass::gemm::GemmUniversalMode::kGemm;
|
||||
}
|
||||
|
||||
static void *GetWorkspace(size_t workspace_size) {
|
||||
static hytlass::device_memory::allocation<uint8_t> workspace;
|
||||
if (workspace.size() < workspace_size) {
|
||||
workspace.reset(workspace_size);
|
||||
}
|
||||
return workspace.get();
|
||||
}
|
||||
|
||||
template <typename GemmFunc>
|
||||
hytlass::Status SetMaxDynamicSharedMemorySize() {
|
||||
hipError_t hiprt_result;
|
||||
|
||||
// If requires more than 48KB: configure for extended, dynamic shared memory
|
||||
if constexpr (GemmFunc::kSharedStorageSize >= (48 << 10)) {
|
||||
hiprt_result = hipFuncSetAttribute(
|
||||
(const void *)hytlass::Kernel2<typename GemmFunc::GemmKernel>,
|
||||
hipFuncAttributeMaxDynamicSharedMemorySize,
|
||||
GemmFunc::kSharedStorageSize);
|
||||
if (hiprt_result != hipSuccess) {
|
||||
HYTLASS_TRACE_HOST("hipFuncSetAttribute() returned error "
|
||||
<< hipGetErrorString(hiprt_result));
|
||||
return hytlass::Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
|
||||
#if AP_ENABLE_DEBUG
|
||||
// Update SM occupancy member
|
||||
int sm_occupancy = -1;
|
||||
hiprt_result = hipOccupancyMaxActiveBlocksPerMultiprocessorWithFlags(
|
||||
&sm_occupancy,
|
||||
hytlass::Kernel2<typename GemmFunc::GemmKernel>,
|
||||
GemmFunc::GemmKernel::kThreadCount,
|
||||
GemmFunc::kSharedStorageSize,
|
||||
hipOccupancyDisableCachingOverride);
|
||||
if (hiprt_result != hipSuccess) {
|
||||
HYTLASS_TRACE_HOST(
|
||||
"hipOccupancyMaxActiveBlocksPerMultiprocessorWithFlags() returned "
|
||||
"error "
|
||||
<< hipGetErrorString(hiprt_result));
|
||||
return hytlass::Status::kErrorInternal;
|
||||
}
|
||||
HYTLASS_TRACE_HOST("sm_occupancy: (" << sm_occupancy
|
||||
<< ") "
|
||||
"smem_size: ("
|
||||
<< GemmFunc::kSharedStorageSize
|
||||
<< ") "
|
||||
"GemmKernel::kThreadCount: ("
|
||||
<< GemmFunc::GemmKernel::kThreadCount
|
||||
<< ")");
|
||||
#endif
|
||||
return hytlass::Status::kSuccess;
|
||||
}
|
||||
|
||||
// Convert HIP data type to hytlass data type
|
||||
template <typename T>
|
||||
struct HytlassDataType {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HytlassDataType<half> {
|
||||
using Type = hytlass::half_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HytlassDataType<__hip_bfloat16> {
|
||||
using Type = hytlass::bfloat16_t;
|
||||
};
|
||||
|
||||
// Convert to hytlass layout
|
||||
template <bool Transposed>
|
||||
struct MatrixLayout {
|
||||
using Type = hytlass::layout::RowMajor;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MatrixLayout<true> {
|
||||
using Type = hytlass::layout::ColumnMajor;
|
||||
};
|
||||
|
||||
template <typename ElementT,
|
||||
typename ElementComputeT,
|
||||
template <typename T>
|
||||
class VariadicFunctor,
|
||||
int AlignA = 128 / hytlass::sizeof_bits<ElementT>::value,
|
||||
int AlignB = 128 / hytlass::sizeof_bits<ElementT>::value,
|
||||
int ConfigId = DefaultConfig::kConfigId,
|
||||
int SwizzleFactor = DefaultConfig::kSwizzleFactor,
|
||||
bool Batched = DefaultConfig::kBatched>
|
||||
void MatmulAddVariadic(
|
||||
const GemmEpilogueParams ¶ms,
|
||||
const typename VariadicFunctor<ElementComputeT>::Arguments &variadic_args) {
|
||||
// <- data type of accumulator
|
||||
using ElementAccumulator = typename HytlassDataType<ElementComputeT>::Type;
|
||||
// <- data type of epilogue operations
|
||||
using ElementComputeEpilogue = ElementAccumulator;
|
||||
// <- data type of elements in input matrix A
|
||||
using ElementInputA = typename HytlassDataType<ElementT>::Type;
|
||||
// <- data type of elements in input matrix B
|
||||
using ElementInputB = typename HytlassDataType<ElementT>::Type;
|
||||
// <- data type of elements in output matrix D
|
||||
using ElementOutput = typename HytlassDataType<ElementT>::Type;
|
||||
|
||||
constexpr int AlignC = AlignB;
|
||||
|
||||
// Epilogue operation as LinearCombination:
|
||||
// alpha * accumulator + beta * source
|
||||
using EpilogueOutputOp =
|
||||
cutlass_patch::epilogue::thread::LinearCombinationVariadic<
|
||||
VariadicFunctor,
|
||||
ElementOutput,
|
||||
AlignC,
|
||||
ElementAccumulator,
|
||||
ElementComputeEpilogue,
|
||||
hytlass::epilogue::thread::ScaleType::NoBetaScaling>;
|
||||
|
||||
using GemmFunc = cutlass_patch::gemm::device::GemmUniversalWithVariadic<
|
||||
ElementInputA,
|
||||
hytlass::layout::RowMajor,
|
||||
ElementInputB,
|
||||
hytlass::layout::RowMajor,
|
||||
ElementOutput,
|
||||
hytlass::layout::RowMajor,
|
||||
ElementAccumulator,
|
||||
hytlass::arch::OpClassTensorOp,
|
||||
hytlass::arch::Gfx928,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
TShape,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
WShape,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
IShape,
|
||||
EpilogueOutputOp,
|
||||
typename GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::
|
||||
SwizzleThreadBlock,
|
||||
GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ConfigId>::kNumStages,
|
||||
AlignA,
|
||||
AlignB,
|
||||
typename GemmOperation<ElementT>::Type>;
|
||||
|
||||
CHECK_HYTLASS(SetMaxDynamicSharedMemorySize<GemmFunc>());
|
||||
|
||||
/// Arguments
|
||||
hytlass::gemm::GemmCoord problem_size{params.m, params.n, params.k};
|
||||
|
||||
const ElementInputA *input =
|
||||
reinterpret_cast<const ElementInputA *>(params.input);
|
||||
const ElementInputB *weight =
|
||||
reinterpret_cast<const ElementInputB *>(params.weight);
|
||||
const ElementOutput *bias =
|
||||
reinterpret_cast<const ElementOutput *>(params.bias);
|
||||
ElementOutput *output = reinterpret_cast<ElementOutput *>(params.output);
|
||||
|
||||
ElementComputeEpilogue alpha = static_cast<ElementComputeEpilogue>(1);
|
||||
ElementComputeEpilogue beta = bias ? static_cast<ElementComputeEpilogue>(1)
|
||||
: static_cast<ElementComputeEpilogue>(0);
|
||||
|
||||
typename GemmFunc::Arguments arguments{
|
||||
GetGemmMode(params.batch_count),
|
||||
problem_size, // <- problem size of matrix multiplication
|
||||
params.batch_count, // <- batch_count or k-dimension split factor
|
||||
{alpha, beta, variadic_args}, // <- epilogue params, alpha, beta
|
||||
input, // <- input, ptr_A, A, shape={M, K}
|
||||
weight, // <- input, ptr_B, B, shape={K, N}
|
||||
bias, // <- input, ptr_C, shape={M, N} or {1, N}
|
||||
output, // <- output, ptr_D, Z, shape={M, N}
|
||||
params.shape_args.batch_stride_A,
|
||||
params.shape_args.batch_stride_B,
|
||||
params.shape_args.batch_stride_C,
|
||||
params.shape_args.batch_stride_D,
|
||||
params.shape_args.lda,
|
||||
params.shape_args.ldb,
|
||||
params.shape_args.ldc_bias,
|
||||
params.shape_args.ldd};
|
||||
|
||||
size_t workspace_size = GemmFunc::get_workspace_size(arguments);
|
||||
void *workspace = workspace_size > 0 ? GetWorkspace(workspace_size) : nullptr;
|
||||
|
||||
GemmFunc device_gemm;
|
||||
|
||||
hipStream_t *stream_ptr = reinterpret_cast<hipStream_t *>(params.stream_ptr);
|
||||
|
||||
CHECK_HYTLASS(device_gemm.can_implement(arguments));
|
||||
CHECK_HYTLASS(device_gemm.initialize(arguments, workspace, *stream_ptr));
|
||||
|
||||
// Run the GEMM
|
||||
CHECK_HYTLASS(device_gemm(*stream_ptr));
|
||||
#if AP_ENABLE_DEBUG
|
||||
CHECK_HIP(hipStreamSynchronize(*stream_ptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CUTLASS_DEBUG_TRACE_LEVEL
|
||||
|
||||
#ifndef CUTLASS_TRACE_DEVICE
|
||||
#define CUTLASS_TRACE_DEVICE(format, ...) \
|
||||
{ \
|
||||
if (blockIdx.x == 0 && blockIdx.y == 0 && blockIdx.z == 0 && \
|
||||
threadIdx.x == 0 && threadIdx.y == 0) { \
|
||||
printf("[DEVICE][%s:%d, %s]" format "\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CUTLASS_TRACE_DEVICE_TID_DETAIL
|
||||
#define CUTLASS_TRACE_DEVICE_TID_DETAIL(bidz, bidx, tidx, format, ...) \
|
||||
{ \
|
||||
if (blockIdx.x == bidx && blockIdx.y == 0 && blockIdx.z == bidz && \
|
||||
threadIdx.x == tidx && threadIdx.y == 0) { \
|
||||
printf("[DEVICE][%s:%d, %s][bid={%d,%d,%d}, tid={%d,%d,%d}]" format \
|
||||
"\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__, \
|
||||
blockIdx.x, \
|
||||
blockIdx.y, \
|
||||
blockIdx.z, \
|
||||
threadIdx.x, \
|
||||
threadIdx.y, \
|
||||
threadIdx.z, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CUTLASS_TRACE_DEVICE_TID
|
||||
#define CUTLASS_TRACE_DEVICE_TID(format, ...) \
|
||||
{ \
|
||||
CUTLASS_TRACE_DEVICE_TID_DETAIL(0, 0, 0, format, ##__VA_ARGS__) \
|
||||
CUTLASS_TRACE_DEVICE_TID_DETAIL(0, 0, 1, format, ##__VA_ARGS__) \
|
||||
CUTLASS_TRACE_DEVICE_TID_DETAIL(0, 1, 0, format, ##__VA_ARGS__) \
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef CUTLASS_TRACE_DEVICE
|
||||
#define CUTLASS_TRACE_DEVICE(format, ...)
|
||||
#endif
|
||||
|
||||
#ifndef CUTLASS_TRACE_DEVICE_TID
|
||||
#define CUTLASS_TRACE_DEVICE_TID(format, ...)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,298 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
head_template = """// auto-generated by generate_configs.py
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/gemm_coord.h"
|
||||
|
||||
namespace ap {
|
||||
|
||||
constexpr int kNumConfigsHalf = ${num_configs_fp16};
|
||||
constexpr int kNumConfigsFloat = ${num_configs_fp32};
|
||||
|
||||
template <int SwizzleFactor, bool Batched> struct SwizzleWrapper {
|
||||
using Type =
|
||||
cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<SwizzleFactor>;
|
||||
};
|
||||
|
||||
// template <int SwizzleFactor>
|
||||
// struct SwizzleWrapper<SwizzleFactor, true> {
|
||||
// using Type =
|
||||
// cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle;
|
||||
// };
|
||||
"""
|
||||
|
||||
autotune_wrapper_template = """
|
||||
#define AP_AUTOTUNE_${datatype}(func, stream, ...) { \\
|
||||
using FuncType = decltype(func<0>); \\
|
||||
static int selected_config_id = -1; \\
|
||||
static std::vector<std::function<FuncType>> \\
|
||||
matmul_functions = { \\
|
||||
${repeat_functions} \\
|
||||
}; \\
|
||||
if (selected_config_id == -1) { \\
|
||||
selected_config_id = ap::ProfileBestConfig(matmul_functions, stream, ##__VA_ARGS__); \\
|
||||
} \\
|
||||
matmul_functions[selected_config_id](__VA_ARGS__); \\
|
||||
}
|
||||
"""
|
||||
|
||||
fp16_config_template_0 = """
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched, int Id = 0>
|
||||
struct GemmTuningConfigs {
|
||||
using TShape = cutlass::gemm::GemmShape<${tshape}>;
|
||||
using WShape = cutlass::gemm::GemmShape<${wshape}>;
|
||||
using IShape = cutlass::gemm::GemmShape<${ishape}>;
|
||||
static constexpr int kNumStages = ${stages};
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = Id;
|
||||
};
|
||||
"""
|
||||
|
||||
fp16_config_template = """
|
||||
template <typename ElementT, int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<ElementT, SwizzleFactor, Batched, ${config_id}> {
|
||||
using TShape = cutlass::gemm::GemmShape<${tshape}>;
|
||||
using WShape = cutlass::gemm::GemmShape<${wshape}>;
|
||||
using IShape = cutlass::gemm::GemmShape<${ishape}>;
|
||||
static constexpr int kNumStages = ${stages};
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = ${config_id};
|
||||
};
|
||||
"""
|
||||
|
||||
fp32_config_template_0 = """
|
||||
// Specialization for float
|
||||
template <int SwizzleFactor, bool Batched, int Id>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, Id> {
|
||||
using TShape = cutlass::gemm::GemmShape<${tshape}>;
|
||||
using WShape = cutlass::gemm::GemmShape<${wshape}>;
|
||||
using IShape = cutlass::gemm::GemmShape<${ishape}>;
|
||||
static constexpr int kNumStages = ${stages};
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = Id;
|
||||
};
|
||||
"""
|
||||
|
||||
fp32_config_template = """
|
||||
template <int SwizzleFactor, bool Batched>
|
||||
struct GemmTuningConfigs<float, SwizzleFactor, Batched, ${config_id}> {
|
||||
using TShape = cutlass::gemm::GemmShape<${tshape}>;
|
||||
using WShape = cutlass::gemm::GemmShape<${wshape}>;
|
||||
using IShape = cutlass::gemm::GemmShape<${ishape}>;
|
||||
static constexpr int kNumStages = ${stages};
|
||||
|
||||
using SwizzleThreadBlock =
|
||||
typename SwizzleWrapper<SwizzleFactor, Batched>::Type;
|
||||
static constexpr int kId = ${config_id};
|
||||
};
|
||||
"""
|
||||
|
||||
tail_code_str = """
|
||||
} // namespace ap
|
||||
"""
|
||||
|
||||
|
||||
class GemmTuningConfig:
|
||||
def __init__(self, tshape, wshape, ishape, stages, level):
|
||||
self.tshape = tshape
|
||||
self.wshape = wshape
|
||||
self.ishape = ishape
|
||||
self.stages = stages
|
||||
self.level = level
|
||||
|
||||
def __eq__(self, other):
|
||||
def check_shape(s1, s2):
|
||||
assert len(s1) == len(s2)
|
||||
res = True
|
||||
for i in range(len(s1)):
|
||||
if s1[i] != s2[i]:
|
||||
res = False
|
||||
break
|
||||
return res
|
||||
|
||||
res = check_shape(self.tshape, other.tshape)
|
||||
res = res and check_shape(self.wshape, other.wshape)
|
||||
res = res and check_shape(self.ishape, other.ishape)
|
||||
res = res and self.stages == other.stages
|
||||
return res
|
||||
|
||||
|
||||
def all_configs_sm80_fp16():
|
||||
all_tuning_configs_fp16 = [
|
||||
GemmTuningConfig([16, 128, 64], [16, 32, 64], [16, 8, 16], 2, 2),
|
||||
GemmTuningConfig([32, 128, 64], [32, 32, 64], [16, 8, 16], 2, 2),
|
||||
GemmTuningConfig([64, 128, 64], [64, 64, 64], [16, 8, 16], 2, 2),
|
||||
GemmTuningConfig([128, 128, 64], [64, 64, 64], [16, 8, 16], 2, 2),
|
||||
GemmTuningConfig([128, 128, 64], [128, 32, 64], [16, 8, 16], 2, 2),
|
||||
GemmTuningConfig([128, 256, 64], [64, 64, 64], [16, 8, 16], 2, 2),
|
||||
GemmTuningConfig([256, 128, 64], [64, 64, 64], [16, 8, 16], 2, 1),
|
||||
GemmTuningConfig([16, 128, 64], [16, 32, 64], [16, 8, 16], 3, 3),
|
||||
GemmTuningConfig([32, 128, 64], [32, 32, 64], [16, 8, 16], 3, 2),
|
||||
GemmTuningConfig([64, 128, 64], [32, 64, 64], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([64, 128, 64], [64, 64, 64], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([64, 256, 64], [64, 64, 64], [16, 8, 16], 3, 3),
|
||||
GemmTuningConfig([128, 64, 64], [64, 32, 64], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([128, 128, 32], [64, 64, 32], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([128, 128, 64], [64, 64, 64], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([128, 128, 64], [128, 32, 64], [16, 8, 16], 3, 2),
|
||||
GemmTuningConfig([128, 256, 32], [64, 64, 32], [16, 8, 16], 3, 2),
|
||||
GemmTuningConfig([128, 256, 64], [64, 64, 64], [16, 8, 16], 3, 2),
|
||||
GemmTuningConfig([256, 64, 32], [64, 64, 32], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([256, 64, 64], [64, 64, 64], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([256, 128, 32], [64, 64, 32], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([256, 128, 64], [64, 64, 64], [16, 8, 16], 3, 1),
|
||||
GemmTuningConfig([16, 128, 64], [16, 32, 64], [16, 8, 16], 4, 3),
|
||||
GemmTuningConfig([32, 128, 64], [32, 32, 64], [16, 8, 16], 4, 2),
|
||||
GemmTuningConfig([64, 128, 64], [64, 64, 64], [16, 8, 16], 4, 2),
|
||||
GemmTuningConfig([64, 256, 32], [64, 64, 32], [16, 8, 16], 4, 2),
|
||||
GemmTuningConfig([64, 256, 64], [64, 64, 64], [16, 8, 16], 4, 3),
|
||||
GemmTuningConfig([128, 32, 64], [32, 32, 64], [16, 8, 16], 4, 1),
|
||||
GemmTuningConfig([128, 128, 32], [64, 64, 32], [16, 8, 16], 4, 1),
|
||||
GemmTuningConfig([128, 128, 64], [64, 64, 64], [16, 8, 16], 4, 1),
|
||||
GemmTuningConfig([128, 128, 64], [128, 32, 64], [16, 8, 16], 4, 2),
|
||||
GemmTuningConfig([256, 64, 64], [64, 64, 64], [16, 8, 16], 4, 1),
|
||||
GemmTuningConfig([256, 64, 32], [64, 64, 32], [16, 8, 16], 4, 1),
|
||||
GemmTuningConfig([16, 64, 64], [16, 32, 64], [16, 8, 16], 5, 2),
|
||||
GemmTuningConfig([16, 128, 64], [16, 32, 64], [16, 8, 16], 5, 3),
|
||||
GemmTuningConfig([32, 64, 64], [16, 32, 64], [16, 8, 16], 5, 1),
|
||||
GemmTuningConfig([32, 128, 64], [32, 32, 64], [16, 8, 16], 5, 3),
|
||||
GemmTuningConfig([64, 64, 64], [32, 32, 64], [16, 8, 16], 5, 1),
|
||||
GemmTuningConfig([64, 128, 64], [64, 64, 64], [16, 8, 16], 5, 3),
|
||||
GemmTuningConfig([128, 128, 32], [64, 64, 32], [16, 8, 16], 5, 1),
|
||||
GemmTuningConfig([128, 128, 64], [64, 64, 64], [16, 8, 16], 5, 1),
|
||||
GemmTuningConfig([128, 128, 64], [128, 32, 64], [16, 8, 16], 5, 2),
|
||||
GemmTuningConfig([64, 128, 32], [32, 64, 32], [16, 8, 16], 6, 1),
|
||||
GemmTuningConfig([128, 64, 32], [64, 32, 32], [16, 8, 16], 6, 1),
|
||||
GemmTuningConfig([128, 32, 32], [32, 32, 32], [16, 8, 16], 7, 1),
|
||||
GemmTuningConfig([64, 64, 32], [32, 32, 32], [16, 8, 16], 10, 1),
|
||||
]
|
||||
return all_tuning_configs_fp16
|
||||
|
||||
|
||||
def all_configs_sm80_fp32():
|
||||
all_tuning_configs_fp32 = [
|
||||
GemmTuningConfig([64, 64, 16], [32, 32, 16], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([64, 64, 32], [32, 32, 32], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([64, 128, 32], [32, 64, 32], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([64, 256, 16], [32, 64, 16], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([64, 256, 32], [32, 64, 32], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([128, 64, 32], [64, 32, 32], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([128, 128, 16], [32, 64, 16], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([128, 128, 32], [32, 64, 32], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([256, 64, 16], [64, 32, 16], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([256, 64, 32], [64, 32, 32], [16, 8, 8], 3, 1),
|
||||
GemmTuningConfig([64, 128, 16], [32, 64, 16], [16, 8, 8], 4, 1),
|
||||
GemmTuningConfig([128, 64, 16], [64, 32, 16], [16, 8, 8], 4, 1),
|
||||
GemmTuningConfig([128, 128, 16], [32, 64, 16], [16, 8, 8], 4, 1),
|
||||
]
|
||||
return all_tuning_configs_fp32
|
||||
|
||||
|
||||
def generate_autotune_wrapper(datatype, num_configs):
|
||||
repeat_func_strs = []
|
||||
for i in range(num_configs):
|
||||
repeat_func_strs.append(f"func<{i}>")
|
||||
code_str = autotune_wrapper_template.replace(
|
||||
"${datatype}", datatype
|
||||
).replace("${repeat_functions}", ", \\\n ".join(repeat_func_strs))
|
||||
return code_str
|
||||
|
||||
|
||||
def get_configs(all_configs_list, level=3):
|
||||
consigs_list = []
|
||||
for i in range(len(all_configs_list)):
|
||||
if all_configs_list[i].level <= level:
|
||||
already_have = False
|
||||
for config in consigs_list:
|
||||
if config == all_configs_list[i]:
|
||||
print(f"-- The {i}-th config is repeat.")
|
||||
already_have = True
|
||||
break
|
||||
if not already_have:
|
||||
consigs_list.append(all_configs_list[i])
|
||||
return consigs_list
|
||||
|
||||
|
||||
def generate_configs(configs_list, config_template_0, config_template):
|
||||
code_str = ""
|
||||
config_id = 0
|
||||
for config in configs_list:
|
||||
if config_id == 0:
|
||||
config_code_str = (
|
||||
config_template_0.replace(
|
||||
"${tshape}", ", ".join(map(str, config.tshape))
|
||||
)
|
||||
.replace("${wshape}", ", ".join(map(str, config.wshape)))
|
||||
.replace("${ishape}", ", ".join(map(str, config.ishape)))
|
||||
.replace("${stages}", str(config.stages))
|
||||
)
|
||||
else:
|
||||
config_code_str = (
|
||||
config_template.replace(
|
||||
"${tshape}", ", ".join(map(str, config.tshape))
|
||||
)
|
||||
.replace("${wshape}", ", ".join(map(str, config.wshape)))
|
||||
.replace("${ishape}", ", ".join(map(str, config.ishape)))
|
||||
.replace("${stages}", str(config.stages))
|
||||
.replace("${config_id}", str(config_id))
|
||||
)
|
||||
code_str += config_code_str
|
||||
config_id += 1
|
||||
return len(configs_list), code_str
|
||||
|
||||
|
||||
def main():
|
||||
level = 1
|
||||
num_fp16_configs, fp16_configs_code_str = generate_configs(
|
||||
configs_list=get_configs(all_configs_sm80_fp16(), level=level),
|
||||
config_template_0=fp16_config_template_0,
|
||||
config_template=fp16_config_template,
|
||||
)
|
||||
num_fp32_configs, fp32_configs_code_str = generate_configs(
|
||||
configs_list=get_configs(all_configs_sm80_fp32(), level=level),
|
||||
config_template_0=fp32_config_template_0,
|
||||
config_template=fp32_config_template,
|
||||
)
|
||||
print(
|
||||
f"-- Total {num_fp16_configs} fp16 configs, {num_fp32_configs} fp32 configs"
|
||||
)
|
||||
head_code_str = head_template.replace(
|
||||
"${num_configs_fp16}", str(num_fp16_configs)
|
||||
).replace("${num_configs_fp32}", str(num_fp32_configs))
|
||||
fp16_autotune_wrapper_code_str = generate_autotune_wrapper(
|
||||
"half", num_fp16_configs
|
||||
)
|
||||
fp32_autotune_wrapper_code_str = generate_autotune_wrapper(
|
||||
"float", num_fp32_configs
|
||||
)
|
||||
with open("all_tuning_configs.h", "w") as f:
|
||||
f.write(head_code_str)
|
||||
f.write(fp16_autotune_wrapper_code_str)
|
||||
f.write(fp32_autotune_wrapper_code_str)
|
||||
f.write(fp16_configs_code_str)
|
||||
f.write(fp32_configs_code_str)
|
||||
f.write(tail_code_str)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include <cuda.h>
|
||||
#include <cuda_bf16.h>
|
||||
#include <cuda_fp16.h>
|
||||
#endif
|
||||
#ifdef __HIPCC__
|
||||
#include <hip/hip_bfloat16.h>
|
||||
#include <hip/hip_fp16.h>
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
|
||||
namespace ap {
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __host__ __device__ T ComputePow(T base, T exponent) {
|
||||
T res = (exponent == static_cast<T>(2))
|
||||
? (base * base)
|
||||
: ((exponent == static_cast<T>(3)) ? (base * base * base)
|
||||
: (powf(base, exponent)));
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include <cuda.h>
|
||||
#include <cuda_bf16.h>
|
||||
#include <cuda_fp16.h>
|
||||
|
||||
#define CHECK_CUDA(func) \
|
||||
{ \
|
||||
cudaError_t err = func; \
|
||||
if (err != cudaSuccess) { \
|
||||
std::cerr << "[" << __FILE__ << ":" << __LINE__ << ", " << __FUNCTION__ \
|
||||
<< "] " \
|
||||
<< "CUDA error(" << err << "), " << cudaGetErrorString(err) \
|
||||
<< " when call " << #func << std::endl; \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
}
|
||||
|
||||
#include "cutlass_patch/cuda/cutlass_matmul.cuh" // NOLINT
|
||||
#include "math_function.h" // NOLINT
|
||||
#include "profile.h" // NOLINT
|
||||
#endif
|
||||
|
||||
#ifdef __HIPCC__
|
||||
#include <hip/hip_bfloat16.h>
|
||||
#include <hip/hip_fp16.h>
|
||||
#include <hip/hip_runtime.h>
|
||||
|
||||
#define CHECK_HIP(func) \
|
||||
{ \
|
||||
hipError_t err = func; \
|
||||
if (err != hipSuccess) { \
|
||||
std::cerr << "[" << __FILE__ << ":" << __LINE__ << ", " << __FUNCTION__ \
|
||||
<< "] " \
|
||||
<< "HIP error(" << err << "), " << hipGetErrorString(err) \
|
||||
<< " when call " << #func << std::endl; \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
}
|
||||
|
||||
#include "cutlass_patch/hip/hytlass_matmul.h" // NOLINT
|
||||
#include "math_function.h" // NOLINT
|
||||
#include "profile.h" // NOLINT
|
||||
#endif
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define ASSERT_CHECK(__cond) \
|
||||
do { \
|
||||
const bool __cond_var = (__cond); \
|
||||
if (!__cond_var) { \
|
||||
::std::string __err_msg = ::std::string("`") + #__cond + \
|
||||
"` check failed at " + __FILE__ + ":" + \
|
||||
::std::to_string(__LINE__); \
|
||||
throw std::runtime_error(__err_msg); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace ap {
|
||||
|
||||
inline int CheckedCastToInt(int64_t value) {
|
||||
ASSERT_CHECK(value >= 0);
|
||||
ASSERT_CHECK(value <= static_cast<int64_t>(std::numeric_limits<int>::max()));
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
template <typename T, int Dim>
|
||||
struct Alignment {
|
||||
static constexpr int kValue =
|
||||
((Dim % 8) == 0) ? 8
|
||||
: (((Dim % 4) == 0) ? 4 : (((Dim % 2) == 0) ? 2 : 1));
|
||||
};
|
||||
|
||||
template <int Dim>
|
||||
struct Alignment<float, Dim> {
|
||||
static constexpr int kValue =
|
||||
((Dim % 4) == 0) ? 4 : (((Dim % 2) == 0) ? 2 : 1);
|
||||
};
|
||||
|
||||
struct GemmEpilogueParams {
|
||||
int batch_count;
|
||||
int m;
|
||||
int n;
|
||||
int k;
|
||||
|
||||
bool transpose_a;
|
||||
bool transpose_b;
|
||||
|
||||
// Shape related aruguments
|
||||
struct ShapeArguments {
|
||||
int64_t batch_stride_A;
|
||||
int64_t batch_stride_B;
|
||||
int64_t batch_stride_C;
|
||||
int64_t batch_stride_D;
|
||||
int64_t lda;
|
||||
int64_t ldb;
|
||||
int64_t ldc_bias;
|
||||
int64_t ldd;
|
||||
};
|
||||
|
||||
ShapeArguments shape_args;
|
||||
|
||||
const void *input;
|
||||
const void *weight;
|
||||
const void *bias;
|
||||
void *output;
|
||||
|
||||
void *stream_ptr;
|
||||
|
||||
std::vector<int64_t> input0_shape;
|
||||
std::vector<int64_t> input1_shape;
|
||||
std::vector<const void *> epilogue_in_ptrs;
|
||||
std::vector<void *> epilogue_out_ptrs;
|
||||
std::vector<std::vector<int64_t>> epilogue_in_shapes;
|
||||
std::vector<std::vector<int64_t>> epilogue_out_shapes;
|
||||
|
||||
GemmEpilogueParams() {}
|
||||
GemmEpilogueParams(void *stream_ptr,
|
||||
const void *input,
|
||||
const void *weight,
|
||||
const void *bias,
|
||||
void *output,
|
||||
const std::vector<int64_t> &input_shape,
|
||||
const std::vector<int64_t> &weight_shape,
|
||||
const std::vector<int64_t> &bias_shape,
|
||||
bool transpose_a = false,
|
||||
bool transpose_b = false)
|
||||
: stream_ptr(stream_ptr),
|
||||
input(input),
|
||||
weight(weight),
|
||||
bias(bias),
|
||||
output(output),
|
||||
transpose_a(transpose_a),
|
||||
transpose_b(transpose_b) {
|
||||
ASSERT_CHECK(input_shape.size() >= 2U);
|
||||
ASSERT_CHECK(weight_shape.size() >= 2U);
|
||||
|
||||
input0_shape = input_shape;
|
||||
input1_shape = weight_shape;
|
||||
|
||||
int64_t batch_count_i64 = 1;
|
||||
for (size_t i = 0; i < input_shape.size() - 2; ++i) {
|
||||
batch_count_i64 *= input_shape[i];
|
||||
}
|
||||
batch_count = CheckedCastToInt(batch_count_i64);
|
||||
|
||||
int64_t m_i64;
|
||||
int64_t n_i64;
|
||||
int64_t k_i64;
|
||||
if (transpose_a) {
|
||||
m_i64 = input_shape[input_shape.size() - 1];
|
||||
k_i64 = input_shape[input_shape.size() - 2];
|
||||
} else {
|
||||
m_i64 = input_shape[input_shape.size() - 2];
|
||||
k_i64 = input_shape[input_shape.size() - 1];
|
||||
}
|
||||
if (transpose_b) {
|
||||
ASSERT_CHECK(weight_shape[weight_shape.size() - 1] == k_i64);
|
||||
n_i64 = weight_shape[weight_shape.size() - 2];
|
||||
} else {
|
||||
ASSERT_CHECK(weight_shape[weight_shape.size() - 2] == k_i64);
|
||||
n_i64 = weight_shape[weight_shape.size() - 1];
|
||||
}
|
||||
m = CheckedCastToInt(m_i64);
|
||||
n = CheckedCastToInt(n_i64);
|
||||
k = CheckedCastToInt(k_i64);
|
||||
|
||||
if (bias) {
|
||||
ASSERT_CHECK(bias_shape.size() >= 1U);
|
||||
ASSERT_CHECK(bias_shape[bias_shape.size() - 1] == n_i64);
|
||||
}
|
||||
|
||||
#if AP_ENABLE_DEBUG
|
||||
std::cout << "-- [GemmEpilogueParams] batch_count: " << batch_count
|
||||
<< ", m: " << m << ", n: " << n << ", k: " << k << std::endl;
|
||||
std::cout << "-- [GemmEpilogueParams] input: " << input << std::endl;
|
||||
std::cout << "-- [GemmEpilogueParams] weight: " << weight << std::endl;
|
||||
std::cout << "-- [GemmEpilogueParams] bias: " << bias << std::endl;
|
||||
std::cout << "-- [GemmEpilogueParams] output: " << output << std::endl;
|
||||
std::cout << "-- [GemmEpilogueParams] stream: " << stream << std::endl;
|
||||
#endif
|
||||
|
||||
shape_args.batch_stride_A = m_i64 * k_i64;
|
||||
shape_args.batch_stride_B = (weight_shape.size() == 2) ? 0 : n_i64 * k_i64;
|
||||
shape_args.batch_stride_D = m_i64 * n_i64;
|
||||
|
||||
shape_args.lda = transpose_a ? m_i64 : k_i64;
|
||||
shape_args.ldb = transpose_b ? k_i64 : n_i64;
|
||||
shape_args.ldd = n_i64;
|
||||
|
||||
bool is_C_bias = bias_shape.size() == 1UL;
|
||||
|
||||
/// Only available in RRR format
|
||||
shape_args.batch_stride_C = (!bias || is_C_bias) ? 0 : m_i64 * n_i64;
|
||||
shape_args.ldc_bias = (!bias || is_C_bias) ? 0 : n_i64;
|
||||
}
|
||||
|
||||
void SetEpilogues(const std::vector<const void *> &in_ptrs,
|
||||
const std::vector<void *> &out_ptrs) {
|
||||
epilogue_in_ptrs = in_ptrs;
|
||||
epilogue_out_ptrs = out_ptrs;
|
||||
}
|
||||
|
||||
void SetEpilogueAndShapes(
|
||||
const std::vector<const void *> &in_ptrs,
|
||||
const std::vector<std::vector<int64_t>> &in_shapes,
|
||||
const std::vector<void *> &out_ptrs,
|
||||
const std::vector<std::vector<int64_t>> &out_shapes) {
|
||||
ASSERT_CHECK(in_ptrs.size() == in_shapes.size());
|
||||
epilogue_in_ptrs = in_ptrs;
|
||||
epilogue_in_shapes = in_shapes;
|
||||
ASSERT_CHECK(out_ptrs.size() == out_shapes.size());
|
||||
epilogue_out_ptrs = out_ptrs;
|
||||
epilogue_out_shapes = out_shapes;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#ifdef __NVCC__
|
||||
#include <cuda_profiler_api.h>
|
||||
|
||||
#define GPUEvent_t cudaEvent_t
|
||||
|
||||
#ifndef GPUStream_t
|
||||
#define GPUStream_t cudaStream_t
|
||||
#endif
|
||||
|
||||
#define GPUEventCreate(e) cudaEventCreate(e)
|
||||
#define GPUEventDestroy(e) cudaEventDestroy(e)
|
||||
#define GPUEventRecord(e, s) cudaEventRecord(e, s)
|
||||
#define GPUEventSynchronize(e) cudaEventSynchronize(e)
|
||||
#define GPUEventElapsedTime(ms, s, e) cudaEventElapsedTime(ms, s, e)
|
||||
#define GPUProfilerStart() cudaProfilerStart()
|
||||
#define GPUProfilerStop() cudaProfilerStop()
|
||||
#define GPUStreamSynchronize(s) cudaStreamSynchronize(s)
|
||||
#define CHECK_GPU CHECK_CUDA
|
||||
#endif
|
||||
|
||||
#ifdef __HIPCC__
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <hip/hip_runtime_api.h>
|
||||
|
||||
#define GPUEvent_t hipEvent_t
|
||||
|
||||
#ifndef GPUStream_t
|
||||
#define GPUStream_t hipStream_t
|
||||
#endif
|
||||
|
||||
#define GPUEventCreate(e) hipEventCreate(e)
|
||||
#define GPUEventDestroy(e) hipEventDestroy(e)
|
||||
#define GPUEventRecord(e, s) hipEventRecord(e, s)
|
||||
#define GPUEventSynchronize(e) hipEventSynchronize(e)
|
||||
#define GPUEventElapsedTime(ms, s, e) hipEventElapsedTime(ms, s, e)
|
||||
#define GPUProfilerStart() hipProfilerStart()
|
||||
#define GPUProfilerStop() hipProfilerStop()
|
||||
#define GPUStreamSynchronize(s) hipStreamSynchronize(s)
|
||||
#define CHECK_GPU CHECK_HIP
|
||||
#endif
|
||||
|
||||
namespace ap {
|
||||
|
||||
class GpuTimer {
|
||||
public:
|
||||
explicit GpuTimer(bool profile) : profile_(profile) {
|
||||
CHECK_GPU(GPUEventCreate(&start_));
|
||||
CHECK_GPU(GPUEventCreate(&stop_));
|
||||
}
|
||||
|
||||
~GpuTimer() {
|
||||
CHECK_GPU(GPUEventDestroy(start_));
|
||||
CHECK_GPU(GPUEventDestroy(stop_));
|
||||
}
|
||||
|
||||
void Start(GPUStream_t stream) {
|
||||
CHECK_GPU(GPUEventRecord(start_, stream));
|
||||
if (profile_) {
|
||||
CHECK_GPU(GPUProfilerStart());
|
||||
}
|
||||
}
|
||||
|
||||
void Stop(GPUStream_t stream) {
|
||||
CHECK_GPU(GPUEventRecord(stop_, stream));
|
||||
if (profile_) {
|
||||
CHECK_GPU(GPUProfilerStop());
|
||||
}
|
||||
}
|
||||
|
||||
float ElapsedTime() {
|
||||
float milliseconds = 0;
|
||||
CHECK_GPU(GPUEventSynchronize(stop_));
|
||||
CHECK_GPU(GPUEventElapsedTime(&milliseconds, start_, stop_));
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
private:
|
||||
bool profile_{false};
|
||||
GPUEvent_t start_{nullptr};
|
||||
GPUEvent_t stop_{nullptr};
|
||||
};
|
||||
|
||||
template <typename FuncType, typename... Args>
|
||||
int ProfileBestConfig(const std::vector<FuncType> &funcs,
|
||||
void *stream_ptr,
|
||||
Args &&...args) {
|
||||
std::cout
|
||||
<< "=================================================================="
|
||||
<< std::endl;
|
||||
|
||||
constexpr int kWarmupIters = 1;
|
||||
constexpr int kRepeatIters = 100;
|
||||
|
||||
GpuTimer gpu_timer(false);
|
||||
float min_time_ms = 100000.f;
|
||||
int min_time_idx = -1;
|
||||
|
||||
GPUStream_t stream = *reinterpret_cast<GPUStream_t *>(stream_ptr);
|
||||
|
||||
for (int idx = 0; idx < funcs.size(); ++idx) {
|
||||
auto func = funcs[idx];
|
||||
for (int i = 0; i < kWarmupIters; i++) {
|
||||
func(std::forward<Args>(args)...);
|
||||
}
|
||||
if (stream) {
|
||||
CHECK_GPU(GPUStreamSynchronize(stream));
|
||||
}
|
||||
|
||||
gpu_timer.Start(stream);
|
||||
for (int i = 0; i < kRepeatIters; i++) {
|
||||
func(std::forward<Args>(args)...);
|
||||
}
|
||||
gpu_timer.Stop(stream);
|
||||
|
||||
float elapsed_time_ms = gpu_timer.ElapsedTime();
|
||||
std::cout << "-- [ProfileBestConfig] No " << idx
|
||||
<< ", elapsed_time: " << elapsed_time_ms << " ms" << std::endl;
|
||||
if (elapsed_time_ms < min_time_ms) {
|
||||
min_time_ms = elapsed_time_ms;
|
||||
min_time_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "-- [ProfileBestConfig] best config idx: " << min_time_idx
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< "=================================================================="
|
||||
<< std::endl;
|
||||
return min_time_idx;
|
||||
}
|
||||
|
||||
} // namespace ap
|
||||
@@ -0,0 +1,140 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import access_topo_drr
|
||||
import pir
|
||||
|
||||
|
||||
class RemoveDataOpPairPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, src_data_op_name, dst_data_op_name):
|
||||
self.src_data_op_name = pir.a_str(src_data_op_name)
|
||||
self.dst_data_op_name = pir.a_str(dst_data_op_name)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.src_data_op = o.ap_native_op("pd_op.data")
|
||||
o.src_data_op([], [t.input0])
|
||||
o.dst_data_op = o.ap_native_op("pd_op.data")
|
||||
o.dst_data_op([], [t.input1])
|
||||
o.up_spider_op = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider_op([t.input0, t.input1], [])
|
||||
|
||||
def constraint(self, o, t):
|
||||
return [o.src_data_op.name, o.dst_data_op.name] == [
|
||||
self.src_data_op_name,
|
||||
self.dst_data_op_name,
|
||||
]
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
|
||||
|
||||
class RemoveDataOp2SumOp2DataOpPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, src_data_op_name, dst_data_op_name):
|
||||
self.src_data_op_name = pir.a_str(src_data_op_name)
|
||||
self.dst_data_op_name = pir.a_str(dst_data_op_name)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.src_data_op = o.ap_native_op("pd_op.data")
|
||||
o.src_data_op.name = self.src_data_op_name
|
||||
o.src_data_op([], [t.input0])
|
||||
o.full_int_array_op = o.ap_native_op("pd_op.full_int_array")
|
||||
o.full_int_array_op([], [t.axis])
|
||||
o.sum_op = o.ap_native_op("pd_op.sum")
|
||||
o.sum_op([t.input0, t.axis], [t.sum_out])
|
||||
o.dst_data_op = o.ap_native_op("pd_op.data")
|
||||
o.dst_data_op.name = self.dst_data_op_name
|
||||
o.dst_data_op([], [t.input1])
|
||||
o.up_spider_op = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider_op([t.sum_out, t.input1], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
|
||||
|
||||
class RemoveElementInputIndexPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, src_data_op_name, dst_load_from_global_op_name):
|
||||
self.src_data_op_name = pir.a_str(src_data_op_name)
|
||||
self.dst_load_from_global_op_name = pir.a_str(
|
||||
dst_load_from_global_op_name
|
||||
)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.src_data_op = o.ap_native_op("pd_op.data")
|
||||
o.src_data_op.name = self.src_data_op_name
|
||||
o.src_data_op([], [t.src_input])
|
||||
|
||||
o.dst_load_from_global_op = o.ap_native_op("ap_op.load_from_global")
|
||||
o.dst_load_from_global_op.index_func_unique_id = (
|
||||
self.dst_load_from_global_op_name
|
||||
)
|
||||
o.dst_load_from_global_op(
|
||||
[t.dst_input], [t.dst_load_from_global_output]
|
||||
)
|
||||
o.up_spider_op = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider_op([t.src_input, t.dst_load_from_global_output], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
|
||||
|
||||
class RemoveBroadcastInputIndexPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, src_data_op_name, dst_load_from_global_op_name):
|
||||
self.src_data_op_name = pir.a_str(src_data_op_name)
|
||||
self.dst_load_from_global_op_name = pir.a_str(
|
||||
dst_load_from_global_op_name
|
||||
)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.src_data_op = o.ap_native_op("pd_op.data")
|
||||
o.src_data_op.name = self.src_data_op_name
|
||||
o.src_data_op([], [t.input0])
|
||||
o.full_int_array_op = o.ap_native_op("pd_op.full_int_array")
|
||||
o.full_int_array_op([], [t.axis])
|
||||
o.sum_op = o.ap_native_op("pd_op.sum")
|
||||
o.sum_op([t.input0, t.axis], [t.sum_out])
|
||||
o.dst_load_from_global_op = o.ap_native_op("ap_op.load_from_global")
|
||||
o.dst_load_from_global_op.index_func_unique_id = (
|
||||
self.dst_load_from_global_op_name
|
||||
)
|
||||
o.dst_load_from_global_op(
|
||||
[t.dst_input], [t.dst_load_from_global_output]
|
||||
)
|
||||
o.up_spider_op = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider_op([t.sum_out, t.dst_load_from_global_output], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
|
||||
|
||||
class RemoveOutputIndexPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, src_data_op_name, dst_store_to_global_op_name):
|
||||
self.src_data_op_name = pir.a_str(src_data_op_name)
|
||||
self.dst_store_to_global_op_name = pir.a_str(
|
||||
dst_store_to_global_op_name
|
||||
)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.src_data_op = o.ap_native_op("pd_op.data")
|
||||
o.src_data_op.name = self.src_data_op_name
|
||||
o.src_data_op([], [t.src_input])
|
||||
o.down_spider_op = o.ap_native_op("ap_op.down_spider")
|
||||
o.down_spider_op([t.src_input], [t.dst_output_val])
|
||||
o.dst_store_to_global_op = o.ap_native_op("ap_op.store_to_global")
|
||||
o.dst_store_to_global_op.index_func_unique_id = (
|
||||
self.dst_store_to_global_op_name
|
||||
)
|
||||
o.dst_store_to_global_op([t.dst_output, t.dst_output_val], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
@@ -0,0 +1,673 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import abstract_drr
|
||||
import access_topo_drr # noqa: F401
|
||||
import ap
|
||||
import index_program_translator_util
|
||||
import ir_tools
|
||||
import kernel_arg_id_util
|
||||
import kernel_arg_translator_util # noqa: F401
|
||||
import low_level_ir_code_gen_ctx_util # noqa: F401
|
||||
import matmul_epilogue_pass
|
||||
import matmul_variadic_tpl
|
||||
import op_compute_translator_util
|
||||
import op_conversion_drr_pass # noqa: F401
|
||||
import pir # noqa: F401
|
||||
import program_translator_util
|
||||
import topo_drr_pass
|
||||
import umprime # noqa: F401
|
||||
|
||||
|
||||
class MatmulEpilogueFusion(abstract_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
in_num = self.number_of_inputs()
|
||||
out_num = self.number_of_outputs()
|
||||
o.matmul_op = o.ap_native_op("pd_op.matmul")
|
||||
o.matmul_op([t.input0, t.input1], [t.mm_out])
|
||||
o.trivial_op = o.ap_trivial_fusion_op()
|
||||
o.trivial_op(
|
||||
[
|
||||
t.mm_out,
|
||||
*ap.map(
|
||||
lambda index: getattr(t, f"input{index + 2}"),
|
||||
range(in_num - 2),
|
||||
),
|
||||
],
|
||||
ap.map(lambda index: getattr(t, f"output{index}"), range(out_num)),
|
||||
)
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
in_num = self.number_of_inputs()
|
||||
out_num = self.number_of_outputs()
|
||||
o.fustion_op = o.ap_pattern_fusion_op(self.code_gen)
|
||||
o.fustion_op(
|
||||
ap.map(lambda index: getattr(t, f"input{index}"), range(in_num)),
|
||||
ap.map(lambda index: getattr(t, f"output{index}"), range(out_num)),
|
||||
)
|
||||
|
||||
def constraint(self, o, t):
|
||||
program = ir_tools.copy_fused_ops_to_program(
|
||||
o.trivial_op, tensor_match_ctx=t
|
||||
)
|
||||
print("before-umprime: ", program)
|
||||
# umprime passes
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
pass_manager.add_pass(ir_tools.create_access_topo_drr_pass("umprime"))
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(program)
|
||||
print("before-access_topo_pass", program)
|
||||
init_pass_manager = ir_tools.create_pass_manager()
|
||||
init_down_spider = topo_drr_pass.InitDownSpiderAccessTopoPass("mm_out")
|
||||
init_pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(init_down_spider)
|
||||
)
|
||||
outputs_name_list = ap.map(
|
||||
lambda i: f"output{i}", range(self.number_of_outputs())
|
||||
)
|
||||
inputs_name_list = (
|
||||
ap.map(
|
||||
lambda i: f"input{i + 2}", range(self.number_of_inputs() - 2)
|
||||
)
|
||||
if self.number_of_inputs() > 2
|
||||
else []
|
||||
)
|
||||
print('inputs_name_list: ', ', '.join(inputs_name_list))
|
||||
init_fake_data_for_yield_input = (
|
||||
topo_drr_pass.FakeDataForYieldAccessTopoPass(outputs_name_list)
|
||||
)
|
||||
init_pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(
|
||||
init_fake_data_for_yield_input
|
||||
)
|
||||
)
|
||||
init_pass_manager.run(program)
|
||||
print("after-init-access_topo_pass", program)
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
pass_manager.add_pass(ir_tools.create_access_topo_drr_pass("default"))
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(program)
|
||||
print("after-apply-access_topo_pass", program)
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
ap.map(
|
||||
lambda dst_name: pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(
|
||||
matmul_epilogue_pass.RemoveDataOpPairPass(
|
||||
src_data_op_name="mm_out", dst_data_op_name=dst_name
|
||||
)
|
||||
)
|
||||
),
|
||||
inputs_name_list,
|
||||
)
|
||||
ap.map(
|
||||
lambda dst_name: pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(
|
||||
matmul_epilogue_pass.RemoveDataOp2SumOp2DataOpPass(
|
||||
src_data_op_name="mm_out", dst_data_op_name=dst_name
|
||||
)
|
||||
)
|
||||
),
|
||||
inputs_name_list,
|
||||
)
|
||||
|
||||
ap.map(
|
||||
lambda dst_name: pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(
|
||||
matmul_epilogue_pass.RemoveDataOpPairPass(
|
||||
src_data_op_name="mm_out", dst_data_op_name=dst_name
|
||||
)
|
||||
)
|
||||
),
|
||||
outputs_name_list,
|
||||
)
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(program)
|
||||
print("after-remove-input-output-access_topo_pass", program)
|
||||
return program.empty()
|
||||
|
||||
def _insert_load_from_global(self, program, input_names):
|
||||
init_pass_manager = ir_tools.create_pass_manager()
|
||||
|
||||
def AddPass(input_name):
|
||||
ir_pass = topo_drr_pass.InitNaiveLoadFromGlobalAccessTopoPass(
|
||||
input_name
|
||||
)
|
||||
init_pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(ir_pass)
|
||||
)
|
||||
|
||||
ap.map(AddPass, input_names)
|
||||
init_pass_manager.run(program)
|
||||
|
||||
def _insert_store_to_global(self, program, output_names):
|
||||
init_pass_manager = ir_tools.create_pass_manager()
|
||||
ir_pass = topo_drr_pass.FakeDataStoreToGlobalForYieldAccessTopoPass(
|
||||
output_names
|
||||
)
|
||||
init_pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(ir_pass)
|
||||
)
|
||||
init_pass_manager.run(program)
|
||||
|
||||
def _make_kernel_arg_translator(self):
|
||||
return matmul_variadic_tpl.make_kernel_arg_translator()
|
||||
|
||||
def _apply_topo_access_passes(self, mut_program, anchor_data_op_name):
|
||||
init_pass_manager = ir_tools.create_pass_manager()
|
||||
init_down_spider = topo_drr_pass.InitDownSpiderAccessTopoPass(
|
||||
anchor_data_op_name
|
||||
)
|
||||
init_pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(init_down_spider)
|
||||
)
|
||||
init_pass_manager.run(mut_program)
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
pass_manager.add_pass(ir_tools.create_access_topo_drr_pass("default"))
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(mut_program)
|
||||
|
||||
def _simplify_index_program(self, mut_program):
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
drr_pass = topo_drr_pass.ConvertUpSpiderStoreDataOpToYieldOpPass()
|
||||
pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(drr_pass)
|
||||
)
|
||||
drr_pass = topo_drr_pass.ConvertDownSpiderStoreDataOpToYieldOpPass()
|
||||
pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(drr_pass)
|
||||
)
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(mut_program)
|
||||
return mut_program
|
||||
|
||||
def _make_index_func_unique_id2index_program(
|
||||
self, compute_program, anchor_data_op_name, input_names, output_names
|
||||
):
|
||||
full_index_program = compute_program.clone()
|
||||
self._apply_topo_access_passes(full_index_program, anchor_data_op_name)
|
||||
print('full_index_program: ', full_index_program)
|
||||
|
||||
def MatchAndCopyInputIndex(dst_input_name):
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
removed_programs = ap.MutableList()
|
||||
rm_elementwise_drr_pass = (
|
||||
matmul_epilogue_pass.RemoveElementInputIndexPass(
|
||||
src_data_op_name=anchor_data_op_name,
|
||||
dst_load_from_global_op_name=dst_input_name,
|
||||
)
|
||||
)
|
||||
rm_elementwise_ir_pass = (
|
||||
ir_tools.create_access_topo_drr_one_step_pass(
|
||||
rm_elementwise_drr_pass,
|
||||
matched_pattern_mut_list=removed_programs,
|
||||
)
|
||||
)
|
||||
pass_manager.add_pass(rm_elementwise_ir_pass)
|
||||
rm_broadcast_drr_pass = (
|
||||
matmul_epilogue_pass.RemoveBroadcastInputIndexPass(
|
||||
src_data_op_name=anchor_data_op_name,
|
||||
dst_load_from_global_op_name=dst_input_name,
|
||||
)
|
||||
)
|
||||
rm_broadcast_ir_pass = (
|
||||
ir_tools.create_access_topo_drr_one_step_pass(
|
||||
rm_broadcast_drr_pass,
|
||||
matched_pattern_mut_list=removed_programs,
|
||||
)
|
||||
)
|
||||
pass_manager.add_pass(rm_broadcast_ir_pass)
|
||||
pass_manager.run(full_index_program)
|
||||
|
||||
def Converter(program):
|
||||
return [dst_input_name, self._simplify_index_program(program)]
|
||||
|
||||
return ap.map(Converter, removed_programs)
|
||||
|
||||
input_and_index_programs = ap.flat_map(
|
||||
MatchAndCopyInputIndex, input_names
|
||||
)
|
||||
|
||||
def MatchAndCopyOutputIndex(dst_output_name):
|
||||
print('full_index_program output: ', full_index_program)
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
removed_programs = ap.MutableList()
|
||||
drr_pass = matmul_epilogue_pass.RemoveOutputIndexPass(
|
||||
src_data_op_name=anchor_data_op_name,
|
||||
dst_store_to_global_op_name=dst_output_name,
|
||||
)
|
||||
ir_pass = ir_tools.create_access_topo_drr_one_step_pass(
|
||||
drr_pass, matched_pattern_mut_list=removed_programs
|
||||
)
|
||||
pass_manager.add_pass(ir_pass)
|
||||
pass_manager.run(full_index_program)
|
||||
|
||||
def Converter(program):
|
||||
return [dst_output_name, self._simplify_index_program(program)]
|
||||
|
||||
print('len removed of output: ', len(removed_programs))
|
||||
return ap.map(Converter, removed_programs)
|
||||
|
||||
output_and_index_programs = ap.flat_map(
|
||||
MatchAndCopyOutputIndex, output_names
|
||||
)
|
||||
return ap.OrderedDict(
|
||||
[*input_and_index_programs, *output_and_index_programs]
|
||||
)
|
||||
|
||||
def _replace_with_load_from_register(
|
||||
self, mut_program, load_ir_value_name, register_var_name
|
||||
):
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
drr_pass = topo_drr_pass.ReplaceWithLoadFromRegisterPass(
|
||||
name=load_ir_value_name, register_var_name=register_var_name
|
||||
)
|
||||
pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(drr_pass)
|
||||
)
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(mut_program)
|
||||
return mut_program
|
||||
|
||||
def _replace_with_store_to_register(
|
||||
self, mut_program, store_ir_value_name, register_var_name
|
||||
):
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
drr_pass = topo_drr_pass.ReplaceWithStoreToRegisterPass(
|
||||
name=store_ir_value_name, register_var_name=register_var_name
|
||||
)
|
||||
pass_manager.add_pass(
|
||||
ir_tools.create_access_topo_drr_one_step_pass(drr_pass)
|
||||
)
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(mut_program)
|
||||
return mut_program
|
||||
|
||||
def _get_program_translator(self, ctx, o, t):
|
||||
outputs_name_list = ap.map(
|
||||
lambda i: f"output{i}", range(self.number_of_outputs())
|
||||
)
|
||||
other_outputs_name_list = ap.map(
|
||||
lambda i: f"output{i + 1}", range(self.number_of_outputs() - 1)
|
||||
)
|
||||
local_outputs_name_list = ap.map(
|
||||
lambda i: f"out{i}", range(self.number_of_outputs())
|
||||
)
|
||||
inputs_name_list = (
|
||||
ap.map(
|
||||
lambda i: f"input{i + 2}", range(self.number_of_inputs() - 2)
|
||||
)
|
||||
if self.number_of_inputs() > 2
|
||||
else []
|
||||
)
|
||||
mut_program = ir_tools.copy_fused_ops_to_program(
|
||||
o.trivial_op, tensor_match_ctx=t
|
||||
)
|
||||
print("before-umprime: ", mut_program)
|
||||
pass_manager = ir_tools.create_pass_manager()
|
||||
pass_manager.add_pass(ir_tools.create_access_topo_drr_pass("umprime"))
|
||||
pass_manager.add_pass(ir_tools.create_dce_pass())
|
||||
pass_manager.run(mut_program)
|
||||
self._insert_load_from_global(mut_program, input_names=["mm_out"])
|
||||
self._insert_load_from_global(mut_program, input_names=inputs_name_list)
|
||||
self._insert_store_to_global(
|
||||
mut_program, output_names=outputs_name_list
|
||||
)
|
||||
kernel_arg_translator = self._make_kernel_arg_translator()
|
||||
index_func_unique_id2index_program = (
|
||||
self._make_index_func_unique_id2index_program(
|
||||
mut_program,
|
||||
anchor_data_op_name="mm_out",
|
||||
input_names=inputs_name_list,
|
||||
output_names=other_outputs_name_list,
|
||||
)
|
||||
)
|
||||
print(
|
||||
"index_func_unique_id2index_program:\n",
|
||||
index_func_unique_id2index_program,
|
||||
)
|
||||
index_program_translator_map = index_program_translator_util.IndexProgramTranslatorMap(
|
||||
index_func_unique_id2index_program=index_func_unique_id2index_program,
|
||||
kernel_arg_translator=kernel_arg_translator,
|
||||
anchor_iter_var_names=matmul_variadic_tpl.get_anchor_iter_var_names(),
|
||||
)
|
||||
self._replace_with_load_from_register(
|
||||
mut_program, load_ir_value_name="mm_out", register_var_name="x"
|
||||
)
|
||||
self._replace_with_store_to_register(mut_program, "output0", "out")
|
||||
print("mut_program:", mut_program)
|
||||
op_compute_translator_maker = (
|
||||
op_compute_translator_util.OpComputeTranslatorFactory()
|
||||
)
|
||||
program_translator = program_translator_util.ProgramTranslator(
|
||||
program_property=mut_program.copy_to_const_program_data(),
|
||||
kernel_arg_translator=kernel_arg_translator,
|
||||
index_program_translator_map=index_program_translator_map,
|
||||
op_translator_maker=op_compute_translator_maker,
|
||||
)
|
||||
|
||||
return program_translator
|
||||
|
||||
def code_gen(self, ctx, o, t):
|
||||
program_translator = self._get_program_translator(ctx, o, t)
|
||||
mut_kernel_arg_id_registry = kernel_arg_id_util.KernelArgIdNameRegistry(
|
||||
code_gen_ctx=ctx, tensor_match_ctx=t, name_prefix=""
|
||||
)
|
||||
|
||||
template_module = matmul_variadic_tpl.MatmulVariadicTemplate(
|
||||
program_translator=program_translator,
|
||||
mut_kernel_arg_id_registry=mut_kernel_arg_id_registry,
|
||||
)
|
||||
|
||||
def get_symbolic_shape_args_list(sym_dim):
|
||||
return ctx.dim_expr_kernel_arg_id(sym_dim)
|
||||
|
||||
input0_shape_kargs = ap.map(
|
||||
get_symbolic_shape_args_list, t.input0.symbolic_shape_to_list()
|
||||
)
|
||||
input1_shape_kargs = ap.map(
|
||||
get_symbolic_shape_args_list, t.input1.symbolic_shape_to_list()
|
||||
)
|
||||
return template_module.compile(
|
||||
input0_karg=ctx.in_tensor_data_ptr_kernel_arg_id(t.input0),
|
||||
input1_karg=ctx.in_tensor_data_ptr_kernel_arg_id(t.input1),
|
||||
output_karg=ctx.out_tensor_data_ptr_kernel_arg_id(t.output0),
|
||||
input0_shape_kargs=input0_shape_kargs,
|
||||
input1_shape_kargs=input1_shape_kargs,
|
||||
)
|
||||
|
||||
|
||||
class NumberOfInputsTrait0:
|
||||
def number_of_inputs(self):
|
||||
return 0
|
||||
|
||||
|
||||
class NumberOfInputsTrait1:
|
||||
def number_of_inputs(self):
|
||||
return 1
|
||||
|
||||
|
||||
class NumberOfInputsTrait2:
|
||||
def number_of_inputs(self):
|
||||
return 2
|
||||
|
||||
|
||||
class NumberOfInputsTrait3:
|
||||
def number_of_inputs(self):
|
||||
return 3
|
||||
|
||||
|
||||
class NumberOfInputsTrait4:
|
||||
def number_of_inputs(self):
|
||||
return 4
|
||||
|
||||
|
||||
class NumberOfInputsTrait5:
|
||||
def number_of_inputs(self):
|
||||
return 5
|
||||
|
||||
|
||||
class NumberOfInputsTrait6:
|
||||
def number_of_inputs(self):
|
||||
return 6
|
||||
|
||||
|
||||
class NumberOfInputsTrait7:
|
||||
def number_of_inputs(self):
|
||||
return 7
|
||||
|
||||
|
||||
class NumberOfInputsTrait8:
|
||||
def number_of_inputs(self):
|
||||
return 8
|
||||
|
||||
|
||||
class NumberOfInputsTrait9:
|
||||
def number_of_inputs(self):
|
||||
return 9
|
||||
|
||||
|
||||
class NumberOfInputsTrait10:
|
||||
def number_of_inputs(self):
|
||||
return 10
|
||||
|
||||
|
||||
class NumberOfInputsTrait11:
|
||||
def number_of_inputs(self):
|
||||
return 11
|
||||
|
||||
|
||||
class NumberOfInputsTrait12:
|
||||
def number_of_inputs(self):
|
||||
return 12
|
||||
|
||||
|
||||
class NumberOfInputsTrait13:
|
||||
def number_of_inputs(self):
|
||||
return 13
|
||||
|
||||
|
||||
class NumberOfInputsTrait14:
|
||||
def number_of_inputs(self):
|
||||
return 14
|
||||
|
||||
|
||||
class NumberOfInputsTrait15:
|
||||
def number_of_inputs(self):
|
||||
return 15
|
||||
|
||||
|
||||
class NumberOfInputsTrait16:
|
||||
def number_of_inputs(self):
|
||||
return 16
|
||||
|
||||
|
||||
class NumberOfInputsTrait17:
|
||||
def number_of_inputs(self):
|
||||
return 17
|
||||
|
||||
|
||||
class NumberOfOutputsTrait0:
|
||||
def number_of_outputs(self):
|
||||
return 0
|
||||
|
||||
|
||||
class NumberOfOutputsTrait1:
|
||||
def number_of_outputs(self):
|
||||
return 1
|
||||
|
||||
|
||||
class NumberOfOutputsTrait2:
|
||||
def number_of_outputs(self):
|
||||
return 2
|
||||
|
||||
|
||||
class NumberOfOutputsTrait3:
|
||||
def number_of_outputs(self):
|
||||
return 3
|
||||
|
||||
|
||||
class NumberOfOutputsTrait4:
|
||||
def number_of_outputs(self):
|
||||
return 4
|
||||
|
||||
|
||||
class NumberOfOutputsTrait5:
|
||||
def number_of_outputs(self):
|
||||
return 5
|
||||
|
||||
|
||||
class NumberOfOutputsTrait6:
|
||||
def number_of_outputs(self):
|
||||
return 6
|
||||
|
||||
|
||||
class NumberOfOutputsTrait7:
|
||||
def number_of_outputs(self):
|
||||
return 7
|
||||
|
||||
|
||||
class NumberOfOutputsTrait8:
|
||||
def number_of_outputs(self):
|
||||
return 8
|
||||
|
||||
|
||||
class NumberOfOutputsTrait9:
|
||||
def number_of_outputs(self):
|
||||
return 9
|
||||
|
||||
|
||||
class NumberOfOutputsTrait10:
|
||||
def number_of_outputs(self):
|
||||
return 10
|
||||
|
||||
|
||||
class NumberOfOutputsTrait11:
|
||||
def number_of_outputs(self):
|
||||
return 11
|
||||
|
||||
|
||||
class NumberOfOutputsTrait12:
|
||||
def number_of_outputs(self):
|
||||
return 12
|
||||
|
||||
|
||||
class NumberOfOutputsTrait13:
|
||||
def number_of_outputs(self):
|
||||
return 13
|
||||
|
||||
|
||||
class NumberOfOutputsTrait14:
|
||||
def number_of_outputs(self):
|
||||
return 14
|
||||
|
||||
|
||||
class NumberOfOutputsTrait15:
|
||||
def number_of_outputs(self):
|
||||
return 15
|
||||
|
||||
|
||||
class NumberOfOutputsTrait16:
|
||||
def number_of_outputs(self):
|
||||
return 16
|
||||
|
||||
|
||||
class NumberOfOutputsTrait17:
|
||||
def number_of_outputs(self):
|
||||
return 17
|
||||
|
||||
|
||||
class NumberOfOutputsTrait18:
|
||||
def number_of_outputs(self):
|
||||
return 18
|
||||
|
||||
|
||||
class NumberOfOutputsTrait19:
|
||||
def number_of_outputs(self):
|
||||
return 19
|
||||
|
||||
|
||||
class NumberOfOutputsTrait20:
|
||||
def number_of_outputs(self):
|
||||
return 20
|
||||
|
||||
|
||||
class NumberOfOutputsTrait21:
|
||||
def number_of_outputs(self):
|
||||
return 21
|
||||
|
||||
|
||||
class NumberOfOutputsTrait22:
|
||||
def number_of_outputs(self):
|
||||
return 22
|
||||
|
||||
|
||||
def get_mixin_class(base_class, number_of_inputs, number_of_outputs):
|
||||
num_inputs_to_input_trait_class = [
|
||||
None,
|
||||
NumberOfInputsTrait1,
|
||||
NumberOfInputsTrait2,
|
||||
NumberOfInputsTrait3,
|
||||
NumberOfInputsTrait3,
|
||||
NumberOfInputsTrait4,
|
||||
NumberOfInputsTrait5,
|
||||
NumberOfInputsTrait6,
|
||||
NumberOfInputsTrait7,
|
||||
NumberOfInputsTrait8,
|
||||
NumberOfInputsTrait9,
|
||||
NumberOfInputsTrait10,
|
||||
NumberOfInputsTrait11,
|
||||
NumberOfInputsTrait12,
|
||||
NumberOfInputsTrait13,
|
||||
NumberOfInputsTrait14,
|
||||
NumberOfInputsTrait15,
|
||||
NumberOfInputsTrait16,
|
||||
NumberOfInputsTrait17,
|
||||
]
|
||||
num_outputs_to_output_trait_class = [
|
||||
None,
|
||||
NumberOfOutputsTrait1,
|
||||
NumberOfOutputsTrait2,
|
||||
NumberOfOutputsTrait3,
|
||||
NumberOfOutputsTrait4,
|
||||
NumberOfOutputsTrait5,
|
||||
NumberOfOutputsTrait6,
|
||||
NumberOfOutputsTrait7,
|
||||
NumberOfOutputsTrait8,
|
||||
NumberOfOutputsTrait9,
|
||||
NumberOfOutputsTrait10,
|
||||
NumberOfOutputsTrait11,
|
||||
NumberOfOutputsTrait12,
|
||||
NumberOfOutputsTrait13,
|
||||
NumberOfOutputsTrait14,
|
||||
NumberOfOutputsTrait15,
|
||||
NumberOfOutputsTrait16,
|
||||
NumberOfOutputsTrait17,
|
||||
NumberOfOutputsTrait18,
|
||||
NumberOfOutputsTrait19,
|
||||
NumberOfOutputsTrait20,
|
||||
NumberOfOutputsTrait21,
|
||||
NumberOfOutputsTrait22,
|
||||
]
|
||||
return type(
|
||||
f"MatmulEpilogueFusion{number_of_inputs}_{number_of_outputs}",
|
||||
[
|
||||
base_class,
|
||||
num_inputs_to_input_trait_class[number_of_inputs],
|
||||
num_outputs_to_output_trait_class[number_of_outputs],
|
||||
],
|
||||
ap.SerializableAttrMap(),
|
||||
)
|
||||
|
||||
|
||||
# abstract_drr.register_drr_pass("matmul_binary_outs_fusion", nice=0)(get_mixin_class(MatmulEpilogueFusion, 3, 2))
|
||||
|
||||
|
||||
def register_class(base_class, max_num_inputs, max_num_outputs):
|
||||
def register_drr_class(num_inputs, num_outputs):
|
||||
abstract_drr.register_drr_pass(
|
||||
f"matmul_binary_in{num_inputs}_out{num_outputs}_fusion", nice=0
|
||||
)(get_mixin_class(base_class, num_inputs, num_outputs))
|
||||
|
||||
def register_num_inputs_drr_classes(num_inputs):
|
||||
def register_num_outputs_drr_classes(num_outputs):
|
||||
return register_drr_class(num_inputs + 2, num_outputs + 1)
|
||||
|
||||
ap.map(register_num_outputs_drr_classes, range(max_num_outputs))
|
||||
|
||||
ap.map(register_num_inputs_drr_classes, range(max_num_inputs))
|
||||
|
||||
|
||||
register_class(
|
||||
base_class=MatmulEpilogueFusion, max_num_inputs=10, max_num_outputs=10
|
||||
)
|
||||
@@ -0,0 +1,363 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
import compile_command_util
|
||||
import kernel_arg_translator_util
|
||||
import low_level_ir_code_gen_ctx_util
|
||||
|
||||
|
||||
def make_kernel_arg_translator():
|
||||
return kernel_arg_translator_util.KernelArgTranslator(
|
||||
param_struct_name="args"
|
||||
)
|
||||
|
||||
|
||||
def get_anchor_iter_var_names():
|
||||
return ["coord.batch", "coord.row", "coord.column"]
|
||||
|
||||
|
||||
class MatmulVariadicTemplate:
|
||||
def __init__(
|
||||
self,
|
||||
program_translator,
|
||||
mut_kernel_arg_id_registry,
|
||||
):
|
||||
self.program_translator = program_translator
|
||||
self.mut_kernel_arg_id_registry = mut_kernel_arg_id_registry
|
||||
self.kernel_arg_translator = make_kernel_arg_translator()
|
||||
self.dtype2type_name = ap.OrderedDict(
|
||||
[
|
||||
[ap.PointerType.const_float_ptr, "const float*"],
|
||||
[ap.PointerType.const_float16_ptr, "const half*"],
|
||||
[ap.PointerType.const_bfloat16_ptr, "const bfloat16*"],
|
||||
[ap.PointerType.float_ptr, "float*"],
|
||||
[ap.PointerType.float16_ptr, "half*"],
|
||||
[ap.PointerType.bfloat16_ptr, "bfloat16*"],
|
||||
[ap.DataType.float, "float"],
|
||||
[ap.DataType.float16, "half"],
|
||||
[ap.DataType.bfloat16, "bfloat16"],
|
||||
[ap.DataType.int64_t, "int64_t"],
|
||||
]
|
||||
)
|
||||
self.input_dim_karg_to_shape_access = ap.MutableOrderedDict()
|
||||
self.kernel_name = "MatmulVariadicKernel"
|
||||
self.library_name = "matmul_variadic_kernel"
|
||||
|
||||
def _register_name(self, pair):
|
||||
registry = self.mut_kernel_arg_id_registry
|
||||
registry.get_or_create_kernel_arg_id_manul_var_name(
|
||||
kernel_arg_id=pair[0], cpp_var_name=pair[1]
|
||||
)
|
||||
|
||||
def compile(
|
||||
self,
|
||||
input0_karg,
|
||||
input1_karg,
|
||||
output_karg,
|
||||
input0_shape_kargs,
|
||||
input1_shape_kargs,
|
||||
):
|
||||
kargs_name_pair_list = [
|
||||
[input0_karg, "input0"],
|
||||
[input1_karg, "input1"],
|
||||
[output_karg, "output"],
|
||||
*ap.map(
|
||||
lambda i: [input0_shape_kargs[i], f"input0_dim{i}"],
|
||||
range(len(input0_shape_kargs)),
|
||||
),
|
||||
*ap.map(
|
||||
lambda i: [input1_shape_kargs[i], f"input1_dim{i}"],
|
||||
range(len(input1_shape_kargs)),
|
||||
),
|
||||
]
|
||||
|
||||
ap.map(self._register_name, kargs_name_pair_list)
|
||||
mut_lir_code_gen_ctx = (
|
||||
low_level_ir_code_gen_ctx_util.CudaLikeIrCodeGenCtx(
|
||||
compute_dtype=ap.DataType.float
|
||||
)
|
||||
)
|
||||
self.program_translator.translate(
|
||||
mut_kernel_arg_id_registry=self.mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx=mut_lir_code_gen_ctx,
|
||||
)
|
||||
trivial_code_str = mut_lir_code_gen_ctx.get_stmts_joined_str(
|
||||
indent=" "
|
||||
)
|
||||
|
||||
project_module = self.make_project(
|
||||
trivial_code_str,
|
||||
input0_karg,
|
||||
input1_karg,
|
||||
output_karg,
|
||||
input0_shape_kargs,
|
||||
input1_shape_kargs,
|
||||
)
|
||||
return CodeGenResult( # noqa: F821
|
||||
module=project_module,
|
||||
kernel_dispatch_func=KernelDispatch,
|
||||
kernel_dispatch_const_data=ap.SerializableAttrMap(
|
||||
kernel_args_getters=self.get_kernel_arg_runtime_getters()
|
||||
),
|
||||
)
|
||||
|
||||
def get_kernel_arg_runtime_getters(self):
|
||||
all_kernel_arg_id_and_unique_names = self.mut_kernel_arg_id_registry.all_kernel_arg_id2unique_name.items()
|
||||
return ap.map(
|
||||
lambda pair: pair[0].runtime_getter,
|
||||
all_kernel_arg_id_and_unique_names,
|
||||
)
|
||||
|
||||
def get_kernel_arg_types(self):
|
||||
all_kernel_arg_id_and_unique_names = self.mut_kernel_arg_id_registry.all_kernel_arg_id2unique_name.items()
|
||||
return ap.map(
|
||||
lambda pair: pair[0].type, all_kernel_arg_id_and_unique_names
|
||||
)
|
||||
|
||||
def get_kernel_arg_id_var_name(self, kernel_arg_id):
|
||||
all_kernel_arg_id2unique_name = (
|
||||
self.mut_kernel_arg_id_registry.all_kernel_arg_id2unique_name
|
||||
)
|
||||
return all_kernel_arg_id2unique_name[kernel_arg_id]
|
||||
|
||||
def get_kernel_arg_list_str(self, for_declare):
|
||||
def declare_epilogue_arguments_field(pair):
|
||||
kernel_arg_id = pair[0]
|
||||
var_name = pair[1]
|
||||
field_name = self.kernel_arg_translator.get_param_struct_field_name(
|
||||
var_name
|
||||
)
|
||||
dtype = kernel_arg_id.type
|
||||
type_name = self.dtype2type_name[dtype]
|
||||
return (
|
||||
f"{type_name} {field_name}" if for_declare else f"{field_name}"
|
||||
)
|
||||
|
||||
all_kernel_arg_id_and_names = self.mut_kernel_arg_id_registry.all_kernel_arg_id2unique_name.items()
|
||||
return ", ".join(
|
||||
ap.map(
|
||||
declare_epilogue_arguments_field, all_kernel_arg_id_and_names
|
||||
)
|
||||
)
|
||||
|
||||
def get_epilogue_arguments_fields_str(self, indent):
|
||||
def declare_epilogue_arguments_field(pair):
|
||||
kernel_arg_id = pair[0]
|
||||
var_name = pair[1]
|
||||
field_name = self.kernel_arg_translator.get_param_struct_field_name(
|
||||
var_name
|
||||
)
|
||||
dtype = kernel_arg_id.type
|
||||
type_name = self.dtype2type_name[dtype]
|
||||
return f"{type_name} {field_name};"
|
||||
|
||||
generated_kernel_arg_id_and_names = self.mut_kernel_arg_id_registry.generated_kernel_arg_id2unique_name.items()
|
||||
return f"\n{indent}".join(
|
||||
ap.map(
|
||||
declare_epilogue_arguments_field,
|
||||
generated_kernel_arg_id_and_names,
|
||||
)
|
||||
)
|
||||
|
||||
def get_epilogue_arguments_init_str(self, param_obj_name, indent):
|
||||
def declare_epilogue_arguments_assign(pair):
|
||||
kernel_arg_id = pair[0]
|
||||
var_name = pair[1]
|
||||
field_name = self.kernel_arg_translator.get_param_struct_field_name(
|
||||
var_name
|
||||
)
|
||||
return f"{param_obj_name}.{field_name} = {var_name};"
|
||||
|
||||
generated_kernel_arg_id_and_names = self.mut_kernel_arg_id_registry.generated_kernel_arg_id2unique_name.items()
|
||||
return f"\n{indent}".join(
|
||||
ap.map(
|
||||
declare_epilogue_arguments_assign,
|
||||
generated_kernel_arg_id_and_names,
|
||||
)
|
||||
)
|
||||
|
||||
def get_params_input_shape_init_str(
|
||||
self, input_name, input_shape_kargs, indent
|
||||
):
|
||||
def init_input_shape_with_args(i):
|
||||
def get_creator():
|
||||
return f"{input_name}_shape[{i}]"
|
||||
|
||||
karg_var_name = self.get_kernel_arg_id_var_name(
|
||||
input_shape_kargs[i]
|
||||
)
|
||||
self.input_dim_karg_to_shape_access.get_or_create(
|
||||
karg_var_name, get_creator
|
||||
)
|
||||
return f"{indent}{input_name}_shape[{i}] = {karg_var_name};"
|
||||
|
||||
shape_vector_init_str = (
|
||||
f"{input_name}_shape.resize({len(input_shape_kargs)});\n"
|
||||
)
|
||||
return shape_vector_init_str + "\n".join(
|
||||
ap.map(init_input_shape_with_args, range(len(input_shape_kargs)))
|
||||
)
|
||||
|
||||
def make_project(
|
||||
self,
|
||||
trivial_code_str,
|
||||
input0_karg,
|
||||
input1_karg,
|
||||
output_karg,
|
||||
input0_shape_kargs,
|
||||
input1_shape_kargs,
|
||||
):
|
||||
code_template = """
|
||||
// auto generated codes
|
||||
#include "matmul.h"
|
||||
#include <vector>
|
||||
|
||||
namespace ap {
|
||||
|
||||
template <typename T>
|
||||
struct VariadicEpilogueFunctor {
|
||||
struct Arguments {
|
||||
${AP_EPILOGUE_ARGUMENTS_FIELDS}
|
||||
};
|
||||
|
||||
// Note: need to support vectorized operation
|
||||
__forceinline__ __host__ __device__
|
||||
T operator()(T x, const Arguments& args, const MatrixCoord& coord) const {
|
||||
T out;
|
||||
${AP_EPILOGUE_COMPUTATION_STATEMENTS}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <int TuningConfigId>
|
||||
static void RunMatmulWithVariadicKernel(const GemmEpilogueParams ¶ms, ${AP_KERNEL_ARGS_DECLARE}) {
|
||||
using ElementT = ${output_dtype};
|
||||
using ElementComputeT = float;
|
||||
|
||||
typename VariadicEpilogueFunctor<ElementComputeT>::Arguments epilogue_args;
|
||||
|
||||
${AP_EPILOGUE_ARGUMENTS_INIT}
|
||||
|
||||
constexpr int AlignA = Alignment<ElementT, ${k_value}>::kValue;
|
||||
constexpr int AlignB = Alignment<ElementT, ${n_value}>::kValue;
|
||||
|
||||
MatmulAddVariadic<ElementT, ElementComputeT, VariadicEpilogueFunctor,
|
||||
AlignA, AlignB, TuningConfigId>(params, epilogue_args);
|
||||
}
|
||||
|
||||
} // namespace ap
|
||||
|
||||
extern "C" {
|
||||
|
||||
void ${kernel_name}(void* stream_ptr, ${AP_KERNEL_ARGS_DECLARE}) {
|
||||
std::vector<int64_t> ${input0}_shape;
|
||||
${AP_PARAMS_INPUT0_SHAPE_INIT}
|
||||
|
||||
std::vector<int64_t> ${input1}_shape;
|
||||
${AP_PARAMS_INPUT1_SHAPE_INIT}
|
||||
|
||||
ap::GemmEpilogueParams params(
|
||||
stream_ptr, ${input0}, ${input1}, nullptr, ${output}, ${input0}_shape, ${input1}_shape, std::vector<int64_t>{});
|
||||
|
||||
#if AP_ENABLE_AUTOTUNE
|
||||
AP_AUTOTUNE_${output_dtype}(ap::RunMatmulWithVariadicKernel, stream_ptr, params, ${AP_KERNEL_ARGS_CALL});
|
||||
#else
|
||||
ap::RunMatmulWithVariadicKernel<ap::DefaultConfig::kConfigId>(params, ${AP_KERNEL_ARGS_CALL});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
output_dtype = self.dtype2type_name[output_karg.type.data_type]
|
||||
code = (
|
||||
code_template.replace(
|
||||
"${AP_EPILOGUE_COMPUTATION_STATEMENTS}", trivial_code_str
|
||||
)
|
||||
.replace(
|
||||
"${AP_KERNEL_ARGS_DECLARE}",
|
||||
self.get_kernel_arg_list_str(for_declare=True),
|
||||
)
|
||||
.replace(
|
||||
"${AP_KERNEL_ARGS_CALL}",
|
||||
self.get_kernel_arg_list_str(for_declare=False),
|
||||
)
|
||||
.replace(
|
||||
"${AP_PARAMS_INPUT0_SHAPE_INIT}",
|
||||
self.get_params_input_shape_init_str(
|
||||
"${input0}", input0_shape_kargs, indent=" "
|
||||
),
|
||||
)
|
||||
.replace(
|
||||
"${AP_PARAMS_INPUT1_SHAPE_INIT}",
|
||||
self.get_params_input_shape_init_str(
|
||||
"${input1}", input1_shape_kargs, indent=" "
|
||||
),
|
||||
)
|
||||
.replace(
|
||||
"${AP_EPILOGUE_ARGUMENTS_FIELDS}",
|
||||
self.get_epilogue_arguments_fields_str(indent=" "),
|
||||
)
|
||||
.replace(
|
||||
"${AP_EPILOGUE_ARGUMENTS_INIT}",
|
||||
self.get_epilogue_arguments_init_str(
|
||||
"epilogue_args", indent=" "
|
||||
),
|
||||
)
|
||||
.replace("${kernel_name}", self.kernel_name)
|
||||
.replace("${input0}", self.get_kernel_arg_id_var_name(input0_karg))
|
||||
.replace("${input1}", self.get_kernel_arg_id_var_name(input1_karg))
|
||||
.replace("${output}", self.get_kernel_arg_id_var_name(output_karg))
|
||||
.replace("${output_dtype}", output_dtype)
|
||||
.replace("${k_value}", f"{input0_shape_kargs[-1].value}")
|
||||
.replace("${n_value}", f"{input1_shape_kargs[-1].value}")
|
||||
)
|
||||
|
||||
dir_name = ap.dirname(__file__)
|
||||
compile_command_generator = (
|
||||
compile_command_util.CompileCommandGenerator()
|
||||
)
|
||||
compile_cmd = compile_command_generator(
|
||||
"matmul", dir_name, self.library_name
|
||||
)
|
||||
file_ext = compile_command_generator.file_ext
|
||||
|
||||
return CodeModule( # noqa: F821
|
||||
FuncDeclare( # noqa: F821
|
||||
ap.DataType.void,
|
||||
self.kernel_name,
|
||||
[ap.PointerType.void_ptr, *self.get_kernel_arg_types()],
|
||||
),
|
||||
Project( # noqa: F821
|
||||
nested_files=Project.Directory( # noqa: F821
|
||||
[
|
||||
f"{self.library_name}.{file_ext}",
|
||||
Project.FileContent(code), # noqa: F821
|
||||
],
|
||||
["make.sh", Project.FileContent(compile_cmd)], # noqa: F821
|
||||
),
|
||||
compile_cmd="sh make.sh",
|
||||
so_relative_path=f"lib{self.library_name}.so",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def KernelDispatch(ctx):
|
||||
import ap
|
||||
|
||||
so_func = ctx.get_so_function("MatmulVariadicKernel")
|
||||
stream_ptr = ctx.device_ctx.get_stream_addr_as_void_ptr()
|
||||
getters = ctx.kernel_dispatch_const_data.kernel_args_getters
|
||||
args = [stream_ptr, *ap.map(lambda getter: getter(ctx), getters)]
|
||||
ap.apply(so_func, args)
|
||||
@@ -0,0 +1,836 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
import code_gen_value_util
|
||||
|
||||
|
||||
class ApOpLoadFromRegisterCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
out = self.get_out_cg_val(0)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
register_var_name_attr = self.op_property.attributes.register_var_name
|
||||
register_var_name = register_var_name_attr.match(a_str=lambda x: x)
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type, register_var_name
|
||||
)
|
||||
|
||||
|
||||
class ApOpLoadFromGlobalCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
index_func_unique_id_attr = (
|
||||
self.op_property.attributes.index_func_unique_id
|
||||
)
|
||||
index_func_unique_id = index_func_unique_id_attr.match(
|
||||
a_str=lambda x: x
|
||||
)
|
||||
offset_var_name = self.index_program_translator_map.get_offset_var_name(
|
||||
index_func_unique_id=index_func_unique_id,
|
||||
mut_kernel_arg_id_registry=mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx=mut_lir_code_gen_ctx,
|
||||
)
|
||||
data_op_name = inputs[0].var_name
|
||||
arg_name = mut_kernel_arg_id_registry.get_in_tensor_data_ptr_var_name(
|
||||
data_op_name
|
||||
)
|
||||
ptr_var_name = self.kernel_arg_translator.get_use_name(arg_name)
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"{ptr_var_name}[{offset_var_name}]")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class ApOpStoreToRegisterCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
mut_lir_code_gen_ctx.stmts.append(
|
||||
f"{self.get_out_var_name()} = {inputs[0].var_name};"
|
||||
)
|
||||
return []
|
||||
|
||||
def get_out_var_name(self):
|
||||
register_var_name_attr = self.op_property.attributes.register_var_name
|
||||
return register_var_name_attr.match(a_str=lambda x: x)
|
||||
|
||||
|
||||
class ApOpStoreToGlobalCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
self.ptr_type2data_type = ap.OrderedDict(
|
||||
[
|
||||
[ap.PointerType.const_float_ptr, "const float"],
|
||||
[ap.PointerType.const_float16_ptr, "const half"],
|
||||
[ap.PointerType.float_ptr, "float"],
|
||||
[ap.PointerType.float16_ptr, "half"],
|
||||
]
|
||||
)
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
index_func_unique_id_attr = (
|
||||
self.op_property.attributes.index_func_unique_id
|
||||
)
|
||||
index_func_unique_id = index_func_unique_id_attr.match(
|
||||
a_str=lambda x: x
|
||||
)
|
||||
offset_var_name = self.index_program_translator_map.get_offset_var_name(
|
||||
index_func_unique_id=index_func_unique_id,
|
||||
mut_kernel_arg_id_registry=mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx=mut_lir_code_gen_ctx,
|
||||
)
|
||||
arg_name = mut_kernel_arg_id_registry.get_out_tensor_data_ptr_var_name(
|
||||
index_func_unique_id
|
||||
)
|
||||
glb_data_type = self.get_glb_type(
|
||||
mut_kernel_arg_id_registry, index_func_unique_id
|
||||
)
|
||||
ptr_var_name = self.kernel_arg_translator.get_use_name(arg_name)
|
||||
mut_lir_code_gen_ctx.store(
|
||||
glb_data_type, ptr_var_name, offset_var_name, inputs[1].var_name
|
||||
)
|
||||
return []
|
||||
|
||||
def get_glb_type(self, mut_kernel_arg_id_registry, index_func_unique_id):
|
||||
arg_name = mut_kernel_arg_id_registry.get_out_tensor_data_ptr_var_name(
|
||||
index_func_unique_id
|
||||
)
|
||||
kernel_arg_name2ids = ap.OrderedDict(
|
||||
ap.map(
|
||||
lambda item: [item[1], item[0]],
|
||||
mut_kernel_arg_id_registry.generated_kernel_arg_id2unique_name.items(),
|
||||
)
|
||||
)
|
||||
kernel_arg_id = kernel_arg_name2ids[arg_name]
|
||||
return self.ptr_type2data_type[kernel_arg_id.type]
|
||||
|
||||
def get_out_var_name(self):
|
||||
register_var_name_attr = self.op_property.attributes.register_var_name
|
||||
return register_var_name_attr.match(a_str=lambda x: x)
|
||||
|
||||
|
||||
class PdOpDataCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
out = self.get_out_cg_val(0)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
name = self.op_property.attributes.name.match(a_str=lambda x: x)
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type, name
|
||||
)
|
||||
|
||||
|
||||
class PdOpFullCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
value = self.op_property.attributes.value.match(a_f64=lambda x: x)
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"{value}")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpCastCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
self.dtype2type_name = ap.OrderedDict(
|
||||
[
|
||||
[ap.DataType.float, "float"],
|
||||
[ap.DataType.float16, "half"],
|
||||
[ap.DataType.bfloat16, "nv_bfloat16"],
|
||||
[ap.DataType.int32, "int"],
|
||||
[ap.DataType.int64, "int64_t"],
|
||||
]
|
||||
)
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
dtype = self.op_property.attributes.dtype.match(a_dtype=lambda x: x)
|
||||
dtype_name = self.dtype2type_name[dtype]
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(
|
||||
out, f"static_cast<{dtype_name}>({inputs[0].var_name})"
|
||||
)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpExpCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"expf({inputs[0].var_name})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpReluCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(
|
||||
out, f"({inputs[0].var_name} > 0 ? {inputs[0].var_name} : 0) "
|
||||
)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpErfCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
var_name = inputs[0].var_name
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"erf({var_name})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpElementwisePowCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
exponent = inputs[1].var_name
|
||||
var_name = inputs[0].var_name
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"ComputePow({var_name}, {exponent})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpSinCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
var_name = inputs[0].var_name
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"sin({var_name})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpTanhCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
var_name = inputs[0].var_name
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"tanh({var_name})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpFloorCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
var_name = inputs[0].var_name
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"floor({var_name})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class CinnOpScaleCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
scale = self.op_property.attributes.scale.match(a_f32=lambda x: x)
|
||||
bias = self.op_property.attributes.bias.match(a_f32=lambda x: x)
|
||||
bias_after_scale = self.op_property.attributes.bias_after_scale.match(
|
||||
a_bool=lambda x: x
|
||||
)
|
||||
in_name = inputs[0].var_name
|
||||
true_str = f"{scale} * {in_name} + {bias}"
|
||||
false_str = f"{scale} * ({in_name} + {bias})"
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(
|
||||
out, true_str if bias_after_scale else false_str
|
||||
)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpSubtractCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
a = inputs[0]
|
||||
b = inputs[1]
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"({a.var_name} - {b.var_name})")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpAddCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
a = inputs[0]
|
||||
b = inputs[1]
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"{a.var_name} + {b.var_name}")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpMultiplyCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
a = inputs[0]
|
||||
b = inputs[1]
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"{a.var_name} * {b.var_name}")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpDivideCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
a = inputs[0]
|
||||
b = inputs[1]
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(out, f"{a.var_name} / {b.var_name}")
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class PdOpMaximumCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
a = inputs[0]
|
||||
b = inputs[1]
|
||||
out = self.get_out_cg_val(0)
|
||||
mut_lir_code_gen_ctx.let(
|
||||
out,
|
||||
f"(({a.var_name} >= {b.var_name}) ? ({a.var_name}) : ({b.var_name}))",
|
||||
)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class CinnOpYieldStoreCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
return inputs
|
||||
|
||||
|
||||
class CinnOpBroadcastCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
return inputs
|
||||
|
||||
|
||||
class CinnOpExpandCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
return [inputs[0]]
|
||||
|
||||
|
||||
class CinnOpGenerateShapeCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
out = self.get_out_cg_val(0)
|
||||
return [out]
|
||||
|
||||
def get_out_cg_val(self, i):
|
||||
return code_gen_value_util.CodeGenValue(
|
||||
self.output_properties[i].type,
|
||||
f"op{self.op_property.op_index}_out{i}",
|
||||
)
|
||||
|
||||
|
||||
class OpComputeTranslatorFactory:
|
||||
def __init__(self):
|
||||
self.op_name2class = ap.OrderedDict(
|
||||
[
|
||||
["ap_op.load_from_register", ApOpLoadFromRegisterCodeGen],
|
||||
["ap_op.load_from_global", ApOpLoadFromGlobalCodeGen],
|
||||
["ap_op.store_to_register", ApOpStoreToRegisterCodeGen],
|
||||
["ap_op.store_to_global", ApOpStoreToGlobalCodeGen],
|
||||
["pd_op.data", PdOpDataCodeGen],
|
||||
["pd_op.full", PdOpFullCodeGen],
|
||||
["pd_op.cast", PdOpCastCodeGen],
|
||||
["pd_op.exp", PdOpExpCodeGen],
|
||||
["pd_op.relu", PdOpReluCodeGen],
|
||||
["pd_op.sin", PdOpSinCodeGen],
|
||||
["pd_op.tanh", PdOpTanhCodeGen],
|
||||
["pd_op.floor", PdOpFloorCodeGen],
|
||||
["pd_op.erf", PdOpErfCodeGen],
|
||||
["pd_op.elementwise_pow", PdOpElementwisePowCodeGen],
|
||||
["cinn_op.scale", CinnOpScaleCodeGen],
|
||||
["pd_op.subtract", PdOpSubtractCodeGen],
|
||||
["pd_op.add", PdOpAddCodeGen],
|
||||
["pd_op.multiply", PdOpMultiplyCodeGen],
|
||||
["pd_op.divide", PdOpDivideCodeGen],
|
||||
["pd_op.maximum", PdOpMaximumCodeGen],
|
||||
["cinn_op.yield_store", CinnOpYieldStoreCodeGen],
|
||||
["cinn_op.broadcast", CinnOpBroadcastCodeGen],
|
||||
["pd_op.expand", CinnOpExpandCodeGen],
|
||||
["cinn_op.generate_shape", CinnOpGenerateShapeCodeGen],
|
||||
]
|
||||
)
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
):
|
||||
cls = self._get_class(op_property.op_name)
|
||||
return cls(
|
||||
op_property=op_property,
|
||||
input_properties=input_properties,
|
||||
output_properties=output_properties,
|
||||
kernel_arg_translator=kernel_arg_translator,
|
||||
index_program_translator_map=index_program_translator_map,
|
||||
)
|
||||
|
||||
def _get_class(self, op_name):
|
||||
return self.op_name2class[op_name]
|
||||
@@ -0,0 +1,222 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import access_topo_drr
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_cast", tag="default")
|
||||
class PdOpCastAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.cast_op = o.ap_native_op("pd_op.cast")
|
||||
o.cast_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_tanh", tag="default")
|
||||
class PdOpTanhAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.tanh_op = o.ap_native_op("pd_op.tanh")
|
||||
o.tanh_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_floor", tag="default")
|
||||
class PdOpFloorAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.floor_op = o.ap_native_op("pd_op.floor")
|
||||
o.floor_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_erf", tag="default")
|
||||
class PdOpErfAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.erf_op = o.ap_native_op("pd_op.erf")
|
||||
o.erf_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_elementwise_pow", tag="default")
|
||||
class PdOpElementwisePowAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.source_op = o.ap_native_op("pd_op.elementwise_pow")
|
||||
o.source_op([t.input0, t.input1], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.add")
|
||||
o.result_op([t.input0, t.input1], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_exp", tag="default")
|
||||
class PdOpExpAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.exp_op = o.ap_native_op("pd_op.exp")
|
||||
o.exp_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("cinn_op_scale", tag="default")
|
||||
class CinnOpScaleAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.scale_op = o.ap_native_op("cinn_op.scale")
|
||||
o.scale_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_sin", tag="default")
|
||||
class PdOpSinAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.sin_op = o.ap_native_op("pd_op.sin")
|
||||
o.sin_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("cinn_op_yield_store", tag="default")
|
||||
class CinnOpYieldStoreAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.yield_op = o.ap_native_op("cinn_op.yield_store")
|
||||
o.yield_op([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_subtract", tag="default")
|
||||
class PdOpSubtractAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.source_op = o.ap_native_op("pd_op.subtract")
|
||||
o.source_op([t.input0, t.input1], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.add")
|
||||
o.result_op([t.input0, t.input1], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_divide", tag="default")
|
||||
class PdOpDivideAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.source_op = o.ap_native_op("pd_op.divide")
|
||||
o.source_op([t.input0, t.input1], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.add")
|
||||
o.result_op([t.input0, t.input1], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_multiply", tag="default")
|
||||
class PdOpMultiplyAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.source_op = o.ap_native_op("pd_op.multiply")
|
||||
o.source_op([t.input0, t.input1], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.add")
|
||||
o.result_op([t.input0, t.input1], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_maximum", tag="default")
|
||||
class PdOpMaximumAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.source_op = o.ap_native_op("pd_op.maximum")
|
||||
o.source_op([t.input0, t.input1], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.add")
|
||||
o.result_op([t.input0, t.input1], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_left_full_add", tag="default")
|
||||
class PdOpLeftFullAddAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.full_op = o.ap_native_op("pd_op.full")
|
||||
o.full_op([], [t.intermediate])
|
||||
o.source_op = o.ap_native_op("pd_op.add")
|
||||
o.source_op([t.intermediate, t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_right_full_add", tag="default")
|
||||
class PdOpRightFullAddAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.full_op = o.ap_native_op("pd_op.full")
|
||||
o.full_op([], [t.intermediate])
|
||||
o.source_op = o.ap_native_op("pd_op.add")
|
||||
o.source_op([t.input, t.intermediate], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass(
|
||||
"full_generate_shape_expand_left_add", tag="default"
|
||||
)
|
||||
class FullGenerateShapeExpandLeftAddAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.full = o.ap_native_op("pd_op.full")
|
||||
o.full([], [t.intermediate0])
|
||||
o.generate_shape = o.ap_native_op("cinn_op.generate_shape")
|
||||
o.generate_shape([t.input0], [t.intermediate1])
|
||||
o.expand = o.ap_native_op("pd_op.expand")
|
||||
o.expand([t.intermediate0, t.intermediate1], [t.expanded_input])
|
||||
o.add = o.ap_native_op("pd_op.add")
|
||||
o.add([t.expanded_input, t.input0], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.relu = o.ap_native_op("pd_op.relu")
|
||||
o.relu([t.input0], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass(
|
||||
"full_generate_shape_expand_right_add", tag="default"
|
||||
)
|
||||
class FullGenerateShapeExpandRightAddAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.full = o.ap_native_op("pd_op.full")
|
||||
o.full([], [t.intermediate0])
|
||||
o.generate_shape = o.ap_native_op("cinn_op.generate_shape")
|
||||
o.generate_shape([t.input0], [t.intermediate1])
|
||||
o.expand = o.ap_native_op("pd_op.expand")
|
||||
o.expand([t.intermediate0, t.intermediate1], [t.expanded_input])
|
||||
o.add = o.ap_native_op("pd_op.add")
|
||||
o.add([t.input0, t.expanded_input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.relu = o.ap_native_op("pd_op.relu")
|
||||
o.relu([t.input0], [t.output])
|
||||
@@ -0,0 +1,226 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
import index_code_gen_value_util
|
||||
|
||||
|
||||
class PdOpDataCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
index_program_id,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.index_program_id = index_program_id
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
return [
|
||||
index_code_gen_value_util.IndexCodeGenValue(
|
||||
self.anchor_iter_var_names
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class PdOpFullIntArrayCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
index_program_id,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.index_program_id = index_program_id
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
out = index_code_gen_value_util.IndexCodeGenValue(None)
|
||||
|
||||
def get_int64(attr):
|
||||
return attr.match(a_i64=lambda x: x)
|
||||
|
||||
def convert_list(lst):
|
||||
return ap.map(get_int64, lst)
|
||||
|
||||
out.const_data = self.op_property.attributes.value.match(
|
||||
a_array=convert_list
|
||||
)
|
||||
return [out]
|
||||
|
||||
|
||||
class PdOpSumCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
index_program_id,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.index_program_id = index_program_id
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
input_iter_var_names = inputs[0].iter_var_names
|
||||
reduced_axes_set = ap.OrderedDict(
|
||||
ap.map(lambda x: [int(x), True], inputs[1].const_data)
|
||||
)
|
||||
non_reduced_axes = ap.filter(
|
||||
lambda x: reduced_axes_set.contains(x) == False, # noqa: E712
|
||||
range(len(input_iter_var_names)),
|
||||
)
|
||||
output_iter_var_names = ap.map(
|
||||
lambda i: input_iter_var_names[i], non_reduced_axes
|
||||
)
|
||||
return [
|
||||
index_code_gen_value_util.IndexCodeGenValue(output_iter_var_names)
|
||||
]
|
||||
|
||||
|
||||
class CinnOpReshapeCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
index_program_id,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.index_program_id = index_program_id
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
symbolic_shape = self.input_properties[0].symbolic_shape
|
||||
|
||||
def get_or_create_dim_var_name(dim_expr):
|
||||
arg_var_name = mut_kernel_arg_id_registry.get_dim_expr_var_name(
|
||||
dim_expr
|
||||
)
|
||||
return self.kernel_arg_translator.get_use_name(arg_var_name)
|
||||
|
||||
def get_dim_var_name(i):
|
||||
dim_expr = symbolic_shape[i]
|
||||
return get_or_create_dim_var_name(dim_expr)
|
||||
|
||||
rank = len(symbolic_shape)
|
||||
stride_dims_list = ap.map(
|
||||
lambda num_dims: ap.map(
|
||||
lambda i: get_dim_var_name(num_dims + i + 1),
|
||||
range(rank - 1 - num_dims),
|
||||
),
|
||||
range(rank),
|
||||
)
|
||||
var_name_and_dims_list = ap.map(
|
||||
lambda pair: [pair[0], *pair[1]],
|
||||
zip(inputs[0].iter_var_names, stride_dims_list),
|
||||
)
|
||||
offset_expr = " + ".join(
|
||||
ap.map(lambda elts: " * ".join(elts), var_name_and_dims_list)
|
||||
)
|
||||
assert len(self.output_properties[0].symbolic_shape) == 1, (
|
||||
"len(self.output_properties[0]) should be 1"
|
||||
)
|
||||
return [
|
||||
index_code_gen_value_util.IndexCodeGenValue([f"({offset_expr})"])
|
||||
]
|
||||
|
||||
|
||||
class CfYieldCodeGen:
|
||||
def __init__(
|
||||
self,
|
||||
index_program_id,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
self.index_program_id = index_program_id
|
||||
self.op_property = op_property
|
||||
self.input_properties = input_properties
|
||||
self.output_properties = output_properties
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.anchor_iter_var_names = anchor_iter_var_names
|
||||
|
||||
def __call__(
|
||||
self, inputs, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
return []
|
||||
|
||||
|
||||
class OpIndexTranslatorFactory:
|
||||
def __init__(self):
|
||||
self.op_name2class = ap.OrderedDict(
|
||||
[
|
||||
["pd_op.data", PdOpDataCodeGen],
|
||||
["pd_op.full_int_array", PdOpFullIntArrayCodeGen],
|
||||
["pd_op.sum", PdOpSumCodeGen],
|
||||
["cinn_op.reshape", CinnOpReshapeCodeGen],
|
||||
["cf.yield", CfYieldCodeGen],
|
||||
]
|
||||
)
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
index_program_id,
|
||||
op_property,
|
||||
input_properties,
|
||||
output_properties,
|
||||
kernel_arg_translator,
|
||||
anchor_iter_var_names,
|
||||
):
|
||||
cls = self._get_class(op_property.op_name)
|
||||
return cls(
|
||||
index_program_id=index_program_id,
|
||||
op_property=op_property,
|
||||
input_properties=input_properties,
|
||||
output_properties=output_properties,
|
||||
kernel_arg_translator=kernel_arg_translator,
|
||||
anchor_iter_var_names=anchor_iter_var_names,
|
||||
)
|
||||
|
||||
def _get_class(self, op_name):
|
||||
return self.op_name2class[op_name]
|
||||
@@ -0,0 +1,81 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import ap
|
||||
|
||||
|
||||
class ProgramTranslator:
|
||||
def __init__(
|
||||
self,
|
||||
program_property,
|
||||
kernel_arg_translator,
|
||||
index_program_translator_map,
|
||||
op_translator_maker,
|
||||
):
|
||||
self.program_property = program_property
|
||||
self.kernel_arg_translator = kernel_arg_translator
|
||||
self.index_program_translator_map = index_program_translator_map
|
||||
self.op_translator_maker = op_translator_maker
|
||||
self.ir_value_index2translated_value = ap.MutableList()
|
||||
|
||||
def PushNone(x):
|
||||
self.ir_value_index2translated_value.append(None)
|
||||
|
||||
map(PushNone, self.program_property.values)
|
||||
|
||||
# mut_kernel_arg_id_registry: mutable KernelArgIdLazyContext
|
||||
# mut_lir_code_gen_ctx: mutable low level ir code generation context
|
||||
def translate(self, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx):
|
||||
def TranslateOp(op_property):
|
||||
self._translate_op(
|
||||
op_property, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
)
|
||||
|
||||
map(TranslateOp, self.program_property.ops)
|
||||
|
||||
def _translate_op(
|
||||
self, op_property, mut_kernel_arg_id_registry, mut_lir_code_gen_ctx
|
||||
):
|
||||
op_translator = self.op_translator_maker(
|
||||
op_property=op_property,
|
||||
input_properties=map(
|
||||
self._get_value_property, op_property.input_value_indexes
|
||||
),
|
||||
output_properties=map(
|
||||
self._get_value_property, op_property.output_value_indexes
|
||||
),
|
||||
kernel_arg_translator=self.kernel_arg_translator,
|
||||
index_program_translator_map=self.index_program_translator_map,
|
||||
)
|
||||
inputs = map(
|
||||
self._get_translated_value, op_property.input_value_indexes
|
||||
)
|
||||
outputs = op_translator(
|
||||
inputs,
|
||||
mut_kernel_arg_id_registry=mut_kernel_arg_id_registry,
|
||||
mut_lir_code_gen_ctx=mut_lir_code_gen_ctx,
|
||||
)
|
||||
map(
|
||||
self._set_translated_value,
|
||||
zip(op_property.output_value_indexes, outputs),
|
||||
)
|
||||
|
||||
def _get_value_property(self, i):
|
||||
return self.program_property.values[i]
|
||||
|
||||
def _get_translated_value(self, i):
|
||||
return self.ir_value_index2translated_value[i]
|
||||
|
||||
def _set_translated_value(self, pair):
|
||||
self.ir_value_index2translated_value[pair[0]] = pair[1]
|
||||
@@ -0,0 +1,489 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import access_topo_drr
|
||||
import ap
|
||||
import pir
|
||||
|
||||
|
||||
class FakeDataForYieldAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, fake_data_names):
|
||||
self.num_outputs = len(fake_data_names)
|
||||
self.fake_data_names = fake_data_names
|
||||
self.undefined_place = pir.a_place(pir.UndefinedPlace())
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.yield_op = o.ap_native_op("cf.yield")
|
||||
|
||||
def get_yield_input(i):
|
||||
return getattr(t, f"output{i}")
|
||||
|
||||
o.yield_op(ap.map(get_yield_input, range(self.num_outputs)), [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
self.result_pattern_data_op(o, t)
|
||||
self.result_pattern_up_spider(o, t)
|
||||
|
||||
def result_pattern_data_op(self, o, t):
|
||||
ap.map(
|
||||
lambda i: self.data_op_for_output(o, t, i), range(self.num_outputs)
|
||||
)
|
||||
|
||||
def data_op_for_output(self, o, t, i):
|
||||
t.declare_internal_native_ir_value(f"data_out{i}")
|
||||
data_op_unique_name = f"data_op_for_output{i}"
|
||||
setattr(o, data_op_unique_name, o.ap_native_op("pd_op.data"))
|
||||
data_op = getattr(o, data_op_unique_name)
|
||||
data_op.name = lambda o, t: self.get_name(o, t, i)
|
||||
data_op.shape = lambda o, t: self.get_shape(o, t, i)
|
||||
data_op.dtype = lambda o, t: self.get_dtype(o, t, i)
|
||||
data_op.place = lambda o, t: self.get_place(o, t, i)
|
||||
data_op([], [getattr(t, f"data_out{i}")])
|
||||
|
||||
def get_name(self, o, t, i):
|
||||
return pir.a_str(self.fake_data_names[i])
|
||||
|
||||
def get_shape(self, o, t, i):
|
||||
ir_tensor = getattr(t, f"output{i}")
|
||||
|
||||
def GetDims(dtype, dims, data_layout):
|
||||
return dims
|
||||
|
||||
return pir.a_intarray(ir_tensor.type.match(t_dtensor=GetDims))
|
||||
|
||||
def get_dtype(self, o, t, i):
|
||||
ir_tensor = getattr(t, f"output{i}")
|
||||
|
||||
def GetDtype(dtype, dims, data_layout):
|
||||
return dtype
|
||||
|
||||
return pir.a_dtype(ir_tensor.type.match(t_dtensor=GetDtype))
|
||||
|
||||
def get_place(self, o, t, i):
|
||||
return self.undefined_place
|
||||
|
||||
def result_pattern_up_spider(self, o, t):
|
||||
ap.map(
|
||||
lambda i: self.up_spider_for_output(o, t, i),
|
||||
range(self.num_outputs),
|
||||
)
|
||||
|
||||
def up_spider_for_output(self, o, t, i):
|
||||
t.declare_internal_native_ir_value(f"add_out{i}")
|
||||
up_spider_op_name = f"up_spider_op{i}"
|
||||
setattr(o, up_spider_op_name, o.ap_native_op("ap_op.up_spider"))
|
||||
getattr(o, up_spider_op_name)(
|
||||
[getattr(t, f"output{i}"), getattr(t, f"data_out{i}")], []
|
||||
)
|
||||
|
||||
|
||||
class FakeDataStoreToGlobalForYieldAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, fake_data_names):
|
||||
self.num_outputs = len(fake_data_names)
|
||||
self.fake_data_names = fake_data_names
|
||||
self.undefined_place = pir.a_place(pir.UndefinedPlace())
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.yield_op = o.ap_native_op("cf.yield")
|
||||
|
||||
def get_yield_input(i):
|
||||
return getattr(t, f"output{i}")
|
||||
|
||||
o.yield_op(ap.map(get_yield_input, range(self.num_outputs)), [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
self.result_pattern_data_op(o, t)
|
||||
self.result_pattern_store_to_global_op(o, t)
|
||||
|
||||
def result_pattern_data_op(self, o, t):
|
||||
ap.map(
|
||||
lambda i: self.data_op_for_output(o, t, i), range(self.num_outputs)
|
||||
)
|
||||
|
||||
def data_op_for_output(self, o, t, i):
|
||||
t.declare_internal_native_ir_value(f"data_out{i}")
|
||||
data_op_unique_name = f"data_op_for_output{i}"
|
||||
setattr(o, data_op_unique_name, o.ap_native_op("pd_op.data"))
|
||||
data_op = getattr(o, data_op_unique_name)
|
||||
data_op.name = lambda o, t: self.get_name(o, t, i)
|
||||
data_op.shape = lambda o, t: self.get_shape(o, t, i)
|
||||
data_op.dtype = lambda o, t: self.get_dtype(o, t, i)
|
||||
data_op.place = lambda o, t: self.get_place(o, t, i)
|
||||
data_op([], [getattr(t, f"data_out{i}")])
|
||||
|
||||
def get_name(self, o, t, i):
|
||||
return pir.a_str(self.fake_data_names[i])
|
||||
|
||||
def get_shape(self, o, t, i):
|
||||
ir_tensor = getattr(t, f"output{i}")
|
||||
|
||||
def GetDims(dtype, dims, data_layout):
|
||||
return dims
|
||||
|
||||
return pir.a_intarray(ir_tensor.type.match(t_dtensor=GetDims))
|
||||
|
||||
def get_dtype(self, o, t, i):
|
||||
ir_tensor = getattr(t, f"output{i}")
|
||||
|
||||
def GetDtype(dtype, dims, data_layout):
|
||||
return dtype
|
||||
|
||||
return pir.a_dtype(ir_tensor.type.match(t_dtensor=GetDtype))
|
||||
|
||||
def get_place(self, o, t, i):
|
||||
return self.undefined_place
|
||||
|
||||
def result_pattern_store_to_global_op(self, o, t):
|
||||
ap.map(
|
||||
lambda i: self.store_to_global_op_for_output(o, t, i),
|
||||
range(self.num_outputs),
|
||||
)
|
||||
|
||||
def store_to_global_op_for_output(self, o, t, i):
|
||||
store_to_global_op_name = f"store_to_global_op{i}"
|
||||
setattr(
|
||||
o, store_to_global_op_name, o.ap_native_op("ap_op.store_to_global")
|
||||
)
|
||||
store_to_global_op = getattr(o, store_to_global_op_name)
|
||||
store_to_global_op.index_func_unique_id = lambda o, t: pir.a_str(
|
||||
self.fake_data_names[i]
|
||||
)
|
||||
store_to_global_op(
|
||||
[getattr(t, f"data_out{i}"), getattr(t, f"output{i}")], []
|
||||
)
|
||||
|
||||
|
||||
class ConvertUpSpiderStoreDataOpToYieldOpPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.data_op = o.ap_native_op("pd_op.data")
|
||||
o.data_op([], [t.input1])
|
||||
o.load_from_global_op = o.ap_native_op("ap_op.load_from_global")
|
||||
o.load_from_global_op([t.input1], [t.tmp1])
|
||||
o.up_spider_op = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider_op([t.input0, t.tmp1], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.yield_op = o.ap_native_op("cf.yield")
|
||||
o.yield_op([t.input0], [])
|
||||
|
||||
|
||||
class ConvertDownSpiderStoreDataOpToYieldOpPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.data_mm_op = o.ap_native_op("pd_op.data")
|
||||
o.data_mm_op([], [t.input1])
|
||||
o.down_spider_op = o.ap_native_op("ap_op.down_spider")
|
||||
o.down_spider_op([t.input1], [t.tmp1])
|
||||
o.store_to_global = o.ap_native_op("ap_op.store_to_global")
|
||||
o.store_to_global([t.input0, t.tmp1], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.yield_op = o.ap_native_op("cf.yield")
|
||||
o.yield_op([t.input0], [])
|
||||
|
||||
|
||||
class InitDownSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, data_input_name):
|
||||
self.data_input_name_attr = pir.a_str(data_input_name)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.data_op = o.ap_native_op("pd_op.data")
|
||||
o.data_op([], [t.output])
|
||||
|
||||
def constraint(self, o, t):
|
||||
return o.data_op.name == self.data_input_name_attr
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
t.declare_internal_native_ir_value("input")
|
||||
o.new_data_op = o.ap_native_op("pd_op.data")
|
||||
o.new_data_op.name = lambda o, t: o.data_op.name
|
||||
o.new_data_op.shape = lambda o, t: o.data_op.shape
|
||||
o.new_data_op.dtype = lambda o, t: o.data_op.dtype
|
||||
o.new_data_op.place = lambda o, t: o.data_op.place
|
||||
o.new_data_op([], [t.input])
|
||||
o.down_spider = o.ap_native_op("ap_op.down_spider")
|
||||
o.down_spider([t.input], [t.output])
|
||||
|
||||
|
||||
class InitNaiveLoadFromGlobalAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, data_input_name):
|
||||
self.data_input_name_attr = pir.a_str(data_input_name)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.data_op = o.ap_native_op("pd_op.data")
|
||||
o.data_op([], [t.output])
|
||||
|
||||
def constraint(self, o, t):
|
||||
return o.data_op.name == self.data_input_name_attr
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
t.declare_internal_native_ir_value("input")
|
||||
o.new_data_op = o.ap_native_op("pd_op.data")
|
||||
o.new_data_op.name = lambda o, t: o.data_op.name
|
||||
o.new_data_op.shape = lambda o, t: o.data_op.shape
|
||||
o.new_data_op.dtype = lambda o, t: o.data_op.dtype
|
||||
o.new_data_op.place = lambda o, t: o.data_op.place
|
||||
o.new_data_op([], [t.input])
|
||||
o.load_from_global = o.ap_native_op("ap_op.load_from_global")
|
||||
o.load_from_global.index_func_unique_id = lambda o, t: (
|
||||
self.data_input_name_attr
|
||||
)
|
||||
o.load_from_global([t.input], [t.output])
|
||||
|
||||
|
||||
class ReplaceWithLoadFromRegisterPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, name, register_var_name):
|
||||
self.name = pir.a_str(name)
|
||||
self.register_var_name = pir.a_str(register_var_name)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.data_op = o.ap_native_op("pd_op.data")
|
||||
o.data_op([], [t.input])
|
||||
o.load_from_global = o.ap_native_op("ap_op.load_from_global")
|
||||
o.load_from_global.index_func_unique_id = self.name
|
||||
o.load_from_global([t.input], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.load_from_register = o.ap_native_op("ap_op.load_from_register")
|
||||
o.load_from_register.name = lambda o, t: self.name
|
||||
o.load_from_register.register_var_name = lambda o, t: (
|
||||
self.register_var_name
|
||||
)
|
||||
o.load_from_register.type = lambda o, t: pir.a_type(t.output.type)
|
||||
o.load_from_register.symbolic_shape_or_data = lambda o, t: pir.a_symbol(
|
||||
t.output.get_symbolic_shape_or_data()
|
||||
)
|
||||
o.load_from_register([], [t.output])
|
||||
|
||||
|
||||
class ReplaceWithStoreToRegisterPass(access_topo_drr.DrrPass):
|
||||
def __init__(self, name, register_var_name):
|
||||
self.name = pir.a_str(name)
|
||||
self.register_var_name = pir.a_str(register_var_name)
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.data_op = o.ap_native_op("pd_op.data")
|
||||
o.data_op([], [t.output])
|
||||
o.store_to_global_op = o.ap_native_op("ap_op.store_to_global")
|
||||
o.store_to_global_op.index_func_unique_id = self.name
|
||||
o.store_to_global_op([t.output, t.output_val], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.store_to_register_op = o.ap_native_op("ap_op.store_to_register")
|
||||
o.store_to_register_op.name = lambda o, t: self.name
|
||||
o.store_to_register_op.register_var_name = lambda o, t: (
|
||||
self.register_var_name
|
||||
)
|
||||
o.store_to_register_op([t.output_val], [])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("down_spider_relu", tag="default")
|
||||
class DownSpiderReluAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.spider0 = o.ap_native_op("ap_op.down_spider")
|
||||
o.spider0([t.input], [t.tmp])
|
||||
o.relu1 = o.ap_native_op("pd_op.relu")
|
||||
o.relu1([t.tmp], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.fustion_op = o.ap_native_op("ap_op.down_spider")
|
||||
o.fustion_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass(
|
||||
"down_spider_load_from_global", tag="default"
|
||||
)
|
||||
class DownSpiderLoadFromGlobalAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.spider0 = o.ap_native_op("ap_op.down_spider")
|
||||
o.spider0([t.input], [t.tmp])
|
||||
o.load_from_global_op = o.ap_native_op("ap_op.load_from_global")
|
||||
o.load_from_global_op([t.tmp], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.fustion_op = o.ap_native_op("ap_op.down_spider")
|
||||
o.fustion_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("down_spider_up_spider", tag="default")
|
||||
class DownSpiderUpSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.down_spider_op = o.ap_native_op("ap_op.down_spider")
|
||||
o.down_spider_op([t.input], [t.tmp0])
|
||||
o.up_spider_op = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider_op([t.tmp0, t.input], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("left_down_spider_add", tag="default")
|
||||
class LeftDownSpiderAddAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.spider = o.ap_native_op("ap_op.down_spider")
|
||||
o.spider([t.input0], [t.tmp0])
|
||||
o.add = o.ap_native_op("pd_op.add")
|
||||
o.add([t.tmp0, t.input1], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.down_spider = o.ap_native_op("ap_op.down_spider")
|
||||
o.down_spider([t.input0], [t.output])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.input1], [])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("right_down_spider_add", tag="default")
|
||||
class RightDownSpiderAddAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.spider = o.ap_native_op("ap_op.down_spider")
|
||||
o.spider([t.input0], [t.tmp0])
|
||||
o.add = o.ap_native_op("pd_op.add")
|
||||
o.add([t.input1, t.tmp0], [t.output])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.down_spider = o.ap_native_op("ap_op.down_spider")
|
||||
o.down_spider([t.input0], [t.output])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.input1], [])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("expand_up_spider", tag="default")
|
||||
class ExpandUpSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.expand = o.ap_native_op("pd_op.expand")
|
||||
o.expand([t.input1, t.input2], [t.expanded_input])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.expanded_input], [])
|
||||
|
||||
def constraint(self, o, t):
|
||||
input_shape = t.input1.symbolic_shape_to_list()
|
||||
output_shape = t.expanded_input.symbolic_shape_to_list()
|
||||
rank_diff = len(output_shape) - len(input_shape)
|
||||
return rank_diff > 0
|
||||
|
||||
# TODO: Get Inner Expanded Axes
|
||||
def GetInnerExpanded_axes(i):
|
||||
if input_shape[i] == output_shape[i + rank_diff]:
|
||||
return []
|
||||
else:
|
||||
return [i]
|
||||
|
||||
input_rank = len(t.input1.symbolic_shape_to_list())
|
||||
inner_expanded_axes = ap.flat_map(
|
||||
GetInnerExpanded_axes, range(input_rank)
|
||||
)
|
||||
return rank_diff > 0 and len(inner_expanded_axes) == 0
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
t.declare_internal_native_ir_value("reduced_input")
|
||||
o.sum = o.ap_native_op("pd_op.sum")
|
||||
o.sum.axis = self.get_axis
|
||||
o.sum.keepdim = self.get_keepdim
|
||||
o.sum([t.input0], [t.reduced_input])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.reduced_input, t.input1], [])
|
||||
|
||||
def get_keepdim(self, o, t):
|
||||
return pir.a_bool(False)
|
||||
|
||||
def get_axis(self, o, t):
|
||||
input_rank = len(t.input1.symbolic_shape_to_list())
|
||||
output_rank = len(t.expanded_input.symbolic_shape_to_list())
|
||||
axes = range(output_rank - input_rank)
|
||||
return pir.a_intarray(axes)
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("cinn_broadcast_up_spider", tag="default")
|
||||
class CinnBroadcastUpSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.broadcast_op = o.ap_native_op("cinn_op.broadcast")
|
||||
o.broadcast_op([t.input1], [t.expanded_input])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.expanded_input], [])
|
||||
|
||||
def constraint(self, o, t):
|
||||
input_shape = t.input1.symbolic_shape_to_list()
|
||||
output_shape = t.expanded_input.symbolic_shape_to_list()
|
||||
rank_diff = len(output_shape) - len(input_shape)
|
||||
return rank_diff > 0
|
||||
|
||||
# TODO: Get Inner Expanded Axes
|
||||
def GetInnerExpanded_axes(i):
|
||||
if input_shape[i] == output_shape[i + rank_diff]:
|
||||
return []
|
||||
else:
|
||||
return [i]
|
||||
|
||||
input_rank = len(t.input1.symbolic_shape_to_list())
|
||||
inner_expanded_axes = ap.flat_map(
|
||||
GetInnerExpanded_axes, range(input_rank)
|
||||
)
|
||||
return rank_diff > 0 and len(inner_expanded_axes) == 0
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
t.declare_internal_native_ir_value("reduced_input")
|
||||
o.sum = o.ap_native_op("pd_op.sum")
|
||||
o.sum.axis = self.get_axis
|
||||
o.sum.keepdim = self.get_keepdim
|
||||
o.sum([t.input0], [t.reduced_input])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.reduced_input, t.input1], [])
|
||||
|
||||
def get_keepdim(self, o, t):
|
||||
return pir.a_bool(False)
|
||||
|
||||
def get_axis(self, o, t):
|
||||
input_rank = len(t.input1.symbolic_shape_to_list())
|
||||
output_rank = len(t.expanded_input.symbolic_shape_to_list())
|
||||
axes = range(output_rank - input_rank)
|
||||
return pir.a_intarray(axes)
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("right_down_spider_up_spider", tag="default")
|
||||
class RightDownSpiderUpSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.expand = o.ap_native_op("ap_op.down_spider")
|
||||
o.expand([t.input1], [t.output1])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.output1], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.input1], [])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("left_down_spider_up_spider", tag="default")
|
||||
class LeftDownSpiderUpSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.expand = o.ap_native_op("ap_op.down_spider")
|
||||
o.expand([t.input0], [t.output0])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.output0, t.input1], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.input1], [])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass(
|
||||
"triangle_left_down_spider_up_spider", tag="default"
|
||||
)
|
||||
class TriangleLeftDownSpiderUpSpiderAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def source_pattern(self, o, t):
|
||||
o.expand = o.ap_native_op("ap_op.down_spider")
|
||||
o.expand([t.input0], [t.output0])
|
||||
o.up_spider = o.ap_native_op("ap_op.up_spider")
|
||||
o.up_spider([t.input0, t.output0], [])
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
pass
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import access_topo_drr
|
||||
import ap
|
||||
import pir
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_static_relu", tag="umprime")
|
||||
class PdOpReluAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def __init__(self):
|
||||
self.zero = pir.a_f64(ap.DataValue.float64("0"))
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.full_op = o.ap_native_op("pd_op.full")
|
||||
o.full_op([], [t.intermediate])
|
||||
o.maximum_op = o.ap_native_op("pd_op.maximum")
|
||||
o.maximum_op([t.input, t.intermediate], [t.output])
|
||||
|
||||
def constraint(self, o, t):
|
||||
return o.full_op.value == self.zero
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input], [t.output])
|
||||
|
||||
|
||||
@access_topo_drr.register_drr_pass("pd_op_dynamic_relu", tag="umprime")
|
||||
class PdOpDynReluAccessTopoPass(access_topo_drr.DrrPass):
|
||||
def __init__(self):
|
||||
self.zero = pir.a_f64(ap.DataValue.float64("0"))
|
||||
|
||||
def source_pattern(self, o, t):
|
||||
o.full_op = o.ap_native_op("pd_op.full")
|
||||
o.full_op([], [t.intermediate0])
|
||||
o.generate_shape_op = o.ap_native_op("cinn_op.generate_shape")
|
||||
o.generate_shape_op([t.input0], [t.intermediate1])
|
||||
o.expand_op = o.ap_native_op("pd_op.expand")
|
||||
o.expand_op([t.intermediate0, t.intermediate1], [t.intermediate2])
|
||||
o.maximum_op = o.ap_native_op("pd_op.maximum")
|
||||
o.maximum_op([t.input1, t.intermediate2], [t.output])
|
||||
|
||||
def constraint(self, o, t):
|
||||
return o.full_op.value == self.zero
|
||||
|
||||
def result_pattern(self, o, t):
|
||||
o.result_op = o.ap_native_op("pd_op.relu")
|
||||
o.result_op([t.input1], [t.output])
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
DataType = __builtin__DataType # noqa: F821
|
||||
DataValue = __builtin__DataValue # noqa: F821
|
||||
PointerType = __builtin__PointerType # noqa: F821
|
||||
PointerValue = __builtin__PointerValue # noqa: F821
|
||||
MutableList = __builtin__MutableList # noqa: F821
|
||||
OrderedDict = __builtin__OrderedDict # noqa: F821
|
||||
MutableOrderedDict = __builtin__MutableOrderedDict # noqa: F821
|
||||
AttrMap = __builtin__AttrMap # noqa: F821
|
||||
SerializableAttrMap = __builtin__BuiltinSerializableAttrMap # noqa: F821
|
||||
|
||||
_raise = __builtin__raise # noqa: F821
|
||||
|
||||
foreach = __builtin__foreach # noqa: F821
|
||||
range = __builtin__range # noqa: F821
|
||||
map = __builtin__map # noqa: F821
|
||||
reduce = __builtin__reduce # noqa: F821
|
||||
filter = __builtin__filter # noqa: F821
|
||||
zip = __builtin__zip # noqa: F821
|
||||
flat_map = __builtin__flat_map # noqa: F821
|
||||
apply = __builtin__apply # noqa: F821
|
||||
replace_or_trim_left_comma = __builtin__replace_or_trim_left_comma # noqa: F821
|
||||
|
||||
sorted = __builtin__sorted # noqa: F821
|
||||
|
||||
dirname = __builtin__dirname # noqa: F821
|
||||
basename = __builtin__basename # noqa: F821
|
||||
@@ -0,0 +1,62 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import __builtin__
|
||||
|
||||
|
||||
class RegistryEntry:
|
||||
def __init__(self):
|
||||
self.__tag_name__ = None
|
||||
self.__nice__ = None
|
||||
self.__values__ = __builtin__.MutableList()
|
||||
self.__child_register_item_name2value__ = (
|
||||
__builtin__.MutableOrderedDict()
|
||||
)
|
||||
|
||||
# tag_name: str
|
||||
# nice: int
|
||||
def __getattr__(self, attrname):
|
||||
def contains():
|
||||
return self.__child_register_item_name2value__.contains(attrname)
|
||||
|
||||
def find():
|
||||
return self.__child_register_item_name2value__[attrname]
|
||||
|
||||
def create():
|
||||
register_entry = RegistryEntry()
|
||||
self.__child_register_item_name2value__[attrname] = register_entry
|
||||
return register_entry
|
||||
|
||||
return find() if contains() else create()
|
||||
|
||||
def __call__(self, tag_name, nice):
|
||||
registry_obj = RegistryObject(tag_name, nice)
|
||||
self.__values__.append(registry_obj)
|
||||
return RegisterItemDecorator(registry_obj)
|
||||
|
||||
|
||||
class RegistryObject:
|
||||
def __init__(self, tag_name, nice):
|
||||
self.tag_name = tag_name
|
||||
self.nice = nice
|
||||
self.value = None
|
||||
|
||||
|
||||
class RegisterItemDecorator:
|
||||
def __init__(self, register_obj):
|
||||
self.register_obj = register_obj
|
||||
|
||||
def __call__(self, value):
|
||||
self.register_obj.value = value
|
||||
return value
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
|
||||
def GetGroupedTrivialOpNames():
|
||||
return [
|
||||
"pd_op.sin",
|
||||
"pd_op.add",
|
||||
"pd_op.relu",
|
||||
"pd_op.data",
|
||||
"pd_op.full",
|
||||
"pd_op.cast",
|
||||
"pd_op.exp",
|
||||
"pd_op.relu",
|
||||
"pd_op.tanh",
|
||||
"pd_op.floor",
|
||||
"pd_op.erf",
|
||||
"pd_op.elementwise_pow",
|
||||
"cinn_op.scale",
|
||||
"pd_op.subtract",
|
||||
"pd_op.add",
|
||||
"pd_op.multiply",
|
||||
"pd_op.divide",
|
||||
"pd_op.maximum",
|
||||
"cinn_op.yield_store",
|
||||
"cinn_op.broadcast",
|
||||
"pd_op.expand",
|
||||
"cinn_op.generate_shape",
|
||||
]
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
import __builtin__
|
||||
|
||||
DataType = __builtin__.DataType
|
||||
DataValue = __builtin__.DataValue
|
||||
PointerType = __builtin__.PointerType
|
||||
PointerValue = __builtin__.PointerValue
|
||||
MutableList = __builtin__.MutableList
|
||||
OrderedDict = __builtin__.OrderedDict
|
||||
MutableOrderedDict = __builtin__.MutableOrderedDict
|
||||
AttrMap = __builtin__.AttrMap
|
||||
SerializableAttrMap = __builtin__.SerializableAttrMap
|
||||
|
||||
_raise = __builtin__._raise
|
||||
|
||||
foreach = __builtin__.foreach
|
||||
range = __builtin__.range
|
||||
map = __builtin__.map
|
||||
reduce = __builtin__.reduce
|
||||
filter = __builtin__.filter
|
||||
zip = __builtin__.zip
|
||||
flat_map = __builtin__.flat_map
|
||||
apply = __builtin__.apply
|
||||
replace_or_trim_left_comma = __builtin__.replace_or_trim_left_comma
|
||||
|
||||
registry = __builtin__registry # noqa: F821
|
||||
|
||||
sorted = __builtin__.sorted
|
||||
|
||||
dirname = __builtin__.dirname
|
||||
basename = __builtin__.basename
|
||||
@@ -0,0 +1,26 @@
|
||||
# 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 . import backends, datasets, features, functional
|
||||
from .backends.backend import info, load, save
|
||||
|
||||
__all__ = [
|
||||
"functional",
|
||||
"features",
|
||||
"datasets",
|
||||
"backends",
|
||||
"load",
|
||||
"info",
|
||||
"save",
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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 . import init_backend
|
||||
from .init_backend import (
|
||||
get_current_backend,
|
||||
list_available_backends,
|
||||
set_backend,
|
||||
)
|
||||
|
||||
init_backend._init_set_audio_backend()
|
||||
|
||||
__all__ = [
|
||||
'get_current_backend',
|
||||
'list_available_backends',
|
||||
'set_backend',
|
||||
]
|
||||
@@ -0,0 +1,164 @@
|
||||
# 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, BinaryIO
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
|
||||
class AudioInfo:
|
||||
"""Audio info, return type of backend info function"""
|
||||
|
||||
sample_rate: int
|
||||
num_samples: int
|
||||
num_channels: int
|
||||
bits_per_sample: int
|
||||
encoding: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sample_rate: int,
|
||||
num_samples: int,
|
||||
num_channels: int,
|
||||
bits_per_sample: int,
|
||||
encoding: str,
|
||||
) -> None:
|
||||
self.sample_rate = sample_rate
|
||||
self.num_samples = num_samples
|
||||
self.num_channels = num_channels
|
||||
self.bits_per_sample = bits_per_sample
|
||||
self.encoding = encoding
|
||||
|
||||
|
||||
def info(filepath: str | BinaryIO) -> AudioInfo:
|
||||
"""Get signal information of input audio file.
|
||||
|
||||
Args:
|
||||
filepath: audio path or file object.
|
||||
|
||||
Returns:
|
||||
AudioInfo: info of the given audio.
|
||||
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import os
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> base_dir = os.getcwd()
|
||||
>>> filepath = os.path.join(base_dir, "test.wav")
|
||||
|
||||
>>> paddle.audio.save(filepath, waveform, sample_rate)
|
||||
>>> wav_info = paddle.audio.info(filepath)
|
||||
"""
|
||||
# for API doc
|
||||
raise NotImplementedError("please set audio backend")
|
||||
|
||||
|
||||
def load(
|
||||
filepath: str | Path,
|
||||
frame_offset: int = 0,
|
||||
num_frames: int = -1,
|
||||
normalize: bool = True,
|
||||
channels_first: bool = True,
|
||||
) -> tuple[Tensor, int]:
|
||||
"""Load audio data from file.Load the audio content start form frame_offset, and get num_frames.
|
||||
|
||||
Args:
|
||||
frame_offset: from 0 to total frames,
|
||||
num_frames: from -1 (means total frames) or number frames which want to read,
|
||||
normalize:
|
||||
if True: return audio which norm to (-1, 1), dtype=float32
|
||||
if False: return audio with raw data, dtype=int16
|
||||
|
||||
channels_first:
|
||||
if True: return audio with shape (channels, time)
|
||||
|
||||
Return:
|
||||
Tuple[paddle.Tensor, int]: (audio_content, sample rate)
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import os
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> base_dir = os.getcwd()
|
||||
>>> filepath = os.path.join(base_dir, "test.wav")
|
||||
|
||||
>>> paddle.audio.save(filepath, waveform, sample_rate)
|
||||
>>> wav_data_read, sr = paddle.audio.load(filepath)
|
||||
"""
|
||||
# for API doc
|
||||
raise NotImplementedError("please set audio backend")
|
||||
|
||||
|
||||
def save(
|
||||
filepath: str,
|
||||
src: Tensor,
|
||||
sample_rate: int,
|
||||
channels_first: bool = True,
|
||||
encoding: str | None = None,
|
||||
bits_per_sample: int | None = 16,
|
||||
) -> None:
|
||||
"""
|
||||
Save audio tensor to file.
|
||||
|
||||
Args:
|
||||
filepath: saved path
|
||||
src: the audio tensor
|
||||
sample_rate: the number of samples of audio per second.
|
||||
channels_first: src channel information
|
||||
if True, means input tensor is (channels, time)
|
||||
if False, means input tensor is (time, channels)
|
||||
encoding:encoding format, wave_backend only support PCM16 now.
|
||||
bits_per_sample: bits per sample, wave_backend only support 16 bits now.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> filepath = "./test.wav"
|
||||
|
||||
>>> paddle.audio.save(filepath, waveform, sample_rate)
|
||||
"""
|
||||
# for API doc
|
||||
raise NotImplementedError("please set audio backend")
|
||||
@@ -0,0 +1,192 @@
|
||||
# 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
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import paddle
|
||||
|
||||
from . import backend, wave_backend
|
||||
|
||||
|
||||
def _check_version(version: str) -> bool:
|
||||
# require paddleaudio >= 1.0.2
|
||||
ver_arr = version.split('.')
|
||||
v0 = int(ver_arr[0])
|
||||
v1 = int(ver_arr[1])
|
||||
v2 = int(ver_arr[2])
|
||||
if v0 < 1:
|
||||
return False
|
||||
if v0 == 1 and v1 == 0 and v2 <= 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def list_available_backends() -> list[str]:
|
||||
"""List available backends, the backends in paddleaudio and the default backend.
|
||||
|
||||
Returns:
|
||||
list[str]: The list of available backends.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> wav_path = "./test.wav"
|
||||
|
||||
>>> current_backend = paddle.audio.backends.get_current_backend()
|
||||
>>> print(current_backend)
|
||||
wave_backend
|
||||
|
||||
>>> backends = paddle.audio.backends.list_available_backends()
|
||||
>>> # default backends is ['wave_backend']
|
||||
>>> # backends is ['wave_backend', 'soundfile'], if have installed paddleaudio >= 1.0.2
|
||||
>>> if 'soundfile' in backends:
|
||||
... paddle.audio.backends.set_backend('soundfile')
|
||||
>>> paddle.audio.save(wav_path, waveform, sample_rate)
|
||||
|
||||
"""
|
||||
backends = []
|
||||
try:
|
||||
import paddleaudio
|
||||
except ImportError:
|
||||
package = "paddleaudio"
|
||||
warn_msg = (
|
||||
f"Failed importing {package}. \n"
|
||||
"only wave_backend(only can deal with PCM16 WAV) supported.\n"
|
||||
"if want soundfile_backend(more audio type supported),\n"
|
||||
f"please manually installed (usually with `pip install {package} >= 1.0.2`). "
|
||||
)
|
||||
warnings.warn(warn_msg)
|
||||
|
||||
if "paddleaudio" in sys.modules:
|
||||
version = paddleaudio.__version__
|
||||
if not _check_version(version):
|
||||
err_msg = (
|
||||
f"the version of paddleaudio installed is {version},\n"
|
||||
"please ensure the paddleaudio >= 1.0.2."
|
||||
)
|
||||
raise ImportError(err_msg)
|
||||
backends = paddleaudio.backends.list_audio_backends()
|
||||
backends.append("wave_backend")
|
||||
return backends
|
||||
|
||||
|
||||
def get_current_backend() -> str:
|
||||
"""Get the name of the current audio backend
|
||||
|
||||
Returns:
|
||||
str: The name of the current backend,
|
||||
the wave_backend or backend imported from paddleaudio
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> wav_path = "./test.wav"
|
||||
|
||||
>>> current_backend = paddle.audio.backends.get_current_backend()
|
||||
>>> print(current_backend)
|
||||
wave_backend
|
||||
|
||||
>>> backends = paddle.audio.backends.list_available_backends()
|
||||
>>> # default backends is ['wave_backend']
|
||||
>>> # backends is ['wave_backend', 'soundfile'], if have installed paddleaudio >= 1.0.2
|
||||
|
||||
>>> if 'soundfile' in backends:
|
||||
... paddle.audio.backends.set_backend('soundfile')
|
||||
>>> paddle.audio.save(wav_path, waveform, sample_rate)
|
||||
|
||||
"""
|
||||
current_backend = None
|
||||
if "paddleaudio" in sys.modules:
|
||||
import paddleaudio
|
||||
|
||||
current_backend = paddleaudio.backends.get_audio_backend()
|
||||
if paddle.audio.load == paddleaudio.load:
|
||||
return current_backend
|
||||
return "wave_backend"
|
||||
|
||||
|
||||
def set_backend(backend_name: str) -> None:
|
||||
"""Set the backend by one of the list_audio_backend return.
|
||||
|
||||
Args:
|
||||
backend (str): one of the list_audio_backend. "wave_backend" is the default. "soundfile" imported from paddleaudio.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> wav_path = "./test.wav"
|
||||
|
||||
>>> current_backend = paddle.audio.backends.get_current_backend()
|
||||
>>> print(current_backend)
|
||||
wave_backend
|
||||
|
||||
>>> backends = paddle.audio.backends.list_available_backends()
|
||||
>>> # default backends is ['wave_backend']
|
||||
>>> # backends is ['wave_backend', 'soundfile'], if have installed paddleaudio >= 1.0.2
|
||||
|
||||
>>> if 'soundfile' in backends:
|
||||
... paddle.audio.backends.set_backend('soundfile')
|
||||
>>> paddle.audio.save(wav_path, waveform, sample_rate)
|
||||
|
||||
"""
|
||||
if backend_name not in list_available_backends():
|
||||
raise NotImplementedError
|
||||
|
||||
if backend_name == "wave_backend":
|
||||
module = wave_backend
|
||||
else:
|
||||
import paddleaudio
|
||||
|
||||
paddleaudio.backends.set_audio_backend(backend_name)
|
||||
module = paddleaudio
|
||||
|
||||
for func in ["save", "load", "info"]:
|
||||
setattr(backend, func, getattr(module, func))
|
||||
setattr(paddle.audio, func, getattr(module, func))
|
||||
|
||||
|
||||
def _init_set_audio_backend() -> None:
|
||||
# init the default wave_backend.
|
||||
for func in ["save", "load", "info"]:
|
||||
setattr(backend, func, getattr(wave_backend, func))
|
||||
@@ -0,0 +1,236 @@
|
||||
# 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
|
||||
|
||||
import wave
|
||||
from typing import TYPE_CHECKING, BinaryIO
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
|
||||
from .backend import AudioInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
|
||||
def _error_message():
|
||||
package = "paddleaudio"
|
||||
warn_msg = (
|
||||
"only PCM16 WAV supported. \n"
|
||||
"if want support more other audio types, please "
|
||||
f"manually installed (usually with `pip install {package}`). \n "
|
||||
"and use paddle.audio.backends.set_backend('soundfile') to set audio backend"
|
||||
)
|
||||
return warn_msg
|
||||
|
||||
|
||||
def info(filepath: str | BinaryIO) -> AudioInfo:
|
||||
"""Get signal information of input audio file.
|
||||
|
||||
Args:
|
||||
filepath: audio path or file object.
|
||||
|
||||
Returns:
|
||||
AudioInfo: info of the given audio.
|
||||
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import os
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> base_dir = os.getcwd()
|
||||
>>> filepath = os.path.join(base_dir, "test.wav")
|
||||
|
||||
>>> paddle.audio.save(filepath, waveform, sample_rate)
|
||||
>>> wav_info = paddle.audio.info(filepath)
|
||||
"""
|
||||
|
||||
if hasattr(filepath, 'read'):
|
||||
file_obj = filepath
|
||||
else:
|
||||
file_obj = open(filepath, 'rb')
|
||||
|
||||
try:
|
||||
file_ = wave.open(file_obj)
|
||||
except wave.Error:
|
||||
file_obj.seek(0)
|
||||
file_obj.close()
|
||||
err_msg = _error_message()
|
||||
raise NotImplementedError(err_msg)
|
||||
|
||||
channels = file_.getnchannels()
|
||||
sample_rate = file_.getframerate()
|
||||
sample_frames = file_.getnframes() # audio frame
|
||||
bits_per_sample = file_.getsampwidth() * 8
|
||||
encoding = "PCM_S" # default WAV encoding, only support
|
||||
file_obj.close()
|
||||
return AudioInfo(
|
||||
sample_rate, sample_frames, channels, bits_per_sample, encoding
|
||||
)
|
||||
|
||||
|
||||
def load(
|
||||
filepath: str | Path,
|
||||
frame_offset: int = 0,
|
||||
num_frames: int = -1,
|
||||
normalize: bool = True,
|
||||
channels_first: bool = True,
|
||||
) -> tuple[Tensor, int]:
|
||||
"""Load audio data from file. load the audio content start form frame_offset, and get num_frames.
|
||||
|
||||
Args:
|
||||
frame_offset: from 0 to total frames,
|
||||
num_frames: from -1 (means total frames) or number frames which want to read,
|
||||
normalize:
|
||||
if True: return audio which norm to (-1, 1), dtype=float32
|
||||
if False: return audio with raw data, dtype=int16
|
||||
|
||||
channels_first:
|
||||
if True: return audio with shape (channels, time)
|
||||
|
||||
Return:
|
||||
Tuple[paddle.Tensor, int]: (audio_content, sample rate)
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import os
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> base_dir = os.getcwd()
|
||||
>>> filepath = os.path.join(base_dir, "test.wav")
|
||||
|
||||
>>> paddle.audio.save(filepath, waveform, sample_rate)
|
||||
>>> wav_data_read, sr = paddle.audio.load(filepath)
|
||||
"""
|
||||
if hasattr(filepath, 'read'):
|
||||
file_obj = filepath
|
||||
else:
|
||||
file_obj = open(filepath, 'rb')
|
||||
|
||||
try:
|
||||
file_ = wave.open(file_obj)
|
||||
except wave.Error:
|
||||
file_obj.seek(0)
|
||||
file_obj.close()
|
||||
err_msg = _error_message()
|
||||
raise NotImplementedError(err_msg)
|
||||
|
||||
channels = file_.getnchannels()
|
||||
sample_rate = file_.getframerate()
|
||||
frames = file_.getnframes() # audio frame
|
||||
|
||||
audio_content = file_.readframes(frames)
|
||||
file_obj.close()
|
||||
|
||||
# default_subtype = "PCM_16", only support PCM16 WAV
|
||||
audio_as_np16 = np.frombuffer(audio_content, dtype=np.int16)
|
||||
audio_as_np32 = audio_as_np16.astype(np.float32)
|
||||
if normalize:
|
||||
# dtype = "float32"
|
||||
audio_norm = audio_as_np32 / (2**15)
|
||||
else:
|
||||
# dtype = "int16"
|
||||
audio_norm = audio_as_np32
|
||||
|
||||
waveform = np.reshape(audio_norm, (frames, channels))
|
||||
if num_frames != -1:
|
||||
waveform = waveform[frame_offset : frame_offset + num_frames, :]
|
||||
waveform = paddle.to_tensor(waveform)
|
||||
if channels_first:
|
||||
waveform = paddle.transpose(waveform, perm=[1, 0])
|
||||
return waveform, sample_rate
|
||||
|
||||
|
||||
def save(
|
||||
filepath: str,
|
||||
src: Tensor,
|
||||
sample_rate: int,
|
||||
channels_first: bool = True,
|
||||
encoding: str | None = None,
|
||||
bits_per_sample: int | None = 16,
|
||||
) -> None:
|
||||
"""
|
||||
Save audio tensor to file.
|
||||
|
||||
Args:
|
||||
filepath: saved path
|
||||
src: the audio tensor
|
||||
sample_rate: the number of samples of audio per second.
|
||||
channels_first: src channel information
|
||||
if True, means input tensor is (channels, time)
|
||||
if False, means input tensor is (time, channels)
|
||||
encoding: audio encoding format, wave_backend only support PCM16 now.
|
||||
bits_per_sample: bits per sample, wave_backend only support 16 bits now.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
>>> filepath = "./test.wav"
|
||||
|
||||
>>> paddle.audio.save(filepath, waveform, sample_rate)
|
||||
"""
|
||||
assert src.ndim == 2, "Expected 2D tensor"
|
||||
|
||||
audio_numpy = src.numpy()
|
||||
|
||||
# change src shape to (time, channels)
|
||||
if channels_first:
|
||||
audio_numpy = np.transpose(audio_numpy)
|
||||
|
||||
channels = audio_numpy.shape[1]
|
||||
|
||||
# only support PCM16
|
||||
if bits_per_sample not in (None, 16):
|
||||
raise ValueError("Invalid bits_per_sample, only support 16 bit")
|
||||
|
||||
sample_width = int(bits_per_sample / 8) # 2
|
||||
|
||||
if src.dtype == paddle.float32:
|
||||
audio_numpy = (audio_numpy * (2**15)).astype("<h")
|
||||
|
||||
with wave.open(filepath, 'w') as f:
|
||||
f.setnchannels(channels)
|
||||
f.setsampwidth(sample_width)
|
||||
f.setframerate(sample_rate)
|
||||
f.writeframes(audio_numpy.tobytes())
|
||||
@@ -0,0 +1,18 @@
|
||||
# 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 .esc50 import ESC50
|
||||
from .tess import TESS
|
||||
|
||||
__all__ = ["ESC50", "TESS"]
|
||||
@@ -0,0 +1,98 @@
|
||||
# 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
|
||||
|
||||
import paddle
|
||||
|
||||
from ..features import MFCC, LogMelSpectrogram, MelSpectrogram, Spectrogram
|
||||
|
||||
feat_funcs = {
|
||||
'raw': None,
|
||||
'melspectrogram': MelSpectrogram,
|
||||
'mfcc': MFCC,
|
||||
'logmelspectrogram': LogMelSpectrogram,
|
||||
'spectrogram': Spectrogram,
|
||||
}
|
||||
|
||||
|
||||
class AudioClassificationDataset(paddle.io.Dataset):
|
||||
"""
|
||||
Base class of audio classification dataset.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
files: list[str],
|
||||
labels: list[int],
|
||||
feat_type: str = 'raw',
|
||||
sample_rate: int | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
files (:obj:`List[str]`): A list of absolute path of audio files.
|
||||
labels (:obj:`List[int]`): Labels of audio files.
|
||||
feat_type (:obj:`str`, `optional`, defaults to `raw`):
|
||||
It identifies the feature type that user wants to extract an audio file.
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
if feat_type not in feat_funcs.keys():
|
||||
raise RuntimeError(
|
||||
f"Unknown feat_type: {feat_type}, it must be one in {list(feat_funcs.keys())}"
|
||||
)
|
||||
|
||||
self.files = files
|
||||
self.labels = labels
|
||||
|
||||
self.feat_type = feat_type
|
||||
self.sample_rate = sample_rate
|
||||
self.feat_config = (
|
||||
kwargs # Pass keyword arguments to customize feature config
|
||||
)
|
||||
|
||||
def _get_data(self, input_file: str):
|
||||
raise NotImplementedError
|
||||
|
||||
def _convert_to_record(self, idx):
|
||||
file, label = self.files[idx], self.labels[idx]
|
||||
waveform, sample_rate = paddle.audio.load(file)
|
||||
self.sample_rate = sample_rate
|
||||
|
||||
feat_func = feat_funcs[self.feat_type]
|
||||
|
||||
record = {}
|
||||
if len(waveform.shape) == 2:
|
||||
waveform = waveform.squeeze(0) # 1D input
|
||||
waveform = paddle.to_tensor(waveform, dtype=paddle.float32)
|
||||
if feat_func is not None:
|
||||
waveform = waveform.unsqueeze(0) # (batch_size, T)
|
||||
if self.feat_type != 'spectrogram':
|
||||
feature_extractor = feat_func(
|
||||
sr=self.sample_rate, **self.feat_config
|
||||
)
|
||||
else:
|
||||
feature_extractor = feat_func(**self.feat_config)
|
||||
record['feat'] = feature_extractor(waveform).squeeze(0)
|
||||
else:
|
||||
record['feat'] = waveform
|
||||
record['label'] = label
|
||||
return record
|
||||
|
||||
def __getitem__(self, idx):
|
||||
record = self._convert_to_record(idx)
|
||||
return record['feat'], record['label']
|
||||
|
||||
def __len__(self):
|
||||
return len(self.files)
|
||||
@@ -0,0 +1,227 @@
|
||||
# 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
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, Literal, NamedTuple, TypeAlias
|
||||
|
||||
from paddle.dataset.common import DATA_HOME
|
||||
from paddle.utils import download
|
||||
|
||||
from .dataset import AudioClassificationDataset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_ModeLiteral: TypeAlias = Literal[
|
||||
'train',
|
||||
'dev',
|
||||
]
|
||||
_FeatTypeLiteral: TypeAlias = Literal[
|
||||
'raw',
|
||||
'melspectrogram',
|
||||
'mfcc',
|
||||
'logmelspectrogram',
|
||||
'spectrogram',
|
||||
]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class ESC50(AudioClassificationDataset):
|
||||
"""
|
||||
The ESC-50 dataset is a labeled collection of 2000 environmental audio recordings
|
||||
suitable for benchmarking methods of environmental sound classification. The dataset
|
||||
consists of 5-second-long recordings organized into 50 semantical classes (with
|
||||
40 examples per class)
|
||||
|
||||
Reference:
|
||||
ESC: Dataset for Environmental Sound Classification
|
||||
http://dx.doi.org/10.1145/2733373.2806390
|
||||
|
||||
Args:
|
||||
mode (str, optional): It identifies the dataset mode (train or dev). Default:train.
|
||||
split (int, optional): It specify the fold of dev dataset. Default:1.
|
||||
feat_type (str, optional): It identifies the feature type that user wants to extract of an audio file. Default:raw.
|
||||
archive(dict, optional): it tells where to download the audio archive. Default:None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of ESC50 dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(60)
|
||||
>>> import paddle
|
||||
|
||||
>>> esc50_dataset = paddle.audio.datasets.ESC50(
|
||||
... mode='dev',
|
||||
... feat_type='raw',
|
||||
... )
|
||||
>>> for idx in range(5):
|
||||
... audio, label = esc50_dataset[idx]
|
||||
... # do something with audio, label
|
||||
... print(audio.shape, label)
|
||||
... # [audio_data_length] , label_id
|
||||
paddle.Size([220500]) 0
|
||||
paddle.Size([220500]) 14
|
||||
paddle.Size([220500]) 36
|
||||
paddle.Size([220500]) 36
|
||||
paddle.Size([220500]) 19
|
||||
|
||||
>>> esc50_dataset = paddle.audio.datasets.ESC50(
|
||||
... mode='dev',
|
||||
... feat_type='mfcc',
|
||||
... n_mfcc=40,
|
||||
... )
|
||||
>>> for idx in range(5):
|
||||
... audio, label = esc50_dataset[idx]
|
||||
... # do something with mfcc feature, label
|
||||
... print(audio.shape, label)
|
||||
... # [feature_dim, length] , label_id
|
||||
paddle.Size([40, 1723]) 0
|
||||
paddle.Size([40, 1723]) 14
|
||||
paddle.Size([40, 1723]) 36
|
||||
paddle.Size([40, 1723]) 36
|
||||
paddle.Size([40, 1723]) 19
|
||||
|
||||
"""
|
||||
|
||||
archive: dict[str, str] = {
|
||||
'url': 'https://paddleaudio.bj.bcebos.com/datasets/ESC-50-master.zip',
|
||||
'md5': '7771e4b9d86d0945acce719c7a59305a',
|
||||
}
|
||||
|
||||
label_list: list[str] = [
|
||||
# Animals
|
||||
'Dog',
|
||||
'Rooster',
|
||||
'Pig',
|
||||
'Cow',
|
||||
'Frog',
|
||||
'Cat',
|
||||
'Hen',
|
||||
'Insects (flying)',
|
||||
'Sheep',
|
||||
'Crow',
|
||||
# Natural soundscapes & water sounds
|
||||
'Rain',
|
||||
'Sea waves',
|
||||
'Crackling fire',
|
||||
'Crickets',
|
||||
'Chirping birds',
|
||||
'Water drops',
|
||||
'Wind',
|
||||
'Pouring water',
|
||||
'Toilet flush',
|
||||
'Thunderstorm',
|
||||
# Human, non-speech sounds
|
||||
'Crying baby',
|
||||
'Sneezing',
|
||||
'Clapping',
|
||||
'Breathing',
|
||||
'Coughing',
|
||||
'Footsteps',
|
||||
'Laughing',
|
||||
'Brushing teeth',
|
||||
'Snoring',
|
||||
'Drinking, sipping',
|
||||
# Interior/domestic sounds
|
||||
'Door knock',
|
||||
'Mouse click',
|
||||
'Keyboard typing',
|
||||
'Door, wood creaks',
|
||||
'Can opening',
|
||||
'Washing machine',
|
||||
'Vacuum cleaner',
|
||||
'Clock alarm',
|
||||
'Clock tick',
|
||||
'Glass breaking',
|
||||
# Exterior/urban noises
|
||||
'Helicopter',
|
||||
'Chainsaw',
|
||||
'Siren',
|
||||
'Car horn',
|
||||
'Engine',
|
||||
'Train',
|
||||
'Church bells',
|
||||
'Airplane',
|
||||
'Fireworks',
|
||||
'Hand saw',
|
||||
]
|
||||
meta: str = os.path.join('ESC-50-master', 'meta', 'esc50.csv')
|
||||
audio_path: str = os.path.join('ESC-50-master', 'audio')
|
||||
|
||||
class meta_info(NamedTuple):
|
||||
filename: str
|
||||
fold: str
|
||||
target: str
|
||||
category: str
|
||||
esc10: str
|
||||
src_file: str
|
||||
take: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mode: _ModeLiteral = 'train',
|
||||
split: int = 1,
|
||||
feat_type: _FeatTypeLiteral = 'raw',
|
||||
archive: dict[str, str] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
assert split in range(1, 6), (
|
||||
f'The selected split should be integer, and 1 <= split <= 5, but got {split}'
|
||||
)
|
||||
if archive is not None:
|
||||
self.archive = archive
|
||||
files, labels = self._get_data(mode, split)
|
||||
super().__init__(
|
||||
files=files, labels=labels, feat_type=feat_type, **kwargs
|
||||
)
|
||||
|
||||
def _get_meta_info(self) -> list[meta_info]:
|
||||
ret = []
|
||||
with open(os.path.join(DATA_HOME, self.meta), 'r') as rf:
|
||||
for line in rf.readlines()[1:]:
|
||||
ret.append(self.meta_info(*line.strip().split(',')))
|
||||
return ret
|
||||
|
||||
def _get_data(
|
||||
self, mode: _ModeLiteral, split: int
|
||||
) -> tuple[list[str], list[int]]:
|
||||
if not os.path.isdir(
|
||||
os.path.join(DATA_HOME, self.audio_path)
|
||||
) or not os.path.isfile(os.path.join(DATA_HOME, self.meta)):
|
||||
download.get_path_from_url(
|
||||
self.archive['url'],
|
||||
DATA_HOME,
|
||||
self.archive['md5'],
|
||||
decompress=True,
|
||||
)
|
||||
|
||||
meta_info = self._get_meta_info()
|
||||
|
||||
files = []
|
||||
labels = []
|
||||
for sample in meta_info:
|
||||
filename, fold, target, _, _, _, _ = sample
|
||||
if mode == 'train' and int(fold) != split:
|
||||
files.append(os.path.join(DATA_HOME, self.audio_path, filename))
|
||||
labels.append(int(target))
|
||||
|
||||
if mode != 'train' and int(fold) == split:
|
||||
files.append(os.path.join(DATA_HOME, self.audio_path, filename))
|
||||
labels.append(int(target))
|
||||
|
||||
return files, labels
|
||||
@@ -0,0 +1,166 @@
|
||||
# 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
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, NamedTuple
|
||||
|
||||
from paddle.dataset.common import DATA_HOME
|
||||
from paddle.utils import download
|
||||
|
||||
from .dataset import AudioClassificationDataset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .esc50 import _FeatTypeLiteral, _ModeLiteral
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class TESS(AudioClassificationDataset):
|
||||
"""
|
||||
TESS is a set of 200 target words were spoken in the carrier phrase
|
||||
"Say the word _____' by two actresses (aged 26 and 64 years) and
|
||||
recordings were made of the set portraying each of seven emotions(anger,
|
||||
disgust, fear, happiness, pleasant surprise, sadness, and neutral).
|
||||
There are 2800 stimuli in total.
|
||||
|
||||
Reference:
|
||||
Toronto emotional speech set (TESS) https://tspace.library.utoronto.ca/handle/1807/24487
|
||||
https://doi.org/10.5683/SP2/E8H2MF
|
||||
|
||||
Args:
|
||||
mode (str, optional): It identifies the dataset mode (train or dev). Defaults to train.
|
||||
n_folds (int, optional): Split the dataset into n folds. 1 fold for dev dataset and n-1 for train dataset. Defaults to 5.
|
||||
split (int, optional): It specify the fold of dev dataset. Defaults to 1.
|
||||
feat_type (str, optional): It identifies the feature type that user wants to extract of an audio file. Defaults to raw.
|
||||
archive(dict): it tells where to download the audio archive. Defaults to None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of TESS dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(60)
|
||||
>>> import paddle
|
||||
|
||||
>>> tess_dataset = paddle.audio.datasets.TESS(
|
||||
... mode='dev',
|
||||
... feat_type='raw',
|
||||
... )
|
||||
>>> for idx in range(5):
|
||||
... audio, label = tess_dataset[idx]
|
||||
... # do something with audio, label
|
||||
... print(audio.shape, label)
|
||||
... # [audio_data_length] , label_id
|
||||
|
||||
>>> tess_dataset = paddle.audio.datasets.TESS(
|
||||
... mode='dev',
|
||||
... feat_type='mfcc',
|
||||
... n_mfcc=40,
|
||||
... )
|
||||
>>> for idx in range(5):
|
||||
... audio, label = tess_dataset[idx]
|
||||
... # do something with mfcc feature, label
|
||||
... print(audio.shape, label)
|
||||
... # [feature_dim, num_frames] , label_id
|
||||
"""
|
||||
|
||||
archive: dict[str, str] = {
|
||||
'url': 'https://bj.bcebos.com/paddleaudio/datasets/TESS_Toronto_emotional_speech_set.zip',
|
||||
'md5': '1465311b24d1de704c4c63e4ccc470c7',
|
||||
}
|
||||
|
||||
label_list: list[str] = [
|
||||
'angry',
|
||||
'disgust',
|
||||
'fear',
|
||||
'happy',
|
||||
'neutral',
|
||||
'ps', # pleasant surprise
|
||||
'sad',
|
||||
]
|
||||
|
||||
audio_path: str = 'TESS_Toronto_emotional_speech_set'
|
||||
|
||||
class meta_info(NamedTuple):
|
||||
speaker: str
|
||||
word: str
|
||||
emotion: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mode: _ModeLiteral = 'train',
|
||||
n_folds: int = 5,
|
||||
split: int = 1,
|
||||
feat_type: _FeatTypeLiteral = 'raw',
|
||||
archive: dict[str, str] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
assert isinstance(n_folds, int) and (n_folds >= 1), (
|
||||
f'the n_folds should be integer and n_folds >= 1, but got {n_folds}'
|
||||
)
|
||||
assert split in range(1, n_folds + 1), (
|
||||
f'The selected split should be integer and should be 1 <= split <= {n_folds}, but got {split}'
|
||||
)
|
||||
if archive is not None:
|
||||
self.archive = archive
|
||||
files, labels = self._get_data(mode, n_folds, split)
|
||||
super().__init__(
|
||||
files=files, labels=labels, feat_type=feat_type, **kwargs
|
||||
)
|
||||
|
||||
def _get_meta_info(self, files) -> list[meta_info]:
|
||||
ret = []
|
||||
for file in files:
|
||||
basename_without_extend = os.path.basename(file)[:-4]
|
||||
ret.append(self.meta_info(*basename_without_extend.split('_')))
|
||||
return ret
|
||||
|
||||
def _get_data(
|
||||
self, mode: str, n_folds: int, split: int
|
||||
) -> tuple[list[str], list[int]]:
|
||||
if not os.path.isdir(os.path.join(DATA_HOME, self.audio_path)):
|
||||
download.get_path_from_url(
|
||||
self.archive['url'],
|
||||
DATA_HOME,
|
||||
self.archive['md5'],
|
||||
decompress=True,
|
||||
)
|
||||
|
||||
wav_files = []
|
||||
for root, _, files in os.walk(os.path.join(DATA_HOME, self.audio_path)):
|
||||
for file in files:
|
||||
if file.endswith('.wav'):
|
||||
wav_files.append(os.path.join(root, file))
|
||||
|
||||
meta_info = self._get_meta_info(wav_files)
|
||||
|
||||
files = []
|
||||
labels = []
|
||||
for idx, sample in enumerate(meta_info):
|
||||
_, _, emotion = sample
|
||||
target = self.label_list.index(emotion)
|
||||
fold = idx % n_folds + 1
|
||||
|
||||
if mode == 'train' and int(fold) != split:
|
||||
files.append(wav_files[idx])
|
||||
labels.append(target)
|
||||
|
||||
if mode != 'train' and int(fold) == split:
|
||||
files.append(wav_files[idx])
|
||||
labels.append(target)
|
||||
|
||||
return files, labels
|
||||
@@ -0,0 +1,26 @@
|
||||
# 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 .layers import (
|
||||
MFCC,
|
||||
LogMelSpectrogram,
|
||||
MelSpectrogram,
|
||||
Spectrogram,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'LogMelSpectrogram',
|
||||
'MelSpectrogram',
|
||||
'MFCC',
|
||||
'Spectrogram',
|
||||
]
|
||||
@@ -0,0 +1,448 @@
|
||||
# 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 functools import partial
|
||||
from typing import TYPE_CHECKING, Literal, TypeAlias
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
|
||||
from ..functional import compute_fbank_matrix, create_dct, power_to_db
|
||||
from ..functional.window import get_window
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
|
||||
_WindowLiteral: TypeAlias = Literal[
|
||||
'hamming',
|
||||
'hann',
|
||||
'kaiser',
|
||||
'bartlett',
|
||||
'nuttall',
|
||||
'gaussian',
|
||||
'exponential',
|
||||
'triang',
|
||||
'bohman',
|
||||
'blackman',
|
||||
'cosine',
|
||||
'tukey',
|
||||
'taylor',
|
||||
]
|
||||
|
||||
|
||||
class Spectrogram(nn.Layer):
|
||||
"""Compute spectrogram of given signals, typically audio waveforms.
|
||||
The spectrogram is defined as the complex norm of the short-time Fourier transformation.
|
||||
|
||||
Args:
|
||||
n_fft (int, optional): The number of frequency components of the discrete Fourier transform. Defaults to 512.
|
||||
hop_length (Optional[int], optional): The hop length of the short time FFT. If `None`, it is set to `win_length//4`. Defaults to None.
|
||||
win_length (Optional[int], optional): The window length of the short time FFT. If `None`, it is set to same as `n_fft`. Defaults to None.
|
||||
window (str, optional): The window function applied to the signal before the Fourier transform. Supported window functions: 'hamming', 'hann', 'gaussian', 'exponential', 'triang', 'bohman', 'blackman', 'cosine', 'tukey', 'taylor', 'bartlett', 'kaiser', 'nuttall'. Defaults to 'hann'.
|
||||
power (float, optional): Exponent for the magnitude spectrogram. Defaults to 2.0.
|
||||
center (bool, optional): Whether to pad `x` to make that the :math:`t \times hop\\_length` at the center of `t`-th frame. Defaults to True.
|
||||
pad_mode (str, optional): Choose padding pattern when `center` is `True`. Defaults to 'reflect'.
|
||||
dtype (str, optional): Data type of input and window. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of Spectrogram.
|
||||
|
||||
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.audio.features import Spectrogram
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
|
||||
>>> feature_extractor = Spectrogram(n_fft=512, window='hann', power=1.0)
|
||||
>>> feats = feature_extractor(waveform)
|
||||
"""
|
||||
|
||||
power: float
|
||||
fft_window: Tensor
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
n_fft: int = 512,
|
||||
hop_length: int | None = 512,
|
||||
win_length: int | None = None,
|
||||
window: _WindowLiteral = 'hann',
|
||||
power: float = 1.0,
|
||||
center: bool = True,
|
||||
pad_mode: Literal['reflect'] = 'reflect',
|
||||
dtype: str = 'float32',
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
assert power > 0, 'Power of spectrogram must be > 0.'
|
||||
self.power = power
|
||||
|
||||
if win_length is None:
|
||||
win_length = n_fft
|
||||
|
||||
self.fft_window = get_window(
|
||||
window, win_length, fftbins=True, dtype=dtype
|
||||
)
|
||||
self._stft = partial(
|
||||
paddle.signal.stft,
|
||||
n_fft=n_fft,
|
||||
hop_length=hop_length,
|
||||
win_length=win_length,
|
||||
window=self.fft_window,
|
||||
center=center,
|
||||
pad_mode=pad_mode,
|
||||
)
|
||||
self.register_buffer('fft_window', self.fft_window)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
"""
|
||||
Args:
|
||||
x (Tensor): Tensor of waveforms with shape `(N, T)`
|
||||
|
||||
Returns:
|
||||
Tensor: Spectrograms with shape `(N, n_fft//2 + 1, num_frames)`.
|
||||
"""
|
||||
stft = self._stft(x)
|
||||
spectrogram = paddle.pow(paddle.abs(stft), self.power)
|
||||
return spectrogram
|
||||
|
||||
|
||||
class MelSpectrogram(nn.Layer):
|
||||
"""Compute the melspectrogram of given signals, typically audio waveforms. It is computed by multiplying spectrogram with Mel filter bank matrix.
|
||||
|
||||
Args:
|
||||
sr (int, optional): Sample rate. Defaults to 22050.
|
||||
n_fft (int, optional): The number of frequency components of the discrete Fourier transform. Defaults to 512.
|
||||
hop_length (Optional[int], optional): The hop length of the short time FFT. If `None`, it is set to `win_length//4`. Defaults to None.
|
||||
win_length (Optional[int], optional): The window length of the short time FFT. If `None`, it is set to same as `n_fft`. Defaults to None.
|
||||
window (str, optional): The window function applied to the signal before the Fourier transform. Supported window functions: 'hamming', 'hann', 'gaussian', 'exponential', 'triang', 'bohman', 'blackman', 'cosine', 'tukey', 'taylor', 'bartlett', 'kaiser', 'nuttall'. Defaults to 'hann'.
|
||||
power (float, optional): Exponent for the magnitude spectrogram. Defaults to 2.0.
|
||||
center (bool, optional): Whether to pad `x` to make that the :math:`t \times hop\\_length` at the center of `t`-th frame. Defaults to True.
|
||||
pad_mode (str, optional): Choose padding pattern when `center` is `True`. Defaults to 'reflect'.
|
||||
n_mels (int, optional): Number of mel bins. Defaults to 64.
|
||||
f_min (float, optional): Minimum frequency in Hz. Defaults to 50.0.
|
||||
f_max (Optional[float], optional): Maximum frequency in Hz. Defaults to None.
|
||||
htk (bool, optional): Use HTK formula in computing fbank matrix. Defaults to False.
|
||||
norm (Union[str, float], optional): Type of normalization in computing fbank matrix. Slaney-style is used by default. You can specify norm=1.0/2.0 to use customized p-norm normalization. Defaults to 'slaney'.
|
||||
dtype (str, optional): Data type of input and window. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MelSpectrogram.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.audio.features import MelSpectrogram
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
|
||||
>>> feature_extractor = MelSpectrogram(sr=sample_rate, n_fft=512, window='hann', power=1.0)
|
||||
>>> feats = feature_extractor(waveform)
|
||||
"""
|
||||
|
||||
n_mels: int
|
||||
f_min: float
|
||||
f_max: float
|
||||
htk: bool
|
||||
norm: Literal['slaney'] | float
|
||||
fbank_matrix: Tensor
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sr: int = 22050,
|
||||
n_fft: int = 2048,
|
||||
hop_length: int | None = 512,
|
||||
win_length: int | None = None,
|
||||
window: _WindowLiteral = 'hann',
|
||||
power: float = 2.0,
|
||||
center: bool = True,
|
||||
pad_mode: Literal['reflect'] = 'reflect',
|
||||
n_mels: int = 64,
|
||||
f_min: float = 50.0,
|
||||
f_max: float | None = None,
|
||||
htk: bool = False,
|
||||
norm: Literal['slaney'] | float = 'slaney',
|
||||
dtype: str = 'float32',
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._spectrogram = Spectrogram(
|
||||
n_fft=n_fft,
|
||||
hop_length=hop_length,
|
||||
win_length=win_length,
|
||||
window=window,
|
||||
power=power,
|
||||
center=center,
|
||||
pad_mode=pad_mode,
|
||||
dtype=dtype,
|
||||
)
|
||||
self.n_mels = n_mels
|
||||
self.f_min = f_min
|
||||
self.f_max = f_max
|
||||
self.htk = htk
|
||||
self.norm = norm
|
||||
if f_max is None:
|
||||
f_max = sr // 2
|
||||
self.fbank_matrix = compute_fbank_matrix(
|
||||
sr=sr,
|
||||
n_fft=n_fft,
|
||||
n_mels=n_mels,
|
||||
f_min=f_min,
|
||||
f_max=f_max,
|
||||
htk=htk,
|
||||
norm=norm,
|
||||
dtype=dtype,
|
||||
)
|
||||
self.register_buffer('fbank_matrix', self.fbank_matrix)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
"""
|
||||
Args:
|
||||
x (Tensor): Tensor of waveforms with shape `(N, T)`
|
||||
|
||||
Returns:
|
||||
Tensor: Mel spectrograms with shape `(N, n_mels, num_frames)`.
|
||||
"""
|
||||
spect_feature = self._spectrogram(x)
|
||||
mel_feature = paddle.matmul(self.fbank_matrix, spect_feature)
|
||||
return mel_feature
|
||||
|
||||
|
||||
class LogMelSpectrogram(nn.Layer):
|
||||
"""Compute log-mel-spectrogram feature of given signals, typically audio waveforms.
|
||||
|
||||
Args:
|
||||
sr (int, optional): Sample rate. Defaults to 22050.
|
||||
n_fft (int, optional): The number of frequency components of the discrete Fourier transform. Defaults to 512.
|
||||
hop_length (Optional[int], optional): The hop length of the short time FFT. If `None`, it is set to `win_length//4`. Defaults to None.
|
||||
win_length (Optional[int], optional): The window length of the short time FFT. If `None`, it is set to same as `n_fft`. Defaults to None.
|
||||
window (str, optional): The window function applied to the signal before the Fourier transform. Supported window functions: 'hamming', 'hann', 'gaussian', 'exponential', 'triang', 'bohman', 'blackman', 'cosine', 'tukey', 'taylor', 'bartlett', 'kaiser', 'nuttall'. Defaults to 'hann'.
|
||||
power (float, optional): Exponent for the magnitude spectrogram. Defaults to 2.0.
|
||||
center (bool, optional): Whether to pad `x` to make that the :math:`t \times hop\\_length` at the center of `t`-th frame. Defaults to True.
|
||||
pad_mode (str, optional): Choose padding pattern when `center` is `True`. Defaults to 'reflect'.
|
||||
n_mels (int, optional): Number of mel bins. Defaults to 64.
|
||||
f_min (float, optional): Minimum frequency in Hz. Defaults to 50.0.
|
||||
f_max (Optional[float], optional): Maximum frequency in Hz. Defaults to None.
|
||||
htk (bool, optional): Use HTK formula in computing fbank matrix. Defaults to False.
|
||||
norm (Union[str, float], optional): Type of normalization in computing fbank matrix. Slaney-style is used by default. You can specify norm=1.0/2.0 to use customized p-norm normalization. Defaults to 'slaney'.
|
||||
ref_value (float, optional): The reference value. If smaller than 1.0, the db level of the signal will be pulled up accordingly. Otherwise, the db level is pushed down. Defaults to 1.0.
|
||||
amin (float, optional): The minimum value of input magnitude. Defaults to 1e-10.
|
||||
top_db (Optional[float], optional): The maximum db value of spectrogram. Defaults to None.
|
||||
dtype (str, optional): Data type of input and window. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of LogMelSpectrogram.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.audio.features import LogMelSpectrogram
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
|
||||
>>> feature_extractor = LogMelSpectrogram(sr=sample_rate, n_fft=512, window='hann', power=1.0)
|
||||
>>> feats = feature_extractor(waveform)
|
||||
"""
|
||||
|
||||
ref_value: float
|
||||
amin: float
|
||||
top_db: float | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sr: int = 22050,
|
||||
n_fft: int = 512,
|
||||
hop_length: int | None = None,
|
||||
win_length: int | None = None,
|
||||
window: _WindowLiteral = 'hann',
|
||||
power: float = 2.0,
|
||||
center: bool = True,
|
||||
pad_mode: Literal['reflect'] = 'reflect',
|
||||
n_mels: int = 64,
|
||||
f_min: float = 50.0,
|
||||
f_max: float | None = None,
|
||||
htk: bool = False,
|
||||
norm: Literal['slaney'] | float = 'slaney',
|
||||
ref_value: float = 1.0,
|
||||
amin: float = 1e-10,
|
||||
top_db: float | None = None,
|
||||
dtype: str = 'float32',
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._melspectrogram = MelSpectrogram(
|
||||
sr=sr,
|
||||
n_fft=n_fft,
|
||||
hop_length=hop_length,
|
||||
win_length=win_length,
|
||||
window=window,
|
||||
power=power,
|
||||
center=center,
|
||||
pad_mode=pad_mode,
|
||||
n_mels=n_mels,
|
||||
f_min=f_min,
|
||||
f_max=f_max,
|
||||
htk=htk,
|
||||
norm=norm,
|
||||
dtype=dtype,
|
||||
)
|
||||
|
||||
self.ref_value = ref_value
|
||||
self.amin = amin
|
||||
self.top_db = top_db
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
"""
|
||||
Args:
|
||||
x (Tensor): Tensor of waveforms with shape `(N, T)`
|
||||
|
||||
Returns:
|
||||
Tensor: Log mel spectrograms with shape `(N, n_mels, num_frames)`.
|
||||
"""
|
||||
mel_feature = self._melspectrogram(x)
|
||||
log_mel_feature = power_to_db(
|
||||
mel_feature,
|
||||
ref_value=self.ref_value,
|
||||
amin=self.amin,
|
||||
top_db=self.top_db,
|
||||
)
|
||||
return log_mel_feature
|
||||
|
||||
|
||||
class MFCC(nn.Layer):
|
||||
"""Compute mel frequency cepstral coefficients(MFCCs) feature of given waveforms.
|
||||
|
||||
Args:
|
||||
sr (int, optional): Sample rate. Defaults to 22050.
|
||||
n_mfcc (int, optional): [description]. Defaults to 40.
|
||||
n_fft (int, optional): The number of frequency components of the discrete Fourier transform. Defaults to 512.
|
||||
hop_length (Optional[int], optional): The hop length of the short time FFT. If `None`, it is set to `win_length//4`. Defaults to None.
|
||||
win_length (Optional[int], optional): The window length of the short time FFT. If `None`, it is set to same as `n_fft`. Defaults to None.
|
||||
window (str, optional): The window function applied to the signal before the Fourier transform. Supported window functions: 'hamming', 'hann', 'gaussian', 'exponential', 'triang', 'bohman', 'blackman', 'cosine', 'tukey', 'taylor', 'bartlett', 'kaiser', 'nuttall'. Defaults to 'hann'.
|
||||
power (float, optional): Exponent for the magnitude spectrogram. Defaults to 2.0.
|
||||
center (bool, optional): Whether to pad `x` to make that the :math:`t \times hop\\_length` at the center of `t`-th frame. Defaults to True.
|
||||
pad_mode (str, optional): Choose padding pattern when `center` is `True`. Defaults to 'reflect'.
|
||||
n_mels (int, optional): Number of mel bins. Defaults to 64.
|
||||
f_min (float, optional): Minimum frequency in Hz. Defaults to 50.0.
|
||||
f_max (Optional[float], optional): Maximum frequency in Hz. Defaults to None.
|
||||
htk (bool, optional): Use HTK formula in computing fbank matrix. Defaults to False.
|
||||
norm (Union[str, float], optional): Type of normalization in computing fbank matrix. Slaney-style is used by default. You can specify norm=1.0/2.0 to use customized p-norm normalization. Defaults to 'slaney'.
|
||||
ref_value (float, optional): The reference value. If smaller than 1.0, the db level of the signal will be pulled up accordingly. Otherwise, the db level is pushed down. Defaults to 1.0.
|
||||
amin (float, optional): The minimum value of input magnitude. Defaults to 1e-10.
|
||||
top_db (Optional[float], optional): The maximum db value of spectrogram. Defaults to None.
|
||||
dtype (str, optional): Data type of input and window. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MFCC.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.audio.features import MFCC
|
||||
|
||||
>>> sample_rate = 16000
|
||||
>>> wav_duration = 0.5
|
||||
>>> num_channels = 1
|
||||
>>> num_frames = sample_rate * wav_duration
|
||||
>>> wav_data = paddle.linspace(-1.0, 1.0, int(num_frames)) * 0.1
|
||||
>>> waveform = wav_data.tile([num_channels, 1])
|
||||
|
||||
>>> feature_extractor = MFCC(sr=sample_rate, n_fft=512, window='hann')
|
||||
>>> feats = feature_extractor(waveform)
|
||||
"""
|
||||
|
||||
dct_matrix: Tensor
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sr: int = 22050,
|
||||
n_mfcc: int = 40,
|
||||
n_fft: int = 512,
|
||||
hop_length: int | None = None,
|
||||
win_length: int | None = None,
|
||||
window: _WindowLiteral = 'hann',
|
||||
power: float = 2.0,
|
||||
center: bool = True,
|
||||
pad_mode: Literal['reflect'] = 'reflect',
|
||||
n_mels: int = 64,
|
||||
f_min: float = 50.0,
|
||||
f_max: float | None = None,
|
||||
htk: bool = False,
|
||||
norm: Literal['slaney'] | float = 'slaney',
|
||||
ref_value: float = 1.0,
|
||||
amin: float = 1e-10,
|
||||
top_db: float | None = None,
|
||||
dtype: str = 'float32',
|
||||
) -> None:
|
||||
super().__init__()
|
||||
assert n_mfcc <= n_mels, (
|
||||
f'n_mfcc cannot be larger than n_mels: {n_mfcc} vs {n_mels}'
|
||||
)
|
||||
self._log_melspectrogram = LogMelSpectrogram(
|
||||
sr=sr,
|
||||
n_fft=n_fft,
|
||||
hop_length=hop_length,
|
||||
win_length=win_length,
|
||||
window=window,
|
||||
power=power,
|
||||
center=center,
|
||||
pad_mode=pad_mode,
|
||||
n_mels=n_mels,
|
||||
f_min=f_min,
|
||||
f_max=f_max,
|
||||
htk=htk,
|
||||
norm=norm,
|
||||
ref_value=ref_value,
|
||||
amin=amin,
|
||||
top_db=top_db,
|
||||
dtype=dtype,
|
||||
)
|
||||
self.dct_matrix = create_dct(n_mfcc=n_mfcc, n_mels=n_mels, dtype=dtype)
|
||||
self.register_buffer('dct_matrix', self.dct_matrix)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
"""
|
||||
Args:
|
||||
x (Tensor): Tensor of waveforms with shape `(N, T)`
|
||||
|
||||
Returns:
|
||||
Tensor: Mel frequency cepstral coefficients with shape `(N, n_mfcc, num_frames)`.
|
||||
"""
|
||||
log_mel_feature = self._log_melspectrogram(x)
|
||||
mfcc = paddle.matmul(
|
||||
log_mel_feature.transpose((0, 2, 1)), self.dct_matrix
|
||||
).transpose((0, 2, 1)) # (B, n_mels, L)
|
||||
return mfcc
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright (c) 2026 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 .functional import (
|
||||
compute_fbank_matrix,
|
||||
create_dct,
|
||||
fft_frequencies,
|
||||
hz_to_mel,
|
||||
mel_frequencies,
|
||||
mel_to_hz,
|
||||
power_to_db,
|
||||
resample,
|
||||
)
|
||||
from .window import get_window
|
||||
|
||||
__all__ = [
|
||||
'compute_fbank_matrix',
|
||||
'create_dct',
|
||||
'fft_frequencies',
|
||||
'hz_to_mel',
|
||||
'mel_frequencies',
|
||||
'mel_to_hz',
|
||||
'power_to_db',
|
||||
'get_window',
|
||||
'resample',
|
||||
]
|
||||
@@ -0,0 +1,611 @@
|
||||
# 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.
|
||||
# Modified from librosa(https://github.com/librosa/librosa)
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TYPE_CHECKING, Literal, TypeVar
|
||||
|
||||
import paddle
|
||||
from paddle import Tensor
|
||||
from paddle.base.framework import Variable
|
||||
from paddle.pir import Value
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_TensorOrFloat = TypeVar("_TensorOrFloat", Tensor, float)
|
||||
|
||||
|
||||
def hz_to_mel(freq: _TensorOrFloat, htk: bool = False) -> _TensorOrFloat:
|
||||
"""Convert Hz to Mels.
|
||||
|
||||
Args:
|
||||
freq (Union[Tensor, float]): The input tensor with arbitrary shape.
|
||||
htk (bool, optional): Use htk scaling. Defaults to False.
|
||||
|
||||
Returns:
|
||||
Union[Tensor, float]: Frequency in mels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> val = 3.0
|
||||
>>> htk_flag = True
|
||||
>>> mel_paddle_tensor = paddle.audio.functional.hz_to_mel(paddle.to_tensor(val), htk_flag)
|
||||
"""
|
||||
|
||||
if htk:
|
||||
if isinstance(freq, (Tensor, Variable, Value)):
|
||||
return 2595.0 * paddle.log10(1.0 + freq / 700.0)
|
||||
else:
|
||||
return 2595.0 * math.log10(1.0 + freq / 700.0)
|
||||
|
||||
# Fill in the linear part
|
||||
f_min = 0.0
|
||||
f_sp = 200.0 / 3
|
||||
|
||||
mels = (freq - f_min) / f_sp
|
||||
|
||||
# Fill in the log-scale part
|
||||
|
||||
min_log_hz = 1000.0 # beginning of log region (Hz)
|
||||
min_log_mel = (min_log_hz - f_min) / f_sp # same (Mels)
|
||||
logstep = math.log(6.4) / 27.0 # step size for log region
|
||||
|
||||
if isinstance(freq, (Tensor, Variable, Value)):
|
||||
target = (
|
||||
min_log_mel + paddle.log(freq / min_log_hz + 1e-10) / logstep
|
||||
) # prevent nan with 1e-10
|
||||
mask = (freq > min_log_hz).astype(freq.dtype)
|
||||
mels = target * mask + mels * (
|
||||
1 - mask
|
||||
) # will replace by masked_fill OP in future
|
||||
else:
|
||||
if freq >= min_log_hz:
|
||||
mels = min_log_mel + math.log(freq / min_log_hz + 1e-10) / logstep
|
||||
|
||||
return mels
|
||||
|
||||
|
||||
def mel_to_hz(mel: _TensorOrFloat, htk: bool = False) -> _TensorOrFloat:
|
||||
"""Convert mel bin numbers to frequencies.
|
||||
|
||||
Args:
|
||||
mel (Union[float, Tensor]): The mel frequency represented as a tensor with arbitrary shape.
|
||||
htk (bool, optional): Use htk scaling. Defaults to False.
|
||||
|
||||
Returns:
|
||||
Union[float, Tensor]: Frequencies in Hz.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> val = 3.0
|
||||
>>> htk_flag = True
|
||||
>>> mel_paddle_tensor = paddle.audio.functional.mel_to_hz(paddle.to_tensor(val), htk_flag)
|
||||
"""
|
||||
if htk:
|
||||
return 700.0 * (10.0 ** (mel / 2595.0) - 1.0)
|
||||
|
||||
f_min = 0.0
|
||||
f_sp = 200.0 / 3
|
||||
freqs = f_min + f_sp * mel
|
||||
# And now the nonlinear scale
|
||||
min_log_hz = 1000.0 # beginning of log region (Hz)
|
||||
min_log_mel = (min_log_hz - f_min) / f_sp # same (Mels)
|
||||
logstep = math.log(6.4) / 27.0 # step size for log region
|
||||
if isinstance(mel, (Tensor, Variable, Value)):
|
||||
target = min_log_hz * paddle.exp(logstep * (mel - min_log_mel))
|
||||
mask = (mel > min_log_mel).astype(mel.dtype)
|
||||
freqs = target * mask + freqs * (
|
||||
1 - mask
|
||||
) # will replace by masked_fill OP in future
|
||||
else:
|
||||
if mel >= min_log_mel:
|
||||
freqs = min_log_hz * math.exp(logstep * (mel - min_log_mel))
|
||||
return freqs
|
||||
|
||||
|
||||
def mel_frequencies(
|
||||
n_mels: int = 64,
|
||||
f_min: float = 0.0,
|
||||
f_max: float = 11025.0,
|
||||
htk: bool = False,
|
||||
dtype: str = 'float32',
|
||||
) -> Tensor:
|
||||
"""Compute mel frequencies.
|
||||
|
||||
Args:
|
||||
n_mels (int, optional): Number of mel bins. Defaults to 64.
|
||||
f_min (float, optional): Minimum frequency in Hz. Defaults to 0.0.
|
||||
fmax (float, optional): Maximum frequency in Hz. Defaults to 11025.0.
|
||||
htk (bool, optional): Use htk scaling. Defaults to False.
|
||||
dtype (str, optional): The data type of the return frequencies. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
Tensor: Tensor of n_mels frequencies in Hz with shape `(n_mels,)`.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> n_mels = 64
|
||||
>>> f_min = 0.5
|
||||
>>> f_max = 10000
|
||||
>>> htk_flag = True
|
||||
|
||||
>>> paddle_mel_freq = paddle.audio.functional.mel_frequencies(n_mels, f_min, f_max, htk_flag, 'float64')
|
||||
"""
|
||||
# 'Center freqs' of mel bands - uniformly spaced between limits
|
||||
min_mel = hz_to_mel(f_min, htk=htk)
|
||||
max_mel = hz_to_mel(f_max, htk=htk)
|
||||
mels = paddle.linspace(min_mel, max_mel, n_mels, dtype=dtype)
|
||||
freqs = mel_to_hz(mels, htk=htk)
|
||||
return freqs
|
||||
|
||||
|
||||
def fft_frequencies(sr: int, n_fft: int, dtype: str = 'float32') -> Tensor:
|
||||
"""Compute fourier frequencies.
|
||||
|
||||
Args:
|
||||
sr (int): Sample rate.
|
||||
n_fft (int): Number of fft bins.
|
||||
dtype (str, optional): The data type of the return frequencies. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
Tensor: FFT frequencies in Hz with shape `(n_fft//2 + 1,)`.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sr = 16000
|
||||
>>> n_fft = 128
|
||||
>>> fft_freq = paddle.audio.functional.fft_frequencies(sr, n_fft)
|
||||
"""
|
||||
return paddle.linspace(0, float(sr) / 2, int(1 + n_fft // 2), dtype=dtype)
|
||||
|
||||
|
||||
def compute_fbank_matrix(
|
||||
sr: int,
|
||||
n_fft: int,
|
||||
n_mels: int = 64,
|
||||
f_min: float = 0.0,
|
||||
f_max: float | None = None,
|
||||
htk: bool = False,
|
||||
norm: Literal['slaney'] | float = 'slaney',
|
||||
dtype: str = 'float32',
|
||||
) -> Tensor:
|
||||
"""Compute fbank matrix.
|
||||
|
||||
Args:
|
||||
sr (int): Sample rate.
|
||||
n_fft (int): Number of fft bins.
|
||||
n_mels (int, optional): Number of mel bins. Defaults to 64.
|
||||
f_min (float, optional): Minimum frequency in Hz. Defaults to 0.0.
|
||||
f_max (Optional[float], optional): Maximum frequency in Hz. Defaults to None.
|
||||
htk (bool, optional): Use htk scaling. Defaults to False.
|
||||
norm (Union[str, float], optional): Type of normalization. Defaults to 'slaney'.
|
||||
dtype (str, optional): The data type of the return matrix. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
Tensor: Mel transform matrix with shape `(n_mels, n_fft//2 + 1)`.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> sr = 23
|
||||
>>> n_fft = 51
|
||||
>>> fbank = paddle.audio.functional.compute_fbank_matrix(sr, n_fft)
|
||||
"""
|
||||
|
||||
if f_max is None:
|
||||
f_max = float(sr) / 2
|
||||
|
||||
# Initialize the weights
|
||||
weights = paddle.zeros((n_mels, int(1 + n_fft // 2)), dtype=dtype)
|
||||
|
||||
# Center freqs of each FFT bin
|
||||
fftfreqs = fft_frequencies(sr=sr, n_fft=n_fft, dtype=dtype)
|
||||
|
||||
# 'Center freqs' of mel bands - uniformly spaced between limits
|
||||
mel_f = mel_frequencies(
|
||||
n_mels + 2, f_min=f_min, f_max=f_max, htk=htk, dtype=dtype
|
||||
)
|
||||
|
||||
fdiff = mel_f[1:] - mel_f[:-1] # np.diff(mel_f)
|
||||
ramps = mel_f.unsqueeze(1) - fftfreqs.unsqueeze(0)
|
||||
# ramps = np.subtract.outer(mel_f, fftfreqs)
|
||||
|
||||
for i in range(n_mels):
|
||||
# lower and upper slopes for all bins
|
||||
lower = -ramps[i] / fdiff[i]
|
||||
upper = ramps[i + 2] / fdiff[i + 1]
|
||||
|
||||
# .. then intersect them with each other and zero
|
||||
weights[i] = paddle.maximum(
|
||||
paddle.zeros_like(lower), paddle.minimum(lower, upper)
|
||||
)
|
||||
|
||||
# Slaney-style mel is scaled to be approx constant energy per channel
|
||||
if norm == 'slaney':
|
||||
enorm = 2.0 / (mel_f[2 : n_mels + 2] - mel_f[:n_mels])
|
||||
weights *= enorm.unsqueeze(1)
|
||||
elif isinstance(norm, (int, float)):
|
||||
weights = paddle.nn.functional.normalize(weights, p=norm, axis=-1)
|
||||
|
||||
return weights
|
||||
|
||||
|
||||
def power_to_db(
|
||||
spect: Tensor,
|
||||
ref_value: float = 1.0,
|
||||
amin: float = 1e-10,
|
||||
top_db: float | None = 80.0,
|
||||
) -> Tensor:
|
||||
"""Convert a power spectrogram (amplitude squared) to decibel (dB) units. The function computes the scaling `10 * log10(x / ref)` in a numerically stable way.
|
||||
|
||||
Args:
|
||||
spect (Tensor): STFT power spectrogram.
|
||||
ref_value (float, optional): The reference value. If smaller than 1.0, the db level of the signal will be pulled up accordingly. Otherwise, the db level is pushed down. Defaults to 1.0.
|
||||
amin (float, optional): Minimum threshold. Defaults to 1e-10.
|
||||
top_db (Optional[float], optional): Threshold the output at `top_db` below the peak. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Tensor: Power spectrogram in db scale.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> val = 3.0
|
||||
>>> decibel_paddle = paddle.audio.functional.power_to_db(paddle.to_tensor(val))
|
||||
"""
|
||||
if amin <= 0:
|
||||
raise Exception("amin must be strictly positive")
|
||||
|
||||
if ref_value <= 0:
|
||||
raise Exception("ref_value must be strictly positive")
|
||||
|
||||
ones = paddle.ones_like(spect)
|
||||
log_spec = 10.0 * paddle.log10(paddle.maximum(ones * amin, spect))
|
||||
log_spec -= 10.0 * math.log10(max(ref_value, amin))
|
||||
|
||||
if top_db is not None:
|
||||
if top_db < 0:
|
||||
raise Exception("top_db must be non-negative")
|
||||
log_spec = paddle.maximum(log_spec, ones * (log_spec.max() - top_db))
|
||||
|
||||
return log_spec
|
||||
|
||||
|
||||
def create_dct(
|
||||
n_mfcc: int,
|
||||
n_mels: int,
|
||||
norm: Literal['ortho'] | None = 'ortho',
|
||||
dtype: str = 'float32',
|
||||
) -> Tensor:
|
||||
"""Create a discrete cosine transform(DCT) matrix.
|
||||
|
||||
Args:
|
||||
n_mfcc (int): Number of mel frequency cepstral coefficients.
|
||||
n_mels (int): Number of mel filterbanks.
|
||||
norm (Optional[str], optional): Normalization type. Defaults to 'ortho'.
|
||||
dtype (str, optional): The data type of the return matrix. Defaults to 'float32'.
|
||||
|
||||
Returns:
|
||||
Tensor: The DCT matrix with shape `(n_mels, n_mfcc)`.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> n_mfcc = 23
|
||||
>>> n_mels = 257
|
||||
>>> dct = paddle.audio.functional.create_dct(n_mfcc, n_mels)
|
||||
"""
|
||||
n = paddle.arange(n_mels, dtype=dtype)
|
||||
k = paddle.arange(n_mfcc, dtype=dtype).unsqueeze(1)
|
||||
dct = paddle.cos(
|
||||
math.pi / float(n_mels) * (n + 0.5) * k
|
||||
) # size (n_mfcc, n_mels)
|
||||
if norm is None:
|
||||
dct *= 2.0
|
||||
else:
|
||||
assert norm == "ortho"
|
||||
dct[0] *= 1.0 / math.sqrt(2.0)
|
||||
dct *= math.sqrt(2.0 / float(n_mels))
|
||||
return dct.T
|
||||
|
||||
|
||||
def _get_sinc_resample_kernel(
|
||||
orig_freq: int,
|
||||
new_freq: int,
|
||||
gcd: int,
|
||||
lowpass_filter_width: int = 6,
|
||||
rolloff: float = 0.99,
|
||||
resampling_method: Literal[
|
||||
"sinc_interp_hann", "sinc_interp_kaiser"
|
||||
] = "sinc_interp_hann",
|
||||
beta: float | None = None,
|
||||
dtype: paddle.dtype | None = None,
|
||||
):
|
||||
"""
|
||||
Generate the sinc interpolation kernel for resampling.
|
||||
|
||||
This internal function computes the resampling kernel based on the sinc
|
||||
interpolation formula with windowing. The kernel is used by
|
||||
_apply_sinc_resample_kernel to perform the actual resampling.
|
||||
|
||||
Args:
|
||||
orig_freq (int): Original sampling frequency.
|
||||
new_freq (int): Target sampling frequency.
|
||||
gcd (int): Greatest common divisor of orig_freq and new_freq.
|
||||
lowpass_filter_width (int, optional): Controls the sharpness of the filter,
|
||||
larger value means sharper but less efficient. Default: 6.
|
||||
rolloff (float, optional): Roll-off frequency as a fraction of the Nyquist.
|
||||
Lower values reduce anti-aliasing but also attenuate high frequencies.
|
||||
Default: 0.99.
|
||||
resampling_method (str, optional): Window method for filter design.
|
||||
Options: ["sinc_interp_hann", "sinc_interp_kaiser"]. Default: "sinc_interp_hann".
|
||||
beta (float, optional): Shape parameter for Kaiser window. Required only
|
||||
when resampling_method="sinc_interp_kaiser". Default: None.
|
||||
dtype (paddle.dtype, optional): Data type for kernel computation.
|
||||
If None, uses float64 for computation and converts to float32 for output.
|
||||
Default: None.
|
||||
|
||||
Returns:
|
||||
tuple: (kernel, width)
|
||||
- kernel (Tensor): Resampling kernel of shape (1, 1, kernel_width)
|
||||
- width (int): Half-width of the filter in terms of input samples
|
||||
|
||||
Raises:
|
||||
Exception: If frequencies are not integers.
|
||||
ValueError: If resampling_method is invalid or lowpass_filter_width <= 0.
|
||||
"""
|
||||
if not (int(orig_freq) == orig_freq and int(new_freq) == new_freq):
|
||||
raise ValueError(
|
||||
"Frequencies must be of integer type to ensure quality resampling computation. "
|
||||
"To work around this, manually convert both frequencies to integer values "
|
||||
"that maintain their resampling rate ratio before passing them into the function. "
|
||||
"Example: To downsample a 44100 hz waveform by a factor of 8, use "
|
||||
"`orig_freq=8` and `new_freq=1` instead of `orig_freq=44100` and `new_freq=5512.5`. "
|
||||
)
|
||||
|
||||
if resampling_method not in ["sinc_interp_hann", "sinc_interp_kaiser"]:
|
||||
raise ValueError(f"Invalid resampling method: {resampling_method}")
|
||||
|
||||
orig_freq = int(orig_freq) // gcd
|
||||
new_freq = int(new_freq) // gcd
|
||||
|
||||
if lowpass_filter_width <= 0:
|
||||
raise ValueError("Low pass filter width should be positive.")
|
||||
base_freq = min(orig_freq, new_freq)
|
||||
|
||||
# Perform antialiasing filtering by removing the highest frequencies.
|
||||
base_freq *= rolloff
|
||||
|
||||
# Calculate filter width based on lowpass_filter_width and frequency ratio
|
||||
width = math.ceil(lowpass_filter_width * orig_freq / base_freq)
|
||||
idx_dtype = dtype if dtype is not None else paddle.float64
|
||||
|
||||
idx = (
|
||||
paddle.arange(-width, width + orig_freq, dtype=idx_dtype)[None, None]
|
||||
/ orig_freq
|
||||
)
|
||||
|
||||
t = (
|
||||
paddle.arange(0, -new_freq, -1, dtype=dtype)[:, None, None] / new_freq
|
||||
+ idx
|
||||
)
|
||||
t *= base_freq
|
||||
t = t.clip_(-lowpass_filter_width, lowpass_filter_width)
|
||||
|
||||
# we do not use built-in paddle windows here as we need to evaluate the window
|
||||
# at specific positions, not over a regular grid.
|
||||
if resampling_method == "sinc_interp_hann":
|
||||
window = paddle.cos(t * math.pi / lowpass_filter_width / 2) ** 2
|
||||
else:
|
||||
# sinc_interp_kaiser
|
||||
if beta is None:
|
||||
beta = 14.769656459379492
|
||||
beta_tensor = paddle.to_tensor(float(beta))
|
||||
window = paddle.i0(
|
||||
beta_tensor * paddle.sqrt(1 - (t / lowpass_filter_width) ** 2),
|
||||
) / paddle.i0(beta_tensor)
|
||||
|
||||
t *= math.pi
|
||||
|
||||
scale = base_freq / orig_freq
|
||||
kernels = paddle.where(
|
||||
t == 0, paddle.to_tensor(1.0).cast(t.dtype), t.sin() / t
|
||||
)
|
||||
kernels *= window * scale
|
||||
|
||||
if dtype is None: # pragma: no cover
|
||||
kernels = kernels.cast(paddle.float32)
|
||||
|
||||
return kernels, width
|
||||
|
||||
|
||||
def _apply_sinc_resample_kernel(
|
||||
waveform: Tensor,
|
||||
orig_freq: int,
|
||||
new_freq: int,
|
||||
gcd: int,
|
||||
kernel: Tensor,
|
||||
width: int,
|
||||
):
|
||||
"""
|
||||
Apply sinc interpolation resampling using precomputed kernel.
|
||||
|
||||
This internal function performs the actual resampling operation using the
|
||||
kernel generated by _get_sinc_resample_kernel. It handles batch processing
|
||||
and ensures correct output length.
|
||||
|
||||
Args:
|
||||
waveform (Tensor): Input waveform of shape (..., time). Must be floating point.
|
||||
orig_freq (int): Original sampling frequency.
|
||||
new_freq (int): Target sampling frequency.
|
||||
gcd (int): Greatest common divisor of orig_freq and new_freq.
|
||||
kernel (Tensor): Resampling kernel from _get_sinc_resample_kernel.
|
||||
width (int): Half-width of the filter from _get_sinc_resample_kernel.
|
||||
|
||||
Returns:
|
||||
Tensor: Resampled waveform of shape (..., new_time).
|
||||
|
||||
"""
|
||||
|
||||
orig_freq = int(orig_freq) // gcd
|
||||
new_freq = int(new_freq) // gcd
|
||||
|
||||
# pack batch
|
||||
shape = waveform.shape
|
||||
waveform = waveform.reshape([-1, shape[-1]])
|
||||
|
||||
num_wavs, length = waveform.shape
|
||||
waveform = paddle.nn.functional.pad(waveform, (width, width + orig_freq))
|
||||
resampled = paddle.nn.functional.conv1d(
|
||||
waveform[:, None], kernel, stride=orig_freq
|
||||
)
|
||||
resampled = resampled.transpose([0, 2, 1]).reshape((num_wavs, -1))
|
||||
target_length = paddle.ceil(
|
||||
paddle.to_tensor(new_freq * length / orig_freq)
|
||||
).astype(paddle.int64)
|
||||
resampled = resampled[..., :target_length]
|
||||
|
||||
# unpack batch
|
||||
resampled = resampled.reshape(shape[:-1] + resampled.shape[-1:])
|
||||
return resampled
|
||||
|
||||
|
||||
def resample(
|
||||
waveform: Tensor,
|
||||
orig_freq: int,
|
||||
new_freq: int,
|
||||
lowpass_filter_width: int = 6,
|
||||
rolloff: float = 0.99,
|
||||
resampling_method: Literal[
|
||||
"sinc_interp_hann", "sinc_interp_kaiser"
|
||||
] = "sinc_interp_hann",
|
||||
beta: float | None = None,
|
||||
) -> Tensor:
|
||||
"""
|
||||
Resample the waveform from orig_freq to new_freq using bandlimited interpolation.
|
||||
|
||||
This function implements resampling through sinc interpolation with windowing.
|
||||
It first computes a resampling kernel based on the specified parameters, then
|
||||
applies it to the input waveform using convolution. The algorithm handles both
|
||||
upsampling and downsampling while minimizing aliasing artifacts.
|
||||
|
||||
Args:
|
||||
waveform (Tensor): The input signal of dimension (..., time). Must be
|
||||
floating point type (float32 or float64).
|
||||
orig_freq (int): The original frequency of the signal. Must be positive.
|
||||
new_freq (int): The desired target frequency. Must be positive.
|
||||
lowpass_filter_width (int, optional): Controls the sharpness of the filter.
|
||||
Larger values give sharper filtering but are less efficient.
|
||||
Default: 6.
|
||||
rolloff (float, optional): The roll-off frequency of the filter as a fraction
|
||||
of the Nyquist frequency. Lower values reduce anti-aliasing but also
|
||||
attenuate some high frequencies. Default: 0.99.
|
||||
resampling_method (str, optional): The windowing method to use for filter
|
||||
design. Options: "sinc_interp_hann" (Hann window) or "sinc_interp_kaiser"
|
||||
(Kaiser window). Default: "sinc_interp_hann".
|
||||
beta (float, optional): Shape parameter for the Kaiser window. Required only
|
||||
when resampling_method="sinc_interp_kaiser". If not provided for Kaiser,
|
||||
a default value of 14.769656459379492 is used. Default: None.
|
||||
|
||||
Returns:
|
||||
Tensor: The waveform resampled to new_freq, with dimension (..., new_time).
|
||||
|
||||
Raises:
|
||||
ValueError: If orig_freq or new_freq are not positive.
|
||||
Exception: If frequencies are not integers (see note below).
|
||||
TypeError: If waveform is not floating point.
|
||||
|
||||
Note:
|
||||
- orig_freq and new_freq must be integers. For non-integer frequencies,
|
||||
convert them to integers while maintaining the ratio.
|
||||
- For repeated resampling with same parameters, use
|
||||
:class:`paddle.audio.transforms.Resample` for better efficiency.
|
||||
- Uses windowed sinc interpolation for high-quality audio resampling.
|
||||
- This function does not support ONNX export now.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.audio.functional import resample
|
||||
|
||||
>>> # Create a sample waveform (1 channel, 1000 samples at 16000 Hz)
|
||||
>>> waveform = paddle.randn([1, 1000])
|
||||
|
||||
>>> # Downsample from 16000 Hz to 8000 Hz
|
||||
>>> resampled = resample(waveform, 16000, 8000)
|
||||
>>> print(resampled.shape)
|
||||
paddle.Size([1, 500])
|
||||
|
||||
>>> # Upsample from 16000 Hz to 48000 Hz with custom filter width
|
||||
>>> resampled = resample(waveform, 16000, 48000, lowpass_filter_width=12)
|
||||
>>> print(resampled.shape)
|
||||
paddle.Size([1, 3000])
|
||||
|
||||
>>> # Use Kaiser window resampling
|
||||
>>> resampled = resample(waveform, 16000, 8000, resampling_method="sinc_interp_kaiser", beta=12.0)
|
||||
>>> print(resampled.shape)
|
||||
paddle.Size([1, 500])
|
||||
|
||||
>>> # Batch processing: multiple waveforms
|
||||
>>> batch_waveforms = paddle.randn([4, 1, 1000]) # [batch, channels, time]
|
||||
>>> resampled_batch = resample(batch_waveforms, 16000, 8000)
|
||||
>>> print(resampled_batch.shape)
|
||||
paddle.Size([4, 1, 500])
|
||||
"""
|
||||
if orig_freq <= 0.0 or new_freq <= 0.0:
|
||||
raise ValueError(
|
||||
"Original frequency and desired frequency should be positive integers"
|
||||
)
|
||||
if not waveform.is_floating_point():
|
||||
raise TypeError(
|
||||
f"Expected floating point type for waveform tensor, but received {waveform.dtype}."
|
||||
)
|
||||
|
||||
if orig_freq == new_freq:
|
||||
return waveform
|
||||
|
||||
gcd = math.gcd(int(orig_freq), int(new_freq))
|
||||
|
||||
kernel, width = _get_sinc_resample_kernel(
|
||||
orig_freq,
|
||||
new_freq,
|
||||
gcd,
|
||||
lowpass_filter_width,
|
||||
rolloff,
|
||||
resampling_method,
|
||||
beta,
|
||||
waveform.dtype,
|
||||
)
|
||||
resampled = _apply_sinc_resample_kernel(
|
||||
waveform, orig_freq, new_freq, gcd, kernel, width
|
||||
)
|
||||
return resampled
|
||||
@@ -0,0 +1,731 @@
|
||||
# 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
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
from paddle._typing import PlaceLike
|
||||
|
||||
from ..features.layers import _WindowLiteral
|
||||
|
||||
from paddle.base.framework import (
|
||||
_current_expected_place,
|
||||
_get_paddle_place,
|
||||
_to_pinned_place,
|
||||
in_dynamic_or_pir_mode,
|
||||
)
|
||||
|
||||
|
||||
class WindowFunctionRegister:
|
||||
def __init__(self):
|
||||
self._functions_dict = {}
|
||||
|
||||
def register(self, func=None):
|
||||
def add_subfunction(func):
|
||||
name = func.__name__
|
||||
self._functions_dict[name] = func
|
||||
return func
|
||||
|
||||
return add_subfunction
|
||||
|
||||
def get(self, name):
|
||||
return self._functions_dict[name]
|
||||
|
||||
|
||||
window_function_register = WindowFunctionRegister()
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _cat(x: list[Tensor], data_type: str) -> Tensor:
|
||||
l = []
|
||||
for t in x:
|
||||
if np.isscalar(t) and not isinstance(t, str):
|
||||
l.append(paddle.to_tensor([t], data_type))
|
||||
else:
|
||||
l.append(paddle.to_tensor(t, data_type))
|
||||
return paddle.concat(l)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _bartlett(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""
|
||||
Computes the Bartlett window.
|
||||
This function is consistent with scipy.signal.windows.bartlett().
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
n = paddle.arange(0, M, dtype=dtype)
|
||||
M = paddle.to_tensor(M, dtype=dtype)
|
||||
w = paddle.where(
|
||||
paddle.less_equal(n, (M - 1) / 2.0),
|
||||
2.0 * n / (M - 1),
|
||||
2.0 - 2.0 * n / (M - 1),
|
||||
)
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _kaiser(
|
||||
M: int, beta: float, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute the Kaiser window.
|
||||
This function is consistent with scipy.signal.windows.kaiser().
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
beta = paddle.to_tensor(beta, dtype=dtype)
|
||||
|
||||
n = paddle.arange(0, M, dtype=dtype)
|
||||
M = paddle.to_tensor(M, dtype=dtype)
|
||||
alpha = (M - 1) / 2.0
|
||||
w = paddle.i0(
|
||||
beta * paddle.sqrt(1 - ((n - alpha) / alpha) ** 2.0)
|
||||
) / paddle.i0(beta)
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _nuttall(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Nuttall window.
|
||||
This function is consistent with scipy.signal.windows.nuttall().
|
||||
"""
|
||||
a = paddle.to_tensor(
|
||||
[0.3635819, 0.4891775, 0.1365995, 0.0106411], dtype=dtype
|
||||
)
|
||||
return _general_cosine(M, a=a, sym=sym, dtype=dtype)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _acosh(x: Tensor | float) -> Tensor:
|
||||
if isinstance(x, float):
|
||||
return math.log(x + math.sqrt(x**2 - 1))
|
||||
return paddle.log(x + paddle.sqrt(paddle.square(x) - 1))
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _extend(M: int, sym: bool) -> bool:
|
||||
"""Extend window by 1 sample if needed for DFT-even symmetry."""
|
||||
if not sym:
|
||||
return M + 1, True
|
||||
else:
|
||||
return M, False
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _len_guards(M: int) -> bool:
|
||||
"""Handle small or incorrect window lengths."""
|
||||
if int(M) != M or M < 0:
|
||||
raise ValueError('Window length M must be a non-negative integer')
|
||||
|
||||
return M <= 1
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _truncate(w: Tensor, needed: bool) -> Tensor:
|
||||
"""Truncate window by 1 sample if needed for DFT-even symmetry."""
|
||||
if needed:
|
||||
return w[:-1]
|
||||
else:
|
||||
return w
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _general_gaussian(
|
||||
M: int, p, sig, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute a window with a generalized Gaussian shape.
|
||||
This function is consistent with scipy.signal.windows.general_gaussian().
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
n = paddle.arange(0, M, dtype=dtype) - (M - 1.0) / 2.0
|
||||
w = paddle.exp(-0.5 * paddle.abs(n / sig) ** (2 * p))
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _general_cosine(
|
||||
M: int, a: list[float], sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute a generic weighted sum of cosine terms window.
|
||||
This function is consistent with scipy.signal.windows.general_cosine().
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
fac = paddle.linspace(-math.pi, math.pi, M, dtype=dtype)
|
||||
w = paddle.zeros((M,), dtype=dtype)
|
||||
for k in range(len(a)):
|
||||
w += a[k] * paddle.cos(k * fac)
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _general_hamming(
|
||||
M: int, alpha: float, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute a generalized Hamming window.
|
||||
This function is consistent with scipy.signal.windows.general_hamming()
|
||||
"""
|
||||
return _general_cosine(M, [alpha, 1.0 - alpha], sym, dtype=dtype)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _taylor(
|
||||
M: int, nbar=4, sll=30, norm=True, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute a Taylor window.
|
||||
The Taylor window taper function approximates the Dolph-Chebyshev window's
|
||||
constant sidelobe level for a parameterized number of near-in sidelobes.
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
# Original text uses a negative sidelobe level parameter and then negates
|
||||
# it in the calculation of B. To keep consistent with other methods we
|
||||
# assume the sidelobe level parameter to be positive.
|
||||
B = 10 ** (sll / 20)
|
||||
A = _acosh(B) / math.pi
|
||||
s2 = nbar**2 / (A**2 + (nbar - 0.5) ** 2)
|
||||
ma = paddle.arange(1, nbar, dtype=dtype)
|
||||
|
||||
Fm = paddle.empty((nbar - 1,), dtype=dtype)
|
||||
signs = paddle.empty_like(ma)
|
||||
signs[::2] = 1
|
||||
signs[1::2] = -1
|
||||
m2 = ma * ma
|
||||
for mi in range(len(ma)):
|
||||
number = signs[mi] * paddle.prod(
|
||||
1 - m2[mi] / s2 / (A**2 + (ma - 0.5) ** 2)
|
||||
)
|
||||
if mi == 0:
|
||||
denom = 2 * paddle.prod(1 - m2[mi] / m2[mi + 1 :])
|
||||
elif mi == len(ma) - 1:
|
||||
denom = 2 * paddle.prod(1 - m2[mi] / m2[:mi])
|
||||
else:
|
||||
denom = (
|
||||
2
|
||||
* paddle.prod(1 - m2[mi] / m2[:mi])
|
||||
* paddle.prod(1 - m2[mi] / m2[mi + 1 :])
|
||||
)
|
||||
|
||||
Fm[mi] = number / denom
|
||||
|
||||
def W(n):
|
||||
return 1 + 2 * paddle.matmul(
|
||||
Fm.unsqueeze(0),
|
||||
paddle.cos(2 * math.pi * ma.unsqueeze(1) * (n - M / 2.0 + 0.5) / M),
|
||||
)
|
||||
|
||||
w = W(paddle.arange(0, M, dtype=dtype))
|
||||
|
||||
# normalize (Note that this is not described in the original text [1])
|
||||
if norm:
|
||||
scale = 1.0 / W((M - 1) / 2)
|
||||
w *= scale
|
||||
w = w.squeeze()
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _hamming(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Compute a Hamming window.
|
||||
The Hamming window is a taper formed by using a raised cosine with
|
||||
non-zero endpoints, optimized to minimize the nearest side lobe.
|
||||
"""
|
||||
return _general_hamming(M, 0.54, sym, dtype=dtype)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _hann(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Compute a Hann window.
|
||||
The Hann window is a taper formed by using a raised cosine or sine-squared
|
||||
with ends that touch zero.
|
||||
"""
|
||||
return _general_hamming(M, 0.5, sym, dtype=dtype)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _tukey(
|
||||
M: int, alpha=0.5, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute a Tukey window.
|
||||
The Tukey window is also known as a tapered cosine window.
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
|
||||
if alpha <= 0:
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
elif alpha >= 1.0:
|
||||
return _hann(M, sym=sym)
|
||||
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
n = paddle.arange(0, M, dtype=dtype)
|
||||
width = int(alpha * (M - 1) / 2.0)
|
||||
n1 = n[0 : width + 1]
|
||||
n2 = n[width + 1 : M - width - 1]
|
||||
n3 = n[M - width - 1 :]
|
||||
|
||||
w1 = 0.5 * (1 + paddle.cos(math.pi * (-1 + 2.0 * n1 / alpha / (M - 1))))
|
||||
w2 = paddle.ones(n2.shape, dtype=dtype)
|
||||
w3 = 0.5 * (
|
||||
1
|
||||
+ paddle.cos(math.pi * (-2.0 / alpha + 1 + 2.0 * n3 / alpha / (M - 1)))
|
||||
)
|
||||
w = paddle.concat([w1, w2, w3])
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _gaussian(
|
||||
M: int, std: float, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute a Gaussian window.
|
||||
The Gaussian widows has a Gaussian shape defined by the standard deviation(std).
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
n = paddle.arange(0, M, dtype=dtype) - (M - 1.0) / 2.0
|
||||
sig2 = 2 * std * std
|
||||
w = paddle.exp(-(n**2) / sig2)
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _exponential(
|
||||
M: int, center=None, tau=1.0, sym: bool = True, dtype: str = 'float64'
|
||||
) -> Tensor:
|
||||
"""Compute an exponential (or Poisson) window."""
|
||||
if sym and center is not None:
|
||||
raise ValueError("If sym==True, center must be None.")
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
if center is None:
|
||||
center = (M - 1) / 2
|
||||
|
||||
n = paddle.arange(0, M, dtype=dtype)
|
||||
w = paddle.exp(-paddle.abs(n - center) / tau)
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _triang(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Compute a triangular window."""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
n = paddle.arange(1, (M + 1) // 2 + 1, dtype=dtype)
|
||||
if M % 2 == 0:
|
||||
w = (2 * n - 1.0) / M
|
||||
w = paddle.concat([w, w[::-1]])
|
||||
else:
|
||||
w = 2 * n / (M + 1.0)
|
||||
w = paddle.concat([w, w[-2::-1]])
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _bohman(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Compute a Bohman window.
|
||||
The Bohman window is the autocorrelation of a cosine window.
|
||||
"""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
|
||||
fac = paddle.abs(paddle.linspace(-1, 1, M, dtype=dtype)[1:-1])
|
||||
w = (1 - fac) * paddle.cos(math.pi * fac) + 1.0 / math.pi * paddle.sin(
|
||||
math.pi * fac
|
||||
)
|
||||
w = _cat([0, w, 0], dtype)
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _blackman(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Compute a Blackman window.
|
||||
The Blackman window is a taper formed by using the first three terms of
|
||||
a summation of cosines. It was designed to have close to the minimal
|
||||
leakage possible. It is close to optimal, only slightly worse than a
|
||||
Kaiser window.
|
||||
"""
|
||||
return _general_cosine(M, [0.42, 0.50, 0.08], sym, dtype=dtype)
|
||||
|
||||
|
||||
@window_function_register.register()
|
||||
def _cosine(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
|
||||
"""Compute a window with a simple cosine shape."""
|
||||
if _len_guards(M):
|
||||
return paddle.ones((M,), dtype=dtype)
|
||||
M, needs_trunc = _extend(M, sym)
|
||||
w = paddle.sin(math.pi / M * (paddle.arange(0, M, dtype=dtype) + 0.5))
|
||||
|
||||
return _truncate(w, needs_trunc)
|
||||
|
||||
|
||||
def get_window(
|
||||
window: _WindowLiteral | tuple[_WindowLiteral, float],
|
||||
win_length: int,
|
||||
fftbins: bool = True,
|
||||
dtype: str | None = 'float64',
|
||||
) -> Tensor:
|
||||
"""Return a window of a given length and type.
|
||||
|
||||
Args:
|
||||
window (Union[str, Tuple[str, float]]): The window function applied to the signal before the Fourier transform. Supported window functions: 'hamming', 'hann', 'gaussian', 'general_gaussian', 'exponential', 'triang', 'bohman', 'blackman', 'cosine', 'tukey', 'taylor', 'bartlett', 'kaiser', 'nuttall'.
|
||||
win_length (int): Number of samples.
|
||||
fftbins (bool, optional): If True, create a "periodic" window. Otherwise, create a "symmetric" window, for use in filter design. Defaults to True.
|
||||
dtype (str, optional): The data type of the return window. Defaults to 'float64'.
|
||||
|
||||
Returns:
|
||||
Tensor: The window represented as a tensor.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> n_fft = 512
|
||||
>>> cosine_window = paddle.audio.functional.get_window('cosine', n_fft)
|
||||
|
||||
>>> std = 7
|
||||
>>> gaussian_window = paddle.audio.functional.get_window(('gaussian', std), n_fft)
|
||||
"""
|
||||
if dtype is None:
|
||||
dtype = 'float32'
|
||||
sym = not fftbins
|
||||
args = ()
|
||||
if isinstance(window, tuple):
|
||||
winstr = window[0]
|
||||
if len(window) > 1:
|
||||
args = window[1:]
|
||||
elif isinstance(window, str):
|
||||
if window in ['gaussian', 'exponential', 'kaiser']:
|
||||
raise ValueError(
|
||||
"The '" + window + "' window needs one or "
|
||||
"more parameters -- pass a tuple."
|
||||
)
|
||||
else:
|
||||
winstr = window
|
||||
else:
|
||||
raise ValueError(f"{type(window)} as window type is not supported.")
|
||||
|
||||
try:
|
||||
winfunc = window_function_register.get('_' + winstr)
|
||||
except KeyError as e:
|
||||
raise ValueError("Unknown window type.") from e
|
||||
params = (win_length, *args)
|
||||
kwargs = {'sym': sym}
|
||||
return winfunc(*params, dtype=dtype, **kwargs)
|
||||
|
||||
|
||||
def _apply_window_postprocess(
|
||||
w: Tensor,
|
||||
*,
|
||||
layout: str | None = None,
|
||||
device: PlaceLike | None = None,
|
||||
pin_memory: bool = False,
|
||||
requires_grad: bool = False,
|
||||
) -> Tensor:
|
||||
if layout not in (None, 'strided'):
|
||||
raise RuntimeError(
|
||||
"Window functions only support layout='strided' or None"
|
||||
)
|
||||
if layout is not None:
|
||||
warnings.warn("layout only supports 'strided' in Paddle; ignored")
|
||||
|
||||
if in_dynamic_or_pir_mode():
|
||||
device = (
|
||||
_get_paddle_place(device)
|
||||
if device is not None
|
||||
else _current_expected_place()
|
||||
)
|
||||
if pin_memory and paddle.in_dynamic_mode() and device is not None:
|
||||
device = _to_pinned_place(device)
|
||||
w = w.to(device=device)
|
||||
if pin_memory and paddle.in_dynamic_mode():
|
||||
w = w.pin_memory()
|
||||
if requires_grad is True:
|
||||
w.stop_gradient = False
|
||||
return w
|
||||
|
||||
|
||||
def hamming_window(
|
||||
window_length: int,
|
||||
periodic: bool = True,
|
||||
alpha: float = 0.54,
|
||||
beta: float = 0.46,
|
||||
*,
|
||||
dtype: str = 'float32',
|
||||
layout: str | None = None,
|
||||
device: PlaceLike | None = None,
|
||||
pin_memory: bool = False,
|
||||
requires_grad: bool = False,
|
||||
):
|
||||
"""
|
||||
Compute a generalized Hamming window.
|
||||
|
||||
Args:
|
||||
window_length (int): The size of the returned window. Must be positive.
|
||||
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
|
||||
alpha (float, optional): The coefficient α in the equation above. Defaults to 0.54.
|
||||
beta (float, optional): The coefficient β in the equation above. Defaults to 0.46.
|
||||
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
|
||||
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
|
||||
device(PlaceLike|None, optional): The desired device of returned tensor.
|
||||
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
|
||||
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
|
||||
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
|
||||
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
|
||||
|
||||
Returns:
|
||||
Tensor: A 1-D tensor of shape `(window_length,)` containing the Hamming window.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> win = paddle.hamming_window(400, requires_grad=True)
|
||||
>>> win = paddle.hamming_window(256, alpha=0.5, beta=0.5)
|
||||
"""
|
||||
w0 = get_window('hamming', window_length, fftbins=periodic, dtype=dtype)
|
||||
alpha0, beta0 = 0.54, 0.46
|
||||
B = beta / beta0
|
||||
A = alpha - B * alpha0
|
||||
w = A + B * w0
|
||||
return _apply_window_postprocess(
|
||||
w,
|
||||
layout=layout,
|
||||
device=device,
|
||||
pin_memory=pin_memory,
|
||||
requires_grad=requires_grad,
|
||||
)
|
||||
|
||||
|
||||
def hann_window(
|
||||
window_length: int,
|
||||
periodic: bool = True,
|
||||
*,
|
||||
dtype: str = 'float32',
|
||||
layout: str | None = None,
|
||||
device: PlaceLike | None = None,
|
||||
pin_memory: bool = False,
|
||||
requires_grad: bool = False,
|
||||
):
|
||||
"""
|
||||
Compute a Hann window.
|
||||
|
||||
Args:
|
||||
window_length (int): The size of the returned window. Must be positive.
|
||||
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
|
||||
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
|
||||
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
|
||||
device(PlaceLike|None, optional): The desired device of returned tensor.
|
||||
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
|
||||
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
|
||||
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
|
||||
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
|
||||
|
||||
Returns:
|
||||
Tensor: A 1-D tensor of shape `(window_length,)` containing the Hann window.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> win = paddle.hann_window(512)
|
||||
>>> win = paddle.hann_window(512, requires_grad=True)
|
||||
"""
|
||||
w = get_window('hann', window_length, fftbins=periodic, dtype=dtype)
|
||||
return _apply_window_postprocess(
|
||||
w,
|
||||
layout=layout,
|
||||
device=device,
|
||||
pin_memory=pin_memory,
|
||||
requires_grad=requires_grad,
|
||||
)
|
||||
|
||||
|
||||
def kaiser_window(
|
||||
window_length: int,
|
||||
periodic: bool = True,
|
||||
beta: float = 12.0,
|
||||
*,
|
||||
dtype: str | None = 'float32',
|
||||
layout: str | None = None,
|
||||
device: PlaceLike | None = None,
|
||||
pin_memory: bool = False,
|
||||
requires_grad: bool = False,
|
||||
out: Tensor | None = None,
|
||||
):
|
||||
"""
|
||||
Compute a Kaiser window.
|
||||
|
||||
Args:
|
||||
window_length (int): The size of the returned window. Must be positive.
|
||||
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
|
||||
beta (float, optional): Shape parameter for the window. Defaults to 12.0.
|
||||
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
|
||||
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
|
||||
device(PlaceLike|None, optional): The desired device of returned tensor.
|
||||
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
|
||||
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
|
||||
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
|
||||
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
|
||||
out(Tensor|None, optional): The output tensor. Default: None.
|
||||
|
||||
Returns:
|
||||
Tensor: A 1-D tensor of shape `(window_length,)` containing the Kaiser window.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> win = paddle.kaiser_window(400, beta=8.6)
|
||||
>>> win = paddle.kaiser_window(400, requires_grad=True)
|
||||
"""
|
||||
w = get_window(
|
||||
('kaiser', beta), window_length, fftbins=periodic, dtype=dtype
|
||||
)
|
||||
w = _apply_window_postprocess(
|
||||
w,
|
||||
layout=layout,
|
||||
device=device,
|
||||
pin_memory=pin_memory,
|
||||
requires_grad=requires_grad,
|
||||
)
|
||||
return paddle.assign(w, out) if out is not None else w
|
||||
|
||||
|
||||
def blackman_window(
|
||||
window_length: int,
|
||||
periodic: bool = True,
|
||||
*,
|
||||
dtype: str = 'float32',
|
||||
layout: str | None = None,
|
||||
device: PlaceLike | None = None,
|
||||
pin_memory: bool = False,
|
||||
requires_grad: bool = False,
|
||||
):
|
||||
"""
|
||||
Compute a Blackman window.
|
||||
|
||||
Args:
|
||||
window_length (int): The size of the returned window. Must be positive.
|
||||
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
|
||||
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
|
||||
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
|
||||
device(PlaceLike|None, optional): The desired device of returned tensor.
|
||||
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
|
||||
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
|
||||
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
|
||||
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
|
||||
|
||||
Returns:
|
||||
Tensor: A 1-D tensor of shape `(window_length,)` containing the Blackman window.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> win = paddle.blackman_window(256)
|
||||
>>> win = paddle.blackman_window(256, requires_grad=True)
|
||||
"""
|
||||
w = get_window('blackman', window_length, fftbins=periodic, dtype=dtype)
|
||||
return _apply_window_postprocess(
|
||||
w,
|
||||
layout=layout,
|
||||
device=device,
|
||||
pin_memory=pin_memory,
|
||||
requires_grad=requires_grad,
|
||||
)
|
||||
|
||||
|
||||
def bartlett_window(
|
||||
window_length: int,
|
||||
periodic: bool = True,
|
||||
*,
|
||||
dtype: str = 'float32',
|
||||
layout: str | None = None,
|
||||
device: PlaceLike | None = None,
|
||||
pin_memory: bool = False,
|
||||
requires_grad: bool = False,
|
||||
):
|
||||
"""
|
||||
Compute a Bartlett window.
|
||||
|
||||
Args:
|
||||
window_length (int): The size of the returned window. Must be positive.
|
||||
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
|
||||
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
|
||||
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
|
||||
device(PlaceLike|None, optional): The desired device of returned tensor.
|
||||
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
|
||||
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
|
||||
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
|
||||
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
|
||||
|
||||
Returns:
|
||||
Tensor: A 1-D tensor of shape `(window_length,)` containing the Bartlett window.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> n_fft = 512
|
||||
>>> win = paddle.bartlett_window(n_fft)
|
||||
|
||||
>>> win = paddle.bartlett_window(n_fft, requires_grad=True)
|
||||
"""
|
||||
w = get_window('bartlett', window_length, fftbins=periodic, dtype=dtype)
|
||||
return _apply_window_postprocess(
|
||||
w,
|
||||
layout=layout,
|
||||
device=device,
|
||||
pin_memory=pin_memory,
|
||||
requires_grad=requires_grad,
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
# 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 ..base.dygraph.base import ( # noqa: F401
|
||||
enable_grad,
|
||||
grad,
|
||||
inference_mode,
|
||||
is_grad_enabled,
|
||||
no_grad_ as no_grad,
|
||||
set_grad_enabled,
|
||||
)
|
||||
from . import ( # noqa: F401
|
||||
backward_mode,
|
||||
function,
|
||||
grad_mode,
|
||||
ir_backward,
|
||||
)
|
||||
from .autograd import hessian, jacobian
|
||||
from .backward_mode import backward
|
||||
from .py_layer import PyLayer, PyLayerContext
|
||||
from .saved_tensors_hooks import saved_tensors_hooks
|
||||
|
||||
Function = PyLayer
|
||||
|
||||
__all__ = [
|
||||
'jacobian',
|
||||
'hessian',
|
||||
'backward',
|
||||
'PyLayer',
|
||||
'Function',
|
||||
'PyLayerContext',
|
||||
'saved_tensors_hooks',
|
||||
]
|
||||
@@ -0,0 +1,775 @@
|
||||
# Copyright (c) 2023 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 collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
import paddle
|
||||
from paddle.base import framework
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
|
||||
|
||||
def as_tensors(xs):
|
||||
if isinstance(xs, framework.Variable):
|
||||
return xs
|
||||
elif isinstance(xs, Sequence):
|
||||
return tuple(xs)
|
||||
else:
|
||||
return xs
|
||||
|
||||
|
||||
class Jacobian:
|
||||
r"""Computes the Jacobian matrix of given xs and ys.
|
||||
|
||||
Once the Jacobian ``J`` is constructed, you can use a multidimensional index
|
||||
to retrieve the submatrix of ``J``, as same as slicing a Tensor. The
|
||||
submatrix is lazily evaluated along row axis, and will be cached once
|
||||
evaluated.
|
||||
|
||||
you can retrieve the submatrix by
|
||||
following methods:
|
||||
|
||||
* J[:], retrieving the full matrix.
|
||||
* J[:, :, j], retrieving the partial derivatives w.r.t. the j'th input
|
||||
variable.
|
||||
* J[:, i, :], retrieving the partial derivatives w.r.t. the i'th output
|
||||
variable.
|
||||
* J[:, i, j], retrieving the partial derivatives w.r.t. the i'th output
|
||||
variable and the j'th input variable.
|
||||
|
||||
Notes:
|
||||
|
||||
Ellipsis index is not supported currently.
|
||||
|
||||
Args:
|
||||
|
||||
ys (Tensor|Tuple[Tensor, ...]): The output derived from xs .
|
||||
xs (Tensor|Tuple[Tensor, ...]): The input tensor(s) .
|
||||
is_batched (bool): If true, the first axis is batch axis. Defaults to
|
||||
False.
|
||||
|
||||
Returns:
|
||||
|
||||
Jacobian (Object): A python object retains the Jacobian matrix.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ys: Tensor,
|
||||
xs: Tensor,
|
||||
is_batched: bool = False,
|
||||
) -> None:
|
||||
if not is_batched:
|
||||
if not 0 <= len(xs.shape) <= 1:
|
||||
raise ValueError(
|
||||
f"xs.ndim should be 0 or 1 when is_batched=False"
|
||||
f" but got {len(xs.shape)}"
|
||||
)
|
||||
if not 0 <= len(ys.shape) <= 1:
|
||||
raise ValueError(
|
||||
f"ys.ndim should be 0 or 1 when is_batched=False"
|
||||
f" but got {len(ys.shape)}"
|
||||
)
|
||||
self._jacobian = _JacobianNoBatch(ys, xs)
|
||||
else:
|
||||
if not 1 <= len(ys.shape) <= 2:
|
||||
raise ValueError(
|
||||
f"ys.ndim should be 1 or 2 when is_batched=True"
|
||||
f" but got {len(ys.shape)}"
|
||||
)
|
||||
if not 1 <= len(xs.shape) <= 2:
|
||||
raise ValueError(
|
||||
f"xs.ndim should be 1 or 2 when is_batched=True"
|
||||
f" but got {len(xs.shape)}"
|
||||
)
|
||||
self._jacobian = _JacobianBatchFirst(ys, xs)
|
||||
|
||||
@property
|
||||
def shape(self) -> list[int]:
|
||||
"""The shape of flattened Jacobian matrix."""
|
||||
return self._jacobian.shape
|
||||
|
||||
def __getitem__(self, indexes):
|
||||
return self._jacobian[indexes]
|
||||
|
||||
def __getattr__(self, __name: str): # noqa: PYI063
|
||||
if __name == "shape":
|
||||
return getattr(self._jacobian, __name)
|
||||
if __name == "_evaluate_all":
|
||||
return getattr(self._jacobian, __name)
|
||||
return getattr(self._jacobian._evaluate_all(), __name)
|
||||
|
||||
def __add__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs + rhs
|
||||
|
||||
def __sub__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs - rhs
|
||||
|
||||
def __mul__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs * rhs
|
||||
|
||||
def __div__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs / rhs
|
||||
|
||||
def __truediv__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs / rhs
|
||||
|
||||
def __pow__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs**rhs
|
||||
|
||||
def __mod__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs % rhs
|
||||
|
||||
def __floordiv__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs // rhs
|
||||
|
||||
def __matmul__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs @ rhs
|
||||
|
||||
def __eq__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs == rhs
|
||||
|
||||
def __ne__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs != rhs
|
||||
|
||||
def __lt__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs < rhs
|
||||
|
||||
def __le__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs <= rhs
|
||||
|
||||
def __gt__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs > rhs
|
||||
|
||||
def __ge__(self, other):
|
||||
lhs = self._evaluate_all()
|
||||
rhs = other._evaluate_all() if isinstance(other, Jacobian) else other
|
||||
return lhs >= rhs
|
||||
|
||||
|
||||
class Hessian(Jacobian):
|
||||
pass
|
||||
|
||||
|
||||
class _Jacobian:
|
||||
"""The base class for computing Jacobian matrix.
|
||||
|
||||
``_Jacobian`` implements the core logic of multidimensional index and lazy
|
||||
evaluation for Jacobian matrix, subclass only need to overwrite following
|
||||
methods:
|
||||
|
||||
* ``_lazy_axis()``, return the axis along which will be lazy
|
||||
evaluating.
|
||||
* ``_flatten(xs)``, flattens the inputs ``xs``.
|
||||
* ``_evaluate(index)``, evaluates one slice along ``_lazy_axis`` .
|
||||
|
||||
Notes:
|
||||
|
||||
Because currently PaddlePaddle only support reverse differentiation by
|
||||
``paddle.grad``, so lazy evaluation is only supported along the row of
|
||||
Jacobian matrix, which means that slicing along row will get better
|
||||
performance.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, ys, xs):
|
||||
self.original_xs_shape = xs.shape
|
||||
self.original_ys_shape = ys.shape
|
||||
self._xs = xs
|
||||
self._ys = ys
|
||||
if len(self._ys.shape) == 0 and not self.is_batched:
|
||||
self._ys = self._ys.reshape(
|
||||
[
|
||||
-1,
|
||||
]
|
||||
)
|
||||
if len(self._ys.shape) == 1 and self.is_batched:
|
||||
self._ys = self._ys.reshape([-1, 1])
|
||||
|
||||
self._flatten_xs = self._flatten(as_tensors(self._xs))
|
||||
self._flatten_ys = self._flatten(as_tensors(self._ys))
|
||||
self._cache = {}
|
||||
|
||||
@property
|
||||
def _lazy_axis(self):
|
||||
""" "The axis of lazily evaluated."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _lazy_indexes(self, indexes):
|
||||
idx = indexes[self._lazy_axis]
|
||||
return (
|
||||
(idx,)
|
||||
if isinstance(idx, int)
|
||||
else tuple(range(idx.start, idx.stop, idx.step))
|
||||
)
|
||||
|
||||
def _flatten(self, xs):
|
||||
raise NotImplementedError
|
||||
|
||||
def _shifted_indexes(self, indexes, lazy_axis_size=0):
|
||||
idx = indexes[self._lazy_axis]
|
||||
shifted_lazy_axis_idx = (
|
||||
0 if isinstance(idx, int) else slice(0, lazy_axis_size, 1)
|
||||
)
|
||||
return (
|
||||
*indexes[: self._lazy_axis],
|
||||
shifted_lazy_axis_idx,
|
||||
*indexes[self._lazy_axis + 1 :],
|
||||
)
|
||||
|
||||
def __getitem__(self, indexes):
|
||||
if self.is_batched is False:
|
||||
if len(self.shape) == 0:
|
||||
# xs and ys are both 0-D tensor
|
||||
raise IndexError("0-D tensor can not be indexed.")
|
||||
elif len(self.shape) == 1:
|
||||
# either ys or xs is 0-D tensor
|
||||
indexes = (
|
||||
(0, indexes)
|
||||
if len(self.original_ys_shape) == 0
|
||||
else (indexes, 0)
|
||||
)
|
||||
else:
|
||||
if len(self.shape) == 1:
|
||||
# xs and ys are both 1-D tensor
|
||||
indexes = (indexes, 0, 0)
|
||||
elif len(self.shape) == 2:
|
||||
# either xs or ys is 1-D tensor
|
||||
if isinstance(indexes, slice):
|
||||
indexes = (indexes, slice(None, None, None))
|
||||
else:
|
||||
indexes = (
|
||||
(indexes[0], 0, indexes[1])
|
||||
if len(self.original_ys_shape) == 1
|
||||
else (indexes[0], indexes[1], 0)
|
||||
)
|
||||
|
||||
indexes = _multi_index(indexes, self.inner_shape)
|
||||
|
||||
if isinstance(indexes[self._lazy_axis], int):
|
||||
other_indexes = (
|
||||
indexes[: self._lazy_axis] + indexes[self._lazy_axis + 1 :]
|
||||
)
|
||||
return self._cached_evaluate(indexes[self._lazy_axis])[
|
||||
other_indexes
|
||||
]
|
||||
lazy_indexes = self._lazy_indexes(indexes)
|
||||
# Using concat and reshape to replace stack operator temporarily, as
|
||||
# it is not a primitive operator.
|
||||
shape = list(self.inner_shape)
|
||||
shape[self._lazy_axis] = len(lazy_indexes)
|
||||
part_jac = paddle.concat(
|
||||
[self._cached_evaluate(i) for i in lazy_indexes],
|
||||
axis=self._lazy_axis,
|
||||
).reshape(shape)
|
||||
result = part_jac[self._shifted_indexes(indexes, len(lazy_indexes))]
|
||||
|
||||
# squeeze redundant 1 in shape
|
||||
if len(result.shape) > len(self.shape):
|
||||
for _ in range(len(result.shape) - len(self.shape)):
|
||||
result = result.squeeze(-1)
|
||||
|
||||
return result
|
||||
|
||||
def _cached_evaluate(self, k):
|
||||
if k is None:
|
||||
return self._cached_evaluate(0).reshape([])
|
||||
v = self._cache.get(k)
|
||||
if v is None:
|
||||
v = self._evaluate(k)
|
||||
self._cache[k] = v
|
||||
return v
|
||||
|
||||
def _evaluate(self, index):
|
||||
"""Evaluate one slice at along lazy axis."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _evaluate_all(self):
|
||||
if len(self.shape) == 0:
|
||||
return self._cached_evaluate(None)
|
||||
else:
|
||||
return self[:]
|
||||
|
||||
|
||||
class _JacobianNoBatch(_Jacobian):
|
||||
"""Compute Jacobian matrix without batch dimension.
|
||||
Suppose the mapping is :math:`f: R^M \to R^N`, the output shape is
|
||||
``(N, M)`` .
|
||||
"""
|
||||
|
||||
def __init__(self, ys, xs):
|
||||
self.is_batched = False
|
||||
super().__init__(ys, xs)
|
||||
# inner_shape is for convenient, it will regard 0-D tensor as 1-D tensor
|
||||
self.inner_shape = [
|
||||
*(self._flatten_ys.shape[0:1]),
|
||||
*(self._flatten_xs.shape[0:1]),
|
||||
]
|
||||
self.shape = [
|
||||
*(self.original_ys_shape[0:1]),
|
||||
*(self.original_xs_shape[0:1]),
|
||||
]
|
||||
|
||||
@property
|
||||
def _lazy_axis(self):
|
||||
return 0
|
||||
|
||||
def _flatten(self, xs):
|
||||
if not isinstance(xs, Sequence):
|
||||
return xs.reshape((-1,))
|
||||
return paddle.concat(tuple(x.reshape((-1,)) for x in xs))
|
||||
|
||||
def _evaluate(self, row_index):
|
||||
return self._flatten(
|
||||
_grad_for_jacobian(
|
||||
self._flatten_ys[row_index],
|
||||
self._xs,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class _JacobianBatchFirst(_Jacobian):
|
||||
"""Compute Jacobian matrix with batch at first axis.
|
||||
Suppose the mapping is :math:`f: R^{B,M} \to R^{B,N}`, the output shape is
|
||||
``(B, N, M)`` .
|
||||
"""
|
||||
|
||||
def __init__(self, ys, xs):
|
||||
self.is_batched = True
|
||||
super().__init__(ys, xs)
|
||||
# inner_shape is for convenient, it will regard 0-D tensor as 1-D tensor
|
||||
self.inner_shape = [
|
||||
*(self._flatten_xs.shape[0:1]),
|
||||
*(self._flatten_ys.shape[1:2]),
|
||||
*(self._flatten_xs.shape[1:2]),
|
||||
]
|
||||
self.shape = [
|
||||
*(self._flatten_xs.shape[0:1]),
|
||||
*(self.original_ys_shape[1:2]),
|
||||
*(self.original_xs_shape[1:2]),
|
||||
]
|
||||
|
||||
@property
|
||||
def _lazy_axis(self):
|
||||
return 1
|
||||
|
||||
def _flatten(self, xs):
|
||||
if not isinstance(xs, Sequence):
|
||||
return xs.reshape((xs.shape[0], -1))
|
||||
return paddle.concat(
|
||||
tuple(x.reshape((x.shape[0], -1)) for x in as_tensors(xs)), 1
|
||||
)
|
||||
|
||||
def _evaluate(self, row_index):
|
||||
return self._flatten(
|
||||
_grad_for_jacobian(self._flatten_ys[:, row_index], self._xs)
|
||||
)
|
||||
|
||||
|
||||
def _multi_index(indexes, shape):
|
||||
"""A tool for parsing N-dimensional index into a standard format.
|
||||
|
||||
Currently supporting following input format:
|
||||
* ([positive|negative|slice], ...), the right-most elements can be
|
||||
omitted.
|
||||
|
||||
The standard format after converted is slice tuple which contains N elements:
|
||||
* ([positive|slice], ..., [positive|slice])
|
||||
|
||||
Notes:
|
||||
Ellipsis indexes such as ``(..., i), (i, ...)`` is not supported.
|
||||
|
||||
Args:
|
||||
indexes (tuple): The input indexes.
|
||||
shape (tuple): The input shape.
|
||||
|
||||
Returns:
|
||||
tuple: The standard format index as the above description.
|
||||
"""
|
||||
indexes = indexes if isinstance(indexes, Sequence) else (indexes,)
|
||||
if any(isinstance(i, type(Ellipsis)) for i in indexes):
|
||||
raise IndexError('Ellipsis index currently is not supported.')
|
||||
# Fill the right-most elements.
|
||||
indexes = indexes + (slice(0, None, None),) * (len(shape) - len(indexes))
|
||||
# Convert to positive index.
|
||||
positive_indexes = []
|
||||
for i, index in enumerate(indexes):
|
||||
if isinstance(index, slice):
|
||||
index = slice(
|
||||
index.start or 0, index.stop or shape[i], index.step or 1
|
||||
)
|
||||
positive_indexes.append(
|
||||
slice(
|
||||
index.start + shape[i] if index.start < 0 else index.start,
|
||||
index.stop + shape[i] if index.stop < 0 else index.stop,
|
||||
# Negative step means index backward, no need to convert to
|
||||
# positive integer.
|
||||
index.step,
|
||||
)
|
||||
)
|
||||
elif isinstance(index, int):
|
||||
positive_indexes.append(index + shape[i] if index < 0 else index)
|
||||
else:
|
||||
raise TypeError(f'Not supported index type {index}.')
|
||||
return tuple(positive_indexes)
|
||||
|
||||
|
||||
@overload
|
||||
def jacobian(
|
||||
ys: Tensor,
|
||||
xs: Tensor,
|
||||
batch_axis: int | None = ...,
|
||||
) -> Jacobian: ...
|
||||
|
||||
|
||||
@overload
|
||||
def jacobian(
|
||||
ys: Sequence[Tensor],
|
||||
xs: Sequence[Tensor],
|
||||
batch_axis: int | None = ...,
|
||||
) -> tuple[tuple[Jacobian, ...], ...]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def jacobian(
|
||||
ys: Tensor,
|
||||
xs: Sequence[Tensor],
|
||||
batch_axis: int | None = ...,
|
||||
) -> tuple[Jacobian, ...]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def jacobian(
|
||||
ys: Sequence[Tensor],
|
||||
xs: Tensor,
|
||||
batch_axis: int | None = ...,
|
||||
) -> tuple[Jacobian, ...]: ...
|
||||
|
||||
|
||||
def jacobian(
|
||||
ys,
|
||||
xs,
|
||||
batch_axis=None,
|
||||
):
|
||||
r"""
|
||||
Computes the Jacobian of the dependent variable ``ys`` versus the independent
|
||||
variable ``xs``.
|
||||
|
||||
Where ``ys`` represents the output of ``xs`` after a certain operation, ``ys`` and
|
||||
``xs`` can be Tensor or tuple of Tensors, ``batch_axis`` indicates the position of
|
||||
the batch dimension of the parameter data.
|
||||
|
||||
When the input is a tuple Tensors, the returned result is a ``Jacobian`` object with
|
||||
the same number of nesting levels as ``xs``, and each Jacobian has the same shape as
|
||||
The ``xs`` tuples are identical in one-to-one correspondence.
|
||||
|
||||
- When ``batch_axis=None``, only 0-dimensional Tensor or 1-dimensional Tensor is
|
||||
supported, assuming the shape of ``xs`` is ``[N, ]``, the shape of ``ys`` is
|
||||
``[M, ]``, then the output Jacobian matrix shape is ``[M, N]``.
|
||||
|
||||
- When ``batch_axis=0``, only 1-dimensional Tensor or 2-dimensional Tensor is
|
||||
supported, assuming the shape of ``xs`` is ``[B, N]``, The shape of ``ys`` is
|
||||
``[B, M]``, then the output Jacobian matrix shape is ``[B, M, N]``.
|
||||
|
||||
After the ``Jacobian`` object is created, the actual calculation process does not
|
||||
occur, but the lazy evaluation method is used for calculation. It can be
|
||||
multi-dimensional indexed to obtain the entire Jacobian matrix or sub-matrix, and
|
||||
the actual calculation will be performed at this time the value is calculated and
|
||||
the result is returned. At the same time, in the actual evaluation process, the
|
||||
calculated sub-matrix will be cached to avoid duplicate calculations in the
|
||||
subsequent indexing process.
|
||||
|
||||
For example, assuming ``Jacobian`` instance ``J`` has shape ``[B, M, N]``, assuming
|
||||
``M > 4`` , then ``J[:, 1:4:1, :]`` means to get the values from row ``1`` to row
|
||||
``3`` of ``J``. In actual calculation, only the rows ``1`` to ``3`` are evaluated,
|
||||
and the calculation results of ``1`` to ``3`` will be cached at the granularity of
|
||||
the row, and will be used next time. When obtaining one or more rows of results
|
||||
above, the already calculated parts will not be recalculated.
|
||||
|
||||
Args:
|
||||
|
||||
ys (Union[paddle.Tensor, Tuple[paddle.Tensor, ...]]): Output or tuple of outputs derived from xs.
|
||||
xs (Union[paddle.Tensor, Tuple[paddle.Tensor, ...]]): Input or tuple of inputs.
|
||||
batch_axis (Optional[int], optional): Index of batch axis. Defaults to None.
|
||||
|
||||
Returns:
|
||||
|
||||
Union[Tuple[Tuple[Jacobian, ...], ...], Tuple[Jacobian, ...], Jacobian]: Jacobian(s) of ys derived from xs.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> x1 = paddle.randn([3])
|
||||
>>> x2 = paddle.randn([3])
|
||||
>>> x1.stop_gradient = False
|
||||
>>> x2.stop_gradient = False
|
||||
|
||||
>>> y = x1 + x2
|
||||
|
||||
>>> J = paddle.autograd.jacobian(y, (x1, x2))
|
||||
>>> J_y_x1 = J[0][:] # evaluate result of dy/dx1
|
||||
>>> J_y_x2 = J[1][:] # evaluate result of dy/dx2
|
||||
|
||||
>>> print(J_y_x1.shape)
|
||||
paddle.Size([3, 3])
|
||||
>>> print(J_y_x2.shape)
|
||||
paddle.Size([3, 3])
|
||||
"""
|
||||
|
||||
if batch_axis is not None and batch_axis != 0:
|
||||
raise ValueError(
|
||||
f"batch_axis should be None or 0, but got {batch_axis}."
|
||||
)
|
||||
|
||||
# TODO(HydrogenSulfate): support batch_axis > 0
|
||||
is_batched = batch_axis is not None
|
||||
if isinstance(ys, Sequence) and isinstance(xs, Sequence):
|
||||
_jacobian = tuple(
|
||||
tuple(Jacobian(_ys, _xs, is_batched) for _xs in xs) for _ys in ys
|
||||
)
|
||||
elif isinstance(ys, Sequence) and not isinstance(xs, Sequence):
|
||||
_jacobian = tuple(Jacobian(_ys, xs, is_batched) for _ys in ys)
|
||||
elif not isinstance(ys, Sequence) and isinstance(xs, Sequence):
|
||||
_jacobian = tuple(Jacobian(ys, _xs, is_batched) for _xs in xs)
|
||||
else:
|
||||
_jacobian = Jacobian(ys, xs, is_batched)
|
||||
|
||||
return _jacobian
|
||||
|
||||
|
||||
@overload
|
||||
def hessian(
|
||||
ys: Tensor,
|
||||
xs: Tensor,
|
||||
batch_axis: int | None = ...,
|
||||
) -> Hessian: ...
|
||||
|
||||
|
||||
@overload
|
||||
def hessian(
|
||||
ys: Tensor,
|
||||
xs: Sequence[Tensor],
|
||||
batch_axis: int | None = ...,
|
||||
) -> tuple[tuple[Hessian, ...], ...]: ...
|
||||
|
||||
|
||||
def hessian(
|
||||
ys,
|
||||
xs,
|
||||
batch_axis=None,
|
||||
):
|
||||
r"""
|
||||
Computes the Jacobian of the dependent variable ``ys`` versus the independent
|
||||
variable ``xs``.
|
||||
|
||||
Among them, ``ys`` means the output of ``xs`` after a certain operation, ``ys`` can
|
||||
only be a single Tensor, ``xs`` can be a Tensor or a Tensor tuple, and
|
||||
``batch_axis`` means The position of the batch dimension of the parameter data.
|
||||
|
||||
When the input ``xs`` is a Tensor tuple, the returned result is a ``Hessian`` tuple,
|
||||
assuming that the internal shape of the ``xs`` tuple is composed of ``([M1, ], [M2, ])``, the shape of the returned
|
||||
result consists of ``(([M1, M1], [M1, M2]), ([M2, M1], [M2, M2]))``
|
||||
|
||||
- When ``batch_axis=None``, only 0-dimensional Tensor or 1-dimensional Tensor is
|
||||
supported, assuming that the shape of ``xs`` is ``[N, ]``, and the shape of ``ys`` is ``[ ]`` (0-dimensional Tensor), the final output is a single Hessian matrix whose shape is ``[N, N]``.
|
||||
|
||||
- When ``batch_axis=0``, only 1-dimensional Tensor or 2-dimensional Tensor is
|
||||
supported, assuming that the shape of ``xs`` is ``[B, N]``, and the shape of ``ys`` is ``[B, ]``, the final output Jacobian matrix shape is ``[B, N, N]``.
|
||||
|
||||
After the ``Hessian`` object is created, the complete calculation process does not
|
||||
occur, but a partial lazy evaluation method is used for calculation. It can be
|
||||
multi-dimensionally indexed to obtain the entire Hessian matrix or sub-matrix. At
|
||||
this time, the actual Evaluates the computation and returns the result. At the same
|
||||
time, in the actual evaluation process, the calculated sub-matrix will be cached to
|
||||
avoid repeated calculations in the subsequent indexing process.
|
||||
|
||||
Args:
|
||||
|
||||
ys (paddle.Tensor): Output derived from xs which contain one element.
|
||||
xs (Union[paddle.Tensor, Tuple[paddle.Tensor, ...]]): Input or tuple of inputs.
|
||||
batch_axis (Optional[int], optional): Index of batch axis. Defaults to None.
|
||||
|
||||
Returns:
|
||||
|
||||
Union[Tuple[Tuple[Hessian, ...], ...], Tuple[Hessian, ...], Hessian]: Hessian(s) of ys derived from xs.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> x1 = paddle.randn([3])
|
||||
>>> x2 = paddle.randn([4])
|
||||
>>> x1.stop_gradient = False
|
||||
>>> x2.stop_gradient = False
|
||||
|
||||
>>> y = x1.sum() + x2.sum()
|
||||
|
||||
>>> H = paddle.autograd.hessian(y, (x1, x2))
|
||||
>>> H_y_x1_x1 = H[0][0][:] # evaluate result of ddy/dx1x1
|
||||
>>> H_y_x1_x2 = H[0][1][:] # evaluate result of ddy/dx1x2
|
||||
>>> H_y_x2_x1 = H[1][0][:] # evaluate result of ddy/dx2x1
|
||||
>>> H_y_x2_x2 = H[1][1][:] # evaluate result of ddy/dx2x2
|
||||
|
||||
>>> print(H_y_x1_x1.shape)
|
||||
paddle.Size([3, 3])
|
||||
>>> print(H_y_x1_x2.shape)
|
||||
paddle.Size([3, 4])
|
||||
>>> print(H_y_x2_x1.shape)
|
||||
paddle.Size([4, 3])
|
||||
>>> print(H_y_x2_x2.shape)
|
||||
paddle.Size([4, 4])
|
||||
"""
|
||||
|
||||
if batch_axis is None:
|
||||
if ys.numel() > 1:
|
||||
raise ValueError(
|
||||
f"Only support ys.numel()({ys.numel()})==1 when batch_axis is None."
|
||||
)
|
||||
ys = ys.reshape(())
|
||||
elif isinstance(batch_axis, int):
|
||||
if ys[0].numel() > 1:
|
||||
raise ValueError(
|
||||
f"Only support ys[0].numel()({ys.numel()})==1 when batch_axis is int"
|
||||
)
|
||||
# TODO(HydrogenSulfate): support batch_axis > 0
|
||||
if batch_axis != 0:
|
||||
raise ValueError("Only support batch_axis=0 yet.")
|
||||
ys = ys.reshape((-1,))
|
||||
else:
|
||||
raise ValueError(
|
||||
f"batch_axis should be None or int, but got {type(batch_axis)}."
|
||||
)
|
||||
|
||||
_jacobian = jacobian(ys, xs, batch_axis)
|
||||
if not isinstance(xs, Sequence):
|
||||
hessian = jacobian(_jacobian, xs, batch_axis)
|
||||
|
||||
# change classname to Hessian instead of Jacobian.
|
||||
hessian.__class__ = Hessian
|
||||
else:
|
||||
hessian = tuple(jacobian(_j, xs, batch_axis) for _j in _jacobian)
|
||||
|
||||
# change classname to Hessian instead of Jacobian.
|
||||
for i in range(len(hessian)):
|
||||
for j in range(len(hessian[0])):
|
||||
hessian[i][j].__class__ = Hessian
|
||||
|
||||
return hessian
|
||||
|
||||
|
||||
def _replace_none_with_zero_tensor(xs, refs):
|
||||
if xs is None:
|
||||
xs = paddle.zeros_like(refs)
|
||||
xs.stop_gradient = refs.stop_gradient
|
||||
return xs
|
||||
elif isinstance(xs, Sequence):
|
||||
return tuple(
|
||||
_replace_none_with_zero_tensor(x, refs[i]) for i, x in enumerate(xs)
|
||||
)
|
||||
else:
|
||||
return xs
|
||||
|
||||
|
||||
def _grad_for_jacobian(ys, xs, v=None):
|
||||
"""A gradient function that can be used in dynamic graph and static graph.
|
||||
|
||||
The ``grad`` combines ``paddle.grad`` used in dynamic graph and
|
||||
``paddle.static.gradients`` used in static graph, and do following changes:
|
||||
|
||||
* The ``allow_unused`` flag is removed and set defaults to true internally,
|
||||
none in outputs will be replaced by zero tensor.
|
||||
* The ``create_graph`` flag is removed and set defaults to true internally,
|
||||
only makes sense in dynamic graph.
|
||||
* When xs is a single Tensor, ``paddle.grad`` returns a list which only
|
||||
contains one Tensor. It may confuse users, thus in this case we improve
|
||||
to return a single Tensor in _grad_for_jacobian interface.
|
||||
|
||||
Args:
|
||||
ys (Tensor|Sequence[Tensor]): The output tensor or tensor sequence of
|
||||
the graph to compute gradients.
|
||||
xs (Tensor|Sequence[Tensor]): The input tensor or tensor sequence of the graph to
|
||||
compute gradients. The returned values of this API are the
|
||||
gradients of inputs .
|
||||
v (Tensor|Sequence[Tensor]|None,optional): The initial gradient values
|
||||
of outputs . If grad_outputs is None, the initial gradient values of
|
||||
outputs would be Tensors filled with 1; if grad_outputs is not None,
|
||||
it must have the same length as outputs , and in this case, the
|
||||
initial gradient value of the i-th outputs would be: (1) a Tensor
|
||||
filled with 1 when the i-th element of grad_outputs is None;
|
||||
(2) the i-th element of grad_outputs when the i-th element of
|
||||
grad_outputs is a Tensor. Default None.
|
||||
|
||||
Returns:
|
||||
Tensor|tuple[Tensor]: Tensor or a tuple of Tensors, whose length is the
|
||||
same as the Tensor number inside inputs, and the i-th returned
|
||||
Tensor is the sum of gradients of outputs with respect to the i-th
|
||||
inputs.
|
||||
"""
|
||||
if paddle.in_dynamic_mode():
|
||||
# paddle.grad returns a list though the inputs is a single Tensor. The
|
||||
# follow code snippet fixes the problem by return the first element of
|
||||
# xs_grad when the xs is a single Tensor.
|
||||
xs_grad = paddle.grad(ys, xs, v, create_graph=True, allow_unused=True)
|
||||
if (
|
||||
isinstance(xs, paddle.base.framework.Variable)
|
||||
and isinstance(xs_grad, Sequence)
|
||||
and len(xs_grad) > 0
|
||||
):
|
||||
xs_grad = xs_grad[0]
|
||||
else:
|
||||
xs_grad = paddle.static.gradients(ys, xs, v)
|
||||
if (
|
||||
isinstance(xs, framework.Variable)
|
||||
and isinstance(xs_grad, Sequence)
|
||||
and len(xs_grad) > 0
|
||||
):
|
||||
xs_grad = xs_grad[0]
|
||||
return _replace_none_with_zero_tensor(xs_grad, xs)
|
||||
@@ -0,0 +1,152 @@
|
||||
# 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.base import core, framework
|
||||
from paddle.base.backward import gradients_with_optimizer # noqa: F401
|
||||
from paddle.utils.download import check_and_create_dir
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
@framework.dygraph_only
|
||||
def backward(
|
||||
tensors: Tensor | Sequence[Tensor],
|
||||
grad_tensors: Tensor | Sequence[Tensor | None] | None = None,
|
||||
retain_graph: bool = False,
|
||||
create_graph: bool = False,
|
||||
*,
|
||||
dump_backward_graph_path: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Compute the backward gradients of given tensors.
|
||||
|
||||
Args:
|
||||
tensors(list of Tensors): the tensors which the gradient to be computed. The tensors can not contain the same tensor.
|
||||
|
||||
grad_tensors(list of Tensors of None, optional): the init gradients of the `tensors`` .If not None, it must have the same length with ``tensors`` ,
|
||||
and if any of the elements is None, then the init gradient is the default value which is filled with 1.0.
|
||||
If None, all the gradients of the ``tensors`` is the default value which is filled with 1.0.
|
||||
Defaults to None.
|
||||
|
||||
retain_graph(bool, optional): If False, the graph used to compute grads will be freed. If you would
|
||||
like to add more ops to the built graph after calling this method( :code:`backward` ), set the parameter
|
||||
:code:`retain_graph` to True, then the grads will be retained. Thus, setting it to False is much more memory-efficient.
|
||||
Defaults to False.
|
||||
dump_backward_graph_path(str, optional): Specifies the directory path for storing the debug file.
|
||||
If this parameter is specified, the backward-related graph (in dot format)
|
||||
and the debugging call stack information will be generated in this directory.
|
||||
Returns:
|
||||
NoneType: None
|
||||
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> x = paddle.to_tensor([[1, 2], [3, 4]], dtype='float32', stop_gradient=False)
|
||||
>>> y = paddle.to_tensor([[3, 2], [3, 4]], dtype='float32')
|
||||
|
||||
>>> grad_tensor1 = paddle.to_tensor([[1, 2], [2, 3]], dtype='float32')
|
||||
>>> grad_tensor2 = paddle.to_tensor([[1, 1], [1, 1]], dtype='float32')
|
||||
|
||||
>>> z1 = paddle.matmul(x, y)
|
||||
>>> z2 = paddle.matmul(x, y)
|
||||
|
||||
>>> paddle.autograd.backward([z1, z2], [grad_tensor1, grad_tensor2], True)
|
||||
>>> print(x.grad)
|
||||
Tensor(shape=[2, 2], dtype=float32, place=Place(cpu), stop_gradient=False,
|
||||
[[12., 18.],
|
||||
[17., 25.]])
|
||||
|
||||
|
||||
>>> x.clear_grad()
|
||||
|
||||
>>> paddle.autograd.backward([z1, z2], [grad_tensor1, None], True)
|
||||
>>> print(x.grad)
|
||||
Tensor(shape=[2, 2], dtype=float32, place=Place(cpu), stop_gradient=False,
|
||||
[[12., 18.],
|
||||
[17., 25.]])
|
||||
|
||||
>>> x.clear_grad()
|
||||
|
||||
>>> paddle.autograd.backward([z1, z2])
|
||||
>>> print(x.grad)
|
||||
Tensor(shape=[2, 2], dtype=float32, place=Place(cpu), stop_gradient=False,
|
||||
[[10., 14.],
|
||||
[10., 14.]])
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def check_tensors(
|
||||
in_out_list: Sequence[Tensor] | Tensor, name: str
|
||||
) -> Sequence[Tensor]:
|
||||
assert in_out_list is not None, f"{name} should not be None"
|
||||
|
||||
if isinstance(in_out_list, (list, tuple)):
|
||||
assert len(in_out_list) > 0, f"{name} cannot be empty"
|
||||
for each_var in in_out_list:
|
||||
assert isinstance(each_var, paddle.Tensor), (
|
||||
f"Elements of {name} must be paddle.Tensor"
|
||||
)
|
||||
return in_out_list
|
||||
else:
|
||||
assert isinstance(in_out_list, paddle.Tensor), (
|
||||
f"{name} must be Tensor or list of Tensor"
|
||||
)
|
||||
return [in_out_list]
|
||||
|
||||
tensors = check_tensors(tensors, "tensors")
|
||||
|
||||
assert len(tensors) == len(set(tensors)), (
|
||||
"The argument 'tensors' of paddle.autograd.backward contains duplicate paddle.Tensor object."
|
||||
)
|
||||
|
||||
if grad_tensors is not None:
|
||||
if not isinstance(grad_tensors, (list, tuple)):
|
||||
grad_tensors = [grad_tensors]
|
||||
|
||||
for each_tensor in grad_tensors:
|
||||
if each_tensor is not None:
|
||||
assert isinstance(each_tensor, paddle.Tensor), (
|
||||
"The argument 'grad_tensors' of paddle.autograd.backward is invalid, it can be 'None', 'paddle.Tensor' or 'list[None/paddle.Tensor]'."
|
||||
)
|
||||
else:
|
||||
grad_tensors = []
|
||||
|
||||
if len(grad_tensors) > 0:
|
||||
assert len(tensors) == len(grad_tensors), (
|
||||
"The length of grad_tensors must be equal to tensors"
|
||||
)
|
||||
|
||||
assert isinstance(retain_graph, bool), "retain_graph must be True or False"
|
||||
check_and_create_dir(dump_backward_graph_path)
|
||||
core.eager.run_backward(
|
||||
tensors,
|
||||
grad_tensors,
|
||||
retain_graph,
|
||||
create_graph,
|
||||
dump_backward_graph_path,
|
||||
)
|
||||
@@ -0,0 +1,810 @@
|
||||
# Copyright (c) 2023 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,tes
|
||||
# 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
|
||||
|
||||
import collections
|
||||
import logging
|
||||
import warnings
|
||||
from collections.abc import Sequence
|
||||
from functools import lru_cache
|
||||
from typing import Any
|
||||
|
||||
from paddle import pir
|
||||
from paddle.base import core
|
||||
from paddle.base.libpaddle.pir import (
|
||||
get_used_external_value,
|
||||
)
|
||||
from paddle.base.wrapped_decorator import signature_safe_contextmanager
|
||||
|
||||
# TODO(CZ): to be removed when we support dynamic shape by default.
|
||||
ALLOW_DYNAMIC_SHAPE_VJP_OPS = [
|
||||
"pd_op.abs",
|
||||
"pd_op.add",
|
||||
"pd_op.amax",
|
||||
"pd_op.amin",
|
||||
"pd_op.angle",
|
||||
"pd_op.argsort",
|
||||
"pd_op.assign",
|
||||
"pd_op.batch_norm_",
|
||||
"pd_op.cast",
|
||||
"pd_op.ceil",
|
||||
"pd_op.concat",
|
||||
"pd_op.cos",
|
||||
"pd_op.cumprod",
|
||||
"pd_op.cumsum",
|
||||
"pd_op.divide",
|
||||
"pd_op.dot",
|
||||
"pd_op.dropout",
|
||||
"pd_op.elementwise_pow",
|
||||
"pd_op.erf",
|
||||
"pd_op.exp",
|
||||
"pd_op.expand",
|
||||
"pd_op.floor",
|
||||
"pd_op.fmax",
|
||||
"pd_op.fmin",
|
||||
"pd_op.gather",
|
||||
"pd_op.gather_nd",
|
||||
"pd_op.gelu",
|
||||
"pd_op.group_norm",
|
||||
"pd_op.hardsigmoid",
|
||||
"pd_op.hardswish",
|
||||
"pd_op.kron",
|
||||
"pd_op.kthvalue",
|
||||
"pd_op.layer_norm",
|
||||
"pd_op.leaky_relu",
|
||||
"pd_op.log",
|
||||
"pd_op.logcumsumexp",
|
||||
"pd_op.logsumexp",
|
||||
"pd_op.linear_v2",
|
||||
"pd_op.matmul",
|
||||
"pd_op.max",
|
||||
"pd_op.maximum",
|
||||
"pd_op.mean",
|
||||
"pd_op.minimum",
|
||||
"pd_op.multiply",
|
||||
"pd_op.pad",
|
||||
"pd_op.pow",
|
||||
"pd_op.prod",
|
||||
"pd_op.reduce_as",
|
||||
"pd_op.relu",
|
||||
"pd_op.relu6",
|
||||
"pd_op.reshape",
|
||||
"pd_op.roll",
|
||||
"pd_op.rsqrt",
|
||||
"pd_op.scale",
|
||||
"pd_op.scatter",
|
||||
"pd_op.scatter_nd_add",
|
||||
"pd_op.sigmoid",
|
||||
"pd_op.silu",
|
||||
"pd_op.sin",
|
||||
"pd_op.softmax",
|
||||
"pd_op.softsign",
|
||||
"pd_op.split",
|
||||
"pd_op.sqrt",
|
||||
"pd_op.square",
|
||||
"pd_op.squeeze",
|
||||
"pd_op.stack",
|
||||
"pd_op.subtract",
|
||||
"pd_op.sum",
|
||||
"pd_op.swiglu",
|
||||
"pd_op.swish",
|
||||
"pd_op.take_along_axis",
|
||||
"pd_op.tanh",
|
||||
"pd_op.tile",
|
||||
"pd_op.topk",
|
||||
"pd_op.transpose",
|
||||
"pd_op.trunc",
|
||||
"pd_op.unsqueeze",
|
||||
"pd_op.where",
|
||||
"pd_op.p_norm",
|
||||
"pd_op.index_put",
|
||||
"pd_op.index_add",
|
||||
"pd_op.elu",
|
||||
"pd_op.masked_fill",
|
||||
"pd_op.masked_select",
|
||||
"pd_op.var",
|
||||
]
|
||||
|
||||
|
||||
class ValueWrapper:
|
||||
def __init__(self, value) -> None:
|
||||
if isinstance(value, ValueWrapper):
|
||||
assert isinstance(value._value, (type(None), pir.Value))
|
||||
else:
|
||||
if not isinstance(value, (type(None), pir.Value)):
|
||||
raise TypeError(
|
||||
"Value Wrapper is only support None and pir.Value"
|
||||
)
|
||||
self._value = value._value if isinstance(value, ValueWrapper) else value
|
||||
|
||||
def __hash__(self) -> int:
|
||||
if isinstance(self._value, pir.Value):
|
||||
return self._value.hash()
|
||||
else:
|
||||
return hash(self._value)
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if not isinstance(other, ValueWrapper):
|
||||
warnings.warn(
|
||||
f'In ValueWrapper.__eq__ expected type of `other` is ValueWrapper but received {other.__class__}.'
|
||||
)
|
||||
return False
|
||||
|
||||
if self._value is None or other._value is None:
|
||||
return self._value is None and other._value is None
|
||||
return self._value.is_same(other._value)
|
||||
|
||||
|
||||
class ValueDict:
|
||||
def __init__(
|
||||
self,
|
||||
iter=None,
|
||||
*,
|
||||
default_factory=None,
|
||||
):
|
||||
self._items: dict[ValueWrapper] = {}
|
||||
self._default_factory = default_factory
|
||||
if iter is not None:
|
||||
for key, val in iter.items():
|
||||
self[key] = val
|
||||
|
||||
def copy(self):
|
||||
ret = ValueDict()
|
||||
ret._items = self._items.copy()
|
||||
ret._default_factory = self._default_factory
|
||||
return ret
|
||||
|
||||
def update(self, other_dict):
|
||||
for key, val in other_dict.items():
|
||||
self[key] = val
|
||||
|
||||
def keys(self):
|
||||
for key in self._items.keys():
|
||||
yield key._value
|
||||
|
||||
def values(self):
|
||||
return self._items.values()
|
||||
|
||||
def items(self):
|
||||
for key, val in self._items.items():
|
||||
yield key._value, val
|
||||
|
||||
def get(self, key, default=None):
|
||||
if not self.__contains__(key):
|
||||
return default
|
||||
return self._items[ValueWrapper(key)]
|
||||
|
||||
def pop(self, key):
|
||||
if not self.__contains__(key):
|
||||
raise KeyError(f'{key} is not in ValueDict')
|
||||
return self._items.pop(ValueWrapper(key))
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
if not self.__contains__(key):
|
||||
self[key] = default
|
||||
return self[key]
|
||||
|
||||
def __setitem__(self, key, val: Any):
|
||||
self._items[ValueWrapper(key)] = val
|
||||
|
||||
def __getitem__(self, key):
|
||||
if not self.__contains__(key):
|
||||
if self._default_factory is not None:
|
||||
self[key] = self._default_factory()
|
||||
else:
|
||||
raise KeyError(f'{key} is not in ValueDict')
|
||||
return self._items[ValueWrapper(key)]
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._items)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._items)
|
||||
|
||||
def __iter__(self):
|
||||
return self.keys()
|
||||
|
||||
def __contains__(self, key):
|
||||
return ValueWrapper(key) in self._items
|
||||
|
||||
def __repr__(self) -> str:
|
||||
items_str = ", ".join(f"{key}: {val}" for key, val in self.items())
|
||||
return f'ValueDict({items_str})'
|
||||
|
||||
|
||||
class ValueSet:
|
||||
def __init__(
|
||||
self, iter: Sequence[ValueWrapper] | set[ValueWrapper] | None = None
|
||||
):
|
||||
self._set: set[ValueWrapper] = set()
|
||||
if iter is not None:
|
||||
for val in iter:
|
||||
self.add(val)
|
||||
|
||||
def copy(self):
|
||||
ret = ValueSet()
|
||||
ret._set = self._set.copy()
|
||||
return ret
|
||||
|
||||
def add(self, val):
|
||||
if not self.__contains__(val):
|
||||
self._set.add(ValueWrapper(val))
|
||||
|
||||
def update(self, other: set):
|
||||
for val in other:
|
||||
self.add(val)
|
||||
|
||||
def pop(self):
|
||||
return self._set.pop()._value
|
||||
|
||||
def remove(self, val):
|
||||
self._set.remove(ValueWrapper(val))
|
||||
|
||||
def discard(self, val):
|
||||
self._set.discard(ValueWrapper(val))
|
||||
|
||||
def __and__(self, other: ValueSet):
|
||||
return ValueSet(self._set & other._set)
|
||||
|
||||
def __sub__(self, other: ValueSet):
|
||||
return ValueSet(self._set - other._set)
|
||||
|
||||
def __or__(self, other: ValueSet):
|
||||
return ValueSet(self._set | other._set)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._set)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._set)
|
||||
|
||||
def __iter__(self):
|
||||
for val in self._set:
|
||||
yield val._value
|
||||
|
||||
def __contains__(self, val):
|
||||
return ValueWrapper(val) in self._set
|
||||
|
||||
def __repr__(self) -> str:
|
||||
items_str = ", ".join(repr(item) for item in self)
|
||||
return f'ValueSet({items_str})'
|
||||
|
||||
|
||||
class State:
|
||||
"""
|
||||
record relationship of forward op/value and backward op/value
|
||||
one state must be binding with a block, if block has parent block,
|
||||
state will include parent block info.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, block):
|
||||
self.block = block
|
||||
# value -> list(list(value))
|
||||
self.value_to_valuegrad = ValueDict(default_factory=list)
|
||||
self.value_to_sumvaluegrad = ValueDict(default_factory=list)
|
||||
# operation -> list(operation)
|
||||
self.op_to_opgrad = collections.defaultdict(list)
|
||||
|
||||
# value -> list(value)
|
||||
self.valuegrad_to_value = ValueDict(default_factory=list)
|
||||
self.sumvaluegrad_to_value = ValueDict(default_factory=list)
|
||||
# operation -> list(operation)
|
||||
self.opgrad_to_op = collections.defaultdict(list)
|
||||
# only for controlflow
|
||||
# inside_value is sub block value, which will yield to parent block,
|
||||
# parent block value is outside_value
|
||||
self.inside_value_to_outside_value_map = ValueDict()
|
||||
|
||||
def turn_map(self) -> None:
|
||||
self.valuegrad_to_value = ValueDict(default_factory=list)
|
||||
self.sumvaluegrad_to_value = ValueDict(default_factory=list)
|
||||
self.opgrad_to_op = collections.defaultdict(list)
|
||||
|
||||
for k, v in self.value_to_valuegrad.items():
|
||||
if v != []:
|
||||
for value in v[0]:
|
||||
self.valuegrad_to_value[value] = [k]
|
||||
for k, v in self.value_to_sumvaluegrad.items():
|
||||
if v != []:
|
||||
for value in v[0]:
|
||||
self.sumvaluegrad_to_value[value] = [k]
|
||||
for k, v in self.op_to_opgrad.items():
|
||||
if v != []:
|
||||
self.opgrad_to_op[v[0]] = [k]
|
||||
|
||||
def copy(self, new_block):
|
||||
state = State(new_block)
|
||||
state.value_to_valuegrad = self.value_to_valuegrad.copy()
|
||||
state.value_to_sumvaluegrad = self.value_to_sumvaluegrad.copy()
|
||||
|
||||
# operation -> list(operation)
|
||||
state.op_to_opgrad = self.op_to_opgrad.copy()
|
||||
|
||||
# value -> list(value)
|
||||
state.valuegrad_to_value = self.valuegrad_to_value.copy()
|
||||
state.sumvaluegrad_to_value = self.sumvaluegrad_to_value.copy()
|
||||
# operation -> list(operation)
|
||||
state.opgrad_to_op = self.opgrad_to_op.copy()
|
||||
|
||||
# only for controlflow
|
||||
state.inside_value_to_outside_value_map = (
|
||||
self.inside_value_to_outside_value_map.copy()
|
||||
)
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def _check_vjp_dynamic_shape(op, inputs):
|
||||
for items in inputs:
|
||||
for item in items:
|
||||
if (
|
||||
item.is_dense_tensor_type()
|
||||
and item.initialized()
|
||||
and -1 in item.shape
|
||||
):
|
||||
return True
|
||||
|
||||
|
||||
# Prim currently does not support dynamic shape, when dynamic shape exits in shape of op inputs, prim will be skipped its vjp op.
|
||||
@signature_safe_contextmanager
|
||||
def dynamic_shape_prim_vjp_guard(op, inputs):
|
||||
origin_prim = core._is_bwd_prim_enabled()
|
||||
if op.name() == "cf.tuple_push":
|
||||
skip_prim = True
|
||||
else:
|
||||
skip_prim = (
|
||||
origin_prim
|
||||
and core._enable_prim_skip_dynamic_shape()
|
||||
and _check_vjp_dynamic_shape(op, inputs)
|
||||
and op.name() not in ALLOW_DYNAMIC_SHAPE_VJP_OPS
|
||||
)
|
||||
|
||||
try:
|
||||
if origin_prim and skip_prim:
|
||||
core._set_prim_backward_enabled(False)
|
||||
yield
|
||||
finally:
|
||||
if origin_prim:
|
||||
core._set_prim_backward_enabled(True)
|
||||
|
||||
|
||||
def check_type(input, input_name, expected_type, op_name, extra_message=''):
|
||||
if not isinstance(input, expected_type):
|
||||
raise TypeError(
|
||||
f"The type of '{input_name}' in {op_name} must be {expected_type}, but received {type(input)}. {extra_message}"
|
||||
)
|
||||
|
||||
|
||||
def _as_list(x):
|
||||
if x is None:
|
||||
return []
|
||||
return list(x) if isinstance(x, Sequence) else [x]
|
||||
|
||||
|
||||
def some_in_set(value_list, value_set):
|
||||
return any(v in value_set for v in value_list)
|
||||
|
||||
|
||||
def is_control_flow(op):
|
||||
return op.name() == "pd_op.if" or op.name() == "pd_op.while"
|
||||
|
||||
|
||||
def is_builtin_op(op):
|
||||
dialect_name, opname = op.name().split(".")
|
||||
return dialect_name == "builtin"
|
||||
|
||||
|
||||
def update_no_grad_set_by_stopgradient(block, no_grad_set):
|
||||
for op in block.ops:
|
||||
if is_control_flow(op):
|
||||
for sub_block in op.blocks():
|
||||
update_no_grad_set_by_stopgradient(sub_block, no_grad_set)
|
||||
for value in op.results():
|
||||
if value.stop_gradient and value not in no_grad_set:
|
||||
no_grad_set.add(value)
|
||||
|
||||
|
||||
def get_real_op_inputs(op):
|
||||
if op.name() == "pd_op.if":
|
||||
return get_used_external_value(op)
|
||||
elif op.name() == "pd_op.while":
|
||||
return op.operands_source() + get_used_external_value(
|
||||
op.as_while_op().body()
|
||||
)
|
||||
elif op.name() == "pd_op.pylayer":
|
||||
return get_used_external_value(op)
|
||||
else:
|
||||
return op.operands_source()
|
||||
|
||||
|
||||
def get_real_op_outputs(op):
|
||||
outputs = op.results()
|
||||
if op.name() == "pd_op.array_write_":
|
||||
for x in op.operands():
|
||||
outputs.append(x.source())
|
||||
if op.name() == "pd_op.while":
|
||||
for internal_op in op.as_while_op().body().ops:
|
||||
if internal_op.name() == "pd_op.array_write_":
|
||||
for x in internal_op.operands():
|
||||
outputs.append(x.source())
|
||||
return outputs
|
||||
|
||||
|
||||
def inverse_sort_op(old_ops):
|
||||
'''
|
||||
if topo graph is op1 -> op2 -> op3
|
||||
return [op3, op2, op1]
|
||||
|
||||
'''
|
||||
|
||||
# init pending_count[op] which describes number of
|
||||
# pending edges for its grad_op
|
||||
|
||||
pending_count = collections.defaultdict(int)
|
||||
ops = []
|
||||
[ops.append(x) for x in old_ops if x not in ops]
|
||||
ops_set = set(ops)
|
||||
sorted_list = []
|
||||
for op in ops:
|
||||
for x in get_real_op_inputs(op):
|
||||
if not pir.is_fake_value(x) and x.get_defining_op() in ops_set:
|
||||
pending_count[x.get_defining_op()] += 1
|
||||
|
||||
queue = collections.deque()
|
||||
|
||||
for op in ops:
|
||||
if pending_count[op] == 0:
|
||||
queue.append(op)
|
||||
|
||||
while queue:
|
||||
op = queue.popleft()
|
||||
sorted_list.append(op)
|
||||
for x in get_real_op_inputs(op):
|
||||
x_op = x.get_defining_op()
|
||||
pending_count[x_op] -= 1
|
||||
if pending_count[x_op] == 0:
|
||||
queue.append(x_op)
|
||||
|
||||
if len(sorted_list) != len(ops):
|
||||
raise ValueError(
|
||||
"inverse_sort_op wrong, sorted_list size is not equal to origin_list size"
|
||||
)
|
||||
change_list = []
|
||||
# true %0 = op1, 1% = increment(0%), 3% = op2(0%), tuple_push(%0, 1%, 3%),
|
||||
# no one use 1% so increment be the first op, actually op2 use 1% ,
|
||||
# sorted_list = [increment, op2, op1] should be [op2, increment, op1],
|
||||
# tuple_push(0%) must be forward last op, backward first op, so skip it.
|
||||
for op in reversed(sorted_list):
|
||||
if op.name() == 'pd_op.increment_':
|
||||
idx_1 = sorted_list.index(op)
|
||||
idx_2 = sorted_list.index(op)
|
||||
|
||||
for op_in in reversed(sorted_list[: sorted_list.index(op)]):
|
||||
if (
|
||||
some_in_set(
|
||||
op.operands_source(),
|
||||
ValueSet(get_real_op_inputs(op_in)),
|
||||
)
|
||||
and op_in.name() != "cf.tuple_push"
|
||||
):
|
||||
idx_2 = sorted_list.index(op_in)
|
||||
if idx_1 != idx_2:
|
||||
change_list.append((idx_1, idx_2))
|
||||
for idx_1, idx_2 in change_list:
|
||||
sorted_list[idx_1], sorted_list[idx_2] = (
|
||||
sorted_list[idx_2],
|
||||
sorted_list[idx_1],
|
||||
)
|
||||
|
||||
return sorted_list
|
||||
|
||||
|
||||
def is_inplace_net(op_list):
|
||||
'''
|
||||
when program has inplace op , it's difficult to find the actual pending_count.
|
||||
'''
|
||||
for op in op_list:
|
||||
if op.name() in ["pd_op.array_write_", "pd_op.assign_out_"]:
|
||||
return True
|
||||
if is_control_flow(op):
|
||||
for block in op.blocks():
|
||||
if is_inplace_net(block.ops):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def remove_op(block, op, state):
|
||||
'''
|
||||
remove op from block
|
||||
'''
|
||||
if state.opgrad_to_op[op] != []:
|
||||
fwd_op = state.opgrad_to_op[op][0]
|
||||
state.op_to_opgrad[fwd_op].remove(op)
|
||||
|
||||
for valuegrad in op.results():
|
||||
if state.valuegrad_to_value[valuegrad] != []:
|
||||
value = state.valuegrad_to_value[valuegrad][0]
|
||||
state.value_to_valuegrad[value] = []
|
||||
|
||||
if value in state.sumvaluegrad_to_value:
|
||||
raise ValueError(
|
||||
f'input_grad in [%s] is value which need to sum {op.name()}'
|
||||
)
|
||||
# NOTE(SigureMo): Ensure access to the op's results before removing it.
|
||||
# Otherwise, the op will be deconstructed and access the num_results
|
||||
# will be undefined behavior, it always cause hanging on the macOS.
|
||||
block.remove_op(op)
|
||||
|
||||
|
||||
def while_prune_check(while_tuple_ops):
|
||||
if len(while_tuple_ops) != 0:
|
||||
for opresult in while_tuple_ops[0].results():
|
||||
if not opresult.use_empty():
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def remove_useless_full_like_ops(block, ops, state):
|
||||
'''
|
||||
remove ops which are not in use recursively,
|
||||
|
||||
'''
|
||||
remove_ops = []
|
||||
inverse_ops = inverse_sort_op(list(ops))
|
||||
# from output to input
|
||||
for op in inverse_ops:
|
||||
if op.name() == "pd_op.full_like":
|
||||
if op.result(0).use_empty():
|
||||
full_op = op.operand_source(1).get_defining_op()
|
||||
remove_ops.append(op)
|
||||
remove_ops.append(full_op)
|
||||
elif is_control_flow(op):
|
||||
for sub_block in op.blocks():
|
||||
remove_useless_full_like_ops(sub_block, sub_block.ops, state)
|
||||
|
||||
for op in remove_ops:
|
||||
remove_op(block, op, state)
|
||||
|
||||
|
||||
def all_stop_gradient_true(block):
|
||||
for op in block.ops:
|
||||
for value in op.results():
|
||||
if value.stop_gradient is False:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def all_input_stop_gradient_true(list_of_list):
|
||||
for list_ in list_of_list:
|
||||
for stop_gradient in list_:
|
||||
if stop_gradient is False:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def all_output_grad_none(list_of_list):
|
||||
for list_ in list_of_list:
|
||||
for value in list_:
|
||||
if value is not None:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def op_has_vjp(op):
|
||||
# NOTE(MarioLulab): In PIR mode, even though the `PyLayer` op does
|
||||
# not have a vjp interface, we still need to generate the backward
|
||||
# block based on its registered backward function. To achieve this,
|
||||
# we add more handling logic for `PyLayer` Op in the `call_vjp` function
|
||||
return core.has_vjp(op) or op.name() == "pd_op.pylayer"
|
||||
|
||||
|
||||
def parent_total_ops(block):
|
||||
'''
|
||||
when block is sub_block, forward op should include its parent block ops
|
||||
(sub block nest should Add on demand to avoid block copy)
|
||||
'''
|
||||
total_ops = []
|
||||
if block.parent_block is not None:
|
||||
if block.parent_block.parent_block:
|
||||
total_ops += block.parent_block.parent_block.ops
|
||||
total_ops += block.parent_block.ops
|
||||
total_ops += block.ops
|
||||
|
||||
return total_ops
|
||||
|
||||
|
||||
# only for control_flow to find corresponding value or value_list
|
||||
def return_map_value(value, map):
|
||||
output = value
|
||||
while output in map:
|
||||
output = map[output]
|
||||
return output
|
||||
|
||||
|
||||
def return_map_value_list(value, map):
|
||||
output = []
|
||||
for i in range(len(value)):
|
||||
if value[i] in map:
|
||||
output.append(return_map_value(value[i], map))
|
||||
else:
|
||||
output.append(value[i])
|
||||
return output
|
||||
|
||||
|
||||
def argument_to_value(while_op):
|
||||
'''
|
||||
return while op's relationship of (block_argument to input value) and (input value to block_argument).
|
||||
'''
|
||||
if while_op.name() != "pd_op.while":
|
||||
return ValueDict(), ValueDict()
|
||||
|
||||
assert len(while_op.as_while_op().block_arguments()) + 1 == len(
|
||||
while_op.operands_source()
|
||||
), (
|
||||
"while op's block_arguments size + 1 should same to while op's operands_source size"
|
||||
)
|
||||
arg_to_value_map = ValueDict()
|
||||
value_to_arg_map = ValueDict()
|
||||
for arg, value in zip(
|
||||
while_op.as_while_op().block_arguments(),
|
||||
while_op.operands_source()[1:],
|
||||
):
|
||||
arg_to_value_map[arg] = value
|
||||
value_to_arg_map[value] = arg
|
||||
return arg_to_value_map, value_to_arg_map
|
||||
|
||||
|
||||
def get_grad_semantic_info(op):
|
||||
'''
|
||||
return whether op's inputs has grad, usually handled from yaml.
|
||||
some op has uncertain inputs need special handling.
|
||||
'''
|
||||
if op.name() in [
|
||||
"builtin.combine",
|
||||
"pd_op.if",
|
||||
"pd_op.while",
|
||||
"pd_op.pylayer",
|
||||
"cf.tuple_push",
|
||||
"dist_op.moe_global_mesh_tensor",
|
||||
"dist_op.moe_sub_mesh_tensors",
|
||||
"dist_op.dist_reshape",
|
||||
]:
|
||||
grad_semantic_info = [True for _ in range(len(get_real_op_inputs(op)))]
|
||||
if op.name() == "pd_op.if":
|
||||
grad_semantic_info[0] = False
|
||||
else:
|
||||
grad_semantic_info = op.get_input_grad_semantics()
|
||||
return grad_semantic_info
|
||||
|
||||
|
||||
def get_split_op(value):
|
||||
for op in value.all_used_ops():
|
||||
if op.name() == "builtin.split":
|
||||
return op
|
||||
return None
|
||||
|
||||
|
||||
@lru_cache
|
||||
def warning_once(message: str):
|
||||
logging.warning(message)
|
||||
|
||||
|
||||
def update_if_output_stopgradient(if_op, true_yield_op, false_yield_op):
|
||||
"""
|
||||
Update if_op's stop_gradient based on true_yield_op and false_yield_op.
|
||||
|
||||
Args:
|
||||
true_yield_op: true block of if_op's last op.
|
||||
false_yield_op: false block of if_op's last op.
|
||||
if_op: update it's op_results()'s stop_gradient.
|
||||
"""
|
||||
if (
|
||||
true_yield_op.name() != 'cf.yield'
|
||||
or false_yield_op.name() != 'cf.yield'
|
||||
):
|
||||
raise ValueError("param is not yield op")
|
||||
|
||||
# Check if operands_source sizes match
|
||||
if len(true_yield_op.operands_source()) != len(
|
||||
false_yield_op.operands_source()
|
||||
):
|
||||
raise ValueError("Mismatched yield operands_source sizes")
|
||||
|
||||
# Check if op_results size matches operands_source
|
||||
if len(if_op.results()) != len(true_yield_op.operands_source()):
|
||||
raise ValueError(
|
||||
"Mismatched if op_results size with yield operands_source"
|
||||
)
|
||||
|
||||
# Update if_op's stop_gradient
|
||||
for i in range(len(true_yield_op.operands_source())):
|
||||
stop_grad1 = true_yield_op.operand_source(i).stop_gradient
|
||||
stop_grad2 = false_yield_op.operand_source(i).stop_gradient
|
||||
|
||||
# Set to False if either stop_gradient is False
|
||||
if not stop_grad1 or not stop_grad2:
|
||||
if_op.result(i).stop_gradient = False
|
||||
|
||||
|
||||
def update_while_output_stopgradient(while_op, yield_op):
|
||||
"""
|
||||
Update while_op's stop_gradient based on yield_op.
|
||||
|
||||
Args:
|
||||
yield_op: The yield operation associated with the while loop.
|
||||
while_op: The while operation whose op_results()'s stop_gradient needs to be updated.
|
||||
"""
|
||||
# Check if yield_op is indeed a yield operation
|
||||
if yield_op.name() != 'cf.yield':
|
||||
raise ValueError("yield_op is not a yield operation")
|
||||
|
||||
# Check if operands_source size of yield_op matches op_results size of while_op
|
||||
if len(while_op.results()) + 1 != len(yield_op.operands_source()):
|
||||
raise ValueError(
|
||||
f"Mismatched while op_results size %d with yield operands_source %d. {len(while_op.results()) + 1, len(yield_op.operands_source())}"
|
||||
)
|
||||
|
||||
# Update while_op's stop_gradient
|
||||
for i in range(1, len(yield_op.operands_source())):
|
||||
stop_grad = yield_op.operand_source(i).stop_gradient
|
||||
|
||||
# Set to False if stop_gradient is False
|
||||
if not stop_grad:
|
||||
while_op.result(i - 1).stop_gradient = False
|
||||
|
||||
|
||||
def find_index_of_yield(value, yield_op):
|
||||
for i, v in enumerate(yield_op.operands_source()):
|
||||
if v.is_same(value):
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
def update_tuple_pop_origin_inputs(tuple_pop_outputs):
|
||||
if tuple_pop_outputs == []:
|
||||
return tuple_pop_outputs
|
||||
op = tuple_pop_outputs[0][0].get_defining_op()
|
||||
assert op.name() == "cf.tuple_pop"
|
||||
stack_op = op.operand_source(0).get_defining_op()
|
||||
tuple_push_inputs = stack_op.result(1).first_use().owner().operands_source()
|
||||
tuple_push_inputs_with_if = []
|
||||
for input in tuple_push_inputs:
|
||||
if input.first_use().owner().name() == "cf.yield":
|
||||
yield_op = input.first_use().owner()
|
||||
index = find_index_of_yield(input, yield_op)
|
||||
assert index != -1
|
||||
tuple_push_inputs_with_if.append(
|
||||
yield_op.get_parent_block().parent_op.result(index)
|
||||
)
|
||||
else:
|
||||
tuple_push_inputs_with_if.append(input)
|
||||
|
||||
# pass inlets
|
||||
return tuple_push_inputs_with_if[1:]
|
||||
|
||||
|
||||
def value_in_block(value, block):
|
||||
value_block = value.get_defining_op().get_parent_block()
|
||||
while block.parent_op.name() != "builtin.module":
|
||||
if block == value_block:
|
||||
return True
|
||||
block = block.parent_block
|
||||
# now block is module op's block
|
||||
if block == value_block:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2026 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 .py_layer import (
|
||||
PyLayerContext as FunctionCtx, # noqa: F401
|
||||
once_differentiable, # noqa: F401
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2025 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 paddle.base.dygraph.base import set_grad_enabled # noqa: F401
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,504 @@
|
||||
# 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, Any, Concatenate, TypeVar
|
||||
|
||||
import paddle
|
||||
from paddle.base import core
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Sequence
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
_RetT = TypeVar('_RetT')
|
||||
|
||||
|
||||
class PyLayerContext:
|
||||
"""
|
||||
``PyLayerContext`` can assist the :ref:`api_paddle_autograd_PyLayer` in implementing certain functionalities.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... # ctx is a object of PyLayerContext.
|
||||
... y = paddle.tanh(x)
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # ctx is a object of PyLayerContext.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
"""
|
||||
|
||||
container: tuple[Tensor, ...]
|
||||
not_inplace_tensors: tuple[Tensor, ...]
|
||||
non_differentiable: tuple[Tensor, ...]
|
||||
materialize_grads: bool
|
||||
grad_in_dtype_consistent: bool
|
||||
|
||||
def set_grad_in_dtype_consistent(self, flag: bool) -> None:
|
||||
"""
|
||||
Set whether to maintain gradient input dtype consistency between forward output and backward input.
|
||||
|
||||
Note:
|
||||
This API should be called only inside `forward`.
|
||||
By default, backward input gradients are automatically cast to match the dtype of forward outputs.
|
||||
Set this to `False` to disable automatic casting and maintain original gradient dtypes in backward.
|
||||
|
||||
Args:
|
||||
flag (bool): Whether to enable automatic dtype conversion in backward.
|
||||
- `True`: Cast backward input gradient to match forward output dtype (default behavior)
|
||||
- `False`: Preserve original dtype of backward input gradient
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
>>> paddle.seed(2025)
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... # The gradient input in the backward process
|
||||
... # will not be automatically cast to the dtype of the forward output.
|
||||
... ctx.set_grad_in_dtype_consistent(False)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
...
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
>>> class cus_tanh_cast_grad(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... # The gradient input in cus_tanh be cast to bfloat16 manually,
|
||||
... # and cus_tanh will not cast the gradient to the dtype of the forward output.
|
||||
... grad = paddle.cast(grad, paddle.float16)
|
||||
... return grad
|
||||
>>> x = paddle.randn([3, 3]).astype("float32")
|
||||
>>> x.stop_gradient = False
|
||||
>>> y = cus_tanh.apply(x)
|
||||
>>> z = cus_tanh_cast_grad.apply(y)
|
||||
>>> z.sum().backward()
|
||||
|
||||
"""
|
||||
self.grad_in_dtype_consistent = flag
|
||||
|
||||
def save_for_backward(self, *tensors: Tensor) -> None:
|
||||
"""
|
||||
Saves given tensors that backward need. Use ``saved_tensor`` in the `backward` to get the saved tensors.
|
||||
|
||||
Note:
|
||||
This API should be called at most once, and only inside `forward`.
|
||||
|
||||
Args:
|
||||
tensors(list of Tensors): Tensors to be stored.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... # ctx is a context object that store some objects for backward.
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
|
||||
"""
|
||||
self.container = tensors
|
||||
|
||||
def saved_tensor(self) -> tuple[Tensor, ...]:
|
||||
"""
|
||||
Get the tensors stored by ``save_for_backward``.
|
||||
|
||||
Returns:
|
||||
list of Tensors or None: If context contains tensors stored by `save_for_backward`,
|
||||
then return these tensors, otherwise return None.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... # ctx is a context object that store some objects for backward.
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
"""
|
||||
return self.container
|
||||
|
||||
@property
|
||||
def saved_tensors(self):
|
||||
"""
|
||||
Get the tensors stored by ``save_for_backward``. This attribute is an alias for the method ``saved_tensor()``.
|
||||
|
||||
Returns:
|
||||
list of Tensors or None: If context contains tensors stored by `save_for_backward`,
|
||||
then return these tensors, otherwise return None.
|
||||
"""
|
||||
return self.saved_tensor()
|
||||
|
||||
def mark_not_inplace(self, *args: Tensor) -> None:
|
||||
"""
|
||||
Marks inputs as not inplace.
|
||||
This should be called at most once, only from inside the `forward` method,
|
||||
and all arguments should be Tensor inputs.
|
||||
|
||||
If the Tensor returned by `forward` method is the same as the Tensor input of forward,
|
||||
and this Tensor is marked as not_inplace, then Paddle will help the user create a new Tensor as output.
|
||||
Thereby preventing the auto grad information of the input Tensor from being overwritten.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> class Exp(paddle.autograd.PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... ctx.mark_not_inplace(x)
|
||||
... return x
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, grad_output):
|
||||
... out = grad_output.exp()
|
||||
... return out
|
||||
|
||||
>>> paddle.seed(2023)
|
||||
>>> x = paddle.randn((1, 1))
|
||||
>>> x.stop_gradient = False
|
||||
>>> attn_layers = []
|
||||
>>> for idx in range(0, 2):
|
||||
... attn_layers.append(Exp())
|
||||
|
||||
>>> for step in range(0, 2):
|
||||
... a = x
|
||||
... for j in range(0, 2):
|
||||
... a = attn_layers[j].apply(x)
|
||||
... a.backward()
|
||||
"""
|
||||
self.not_inplace_tensors = args
|
||||
|
||||
def mark_non_differentiable(self, *args: Tensor) -> None:
|
||||
"""
|
||||
Marks outputs as non-differentiable.
|
||||
This should be called at most once, only from inside the `forward` method,
|
||||
and all arguments should be tensor outputs.
|
||||
|
||||
This will mark outputs as not requiring gradients, increasing the
|
||||
efficiency of backward computation. You still need to accept a gradient
|
||||
for each output in `backward`, but it's always going to
|
||||
be a zero tensor with the same shape as the shape of a corresponding
|
||||
output.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
>>> import numpy as np
|
||||
|
||||
>>> class Tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... a = x + x
|
||||
... b = x + x + x
|
||||
... ctx.mark_non_differentiable(a)
|
||||
... return a, b
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, grad_a, grad_b):
|
||||
... assert np.equal(grad_a.numpy(), paddle.zeros([1]).numpy())
|
||||
... assert np.equal(grad_b.numpy(), paddle.ones([1], dtype="float64").numpy())
|
||||
... return grad_b
|
||||
|
||||
>>> x = paddle.ones([1], dtype="float64")
|
||||
>>> x.stop_gradient = False
|
||||
>>> a, b = Tanh.apply(x)
|
||||
>>> b.sum().backward()
|
||||
"""
|
||||
self.non_differentiable = args
|
||||
|
||||
def set_materialize_grads(self, value: bool) -> None:
|
||||
"""
|
||||
Sets whether to materialize output grad tensors. Default is True.
|
||||
|
||||
This should be called only from inside the `forward` method.
|
||||
|
||||
If True, undefined output grad tensors will be expanded to tensors full
|
||||
of zeros prior to calling the `backward` method.
|
||||
|
||||
If False, undefined output grad tensors will be None.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
>>> import numpy as np
|
||||
|
||||
>>> class Tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... return x + x + x, x + x
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, grad, grad2):
|
||||
... assert np.equal(grad2.numpy(), paddle.zeros([1]).numpy())
|
||||
... return grad
|
||||
|
||||
>>> class Tanh2(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... ctx.set_materialize_grads(False)
|
||||
... return x + x + x, x + x
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, grad, grad2):
|
||||
... assert grad2 == None
|
||||
... return grad
|
||||
|
||||
>>> x = paddle.ones([1], dtype="float64")
|
||||
>>> x.stop_gradient = False
|
||||
>>> Tanh.apply(x)[0].backward()
|
||||
|
||||
>>> x2 = paddle.ones([1], dtype="float64")
|
||||
>>> x2.stop_gradient = False
|
||||
>>> Tanh2.apply(x2)[0].backward()
|
||||
"""
|
||||
self.materialize_grads = value
|
||||
|
||||
|
||||
class PyLayerBackward(core.eager.PyLayer, PyLayerContext):
|
||||
def backward(self, *args):
|
||||
return self._forward_cls.backward(self, *args)
|
||||
|
||||
|
||||
class PyLayerMeta(type):
|
||||
def __init__(cls, name, bases, attrs):
|
||||
cls._backward_function = type(
|
||||
name + '_backward', (PyLayerBackward,), {"_forward_cls": cls}
|
||||
)
|
||||
|
||||
super().__init__(name, bases, attrs)
|
||||
|
||||
|
||||
class PyLayer(core.eager.PyLayer, PyLayerContext, metaclass=PyLayerMeta):
|
||||
"""
|
||||
Paddle implements Python custom operators on the PaddlePaddle framework by creating a subclass of
|
||||
``PyLayer``, which must comply with the following rules:
|
||||
|
||||
1. The subclass must contain static ``forward`` and ``backward`` functions, with the first argument being
|
||||
:ref:`api_paddle_autograd_PyLayerContext`. If a returned value in ``backward`` corresponds to a ``Tensor`` that
|
||||
requires gradients in ``forward``, the returned value must be a ``Tensor``.
|
||||
|
||||
2. Except for the first argument, other arguments of ``backward`` are gradients of the output ``Tensors``
|
||||
of ``forward``. Therefore, the number of input ``Tensor`` in ``backward`` must be the same as the number
|
||||
of output ``Tensor`` in ``forward``. If you need to use input ``Tensor`` from ``forward`` in ``backward``,
|
||||
you can save these ``Tensors`` by inputting them into :ref:`api_paddle_autograd_PyLayerContext`'s
|
||||
``save_for_backward`` method and use them in ``backward`` later.
|
||||
|
||||
3. The output of ``backward`` can be ``Tensor`` or ``list/tuple(Tensor)``, which are gradients of the
|
||||
output ``Tensor`` of ``forward``. Therefore, the number of output ``Tensor`` in ``backward`` is the same
|
||||
as the number of input ``Tensor`` in ``forward``.
|
||||
|
||||
After building the custom operator, apply it by running the ``apply`` method.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
|
||||
>>> paddle.seed(2023)
|
||||
>>> data = paddle.randn([2, 3], dtype="float64")
|
||||
>>> data.stop_gradient = False
|
||||
>>> z = cus_tanh.apply(data)
|
||||
>>> z.mean().backward()
|
||||
|
||||
>>> print(data.grad)
|
||||
Tensor(shape=[2, 3], dtype=float64, place=Place(cpu), stop_gradient=True,
|
||||
[[0.16604150, 0.05858341, 0.14051214],
|
||||
[0.15677770, 0.01564609, 0.02991660]])
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def forward(
|
||||
ctx: PyLayerContext, *args: Any, **kwargs: Any
|
||||
) -> Tensor | Sequence[Tensor]:
|
||||
"""
|
||||
It is to be overloaded by subclasses. It must accept a object of :ref:`api_paddle_autograd_PyLayerContext` as
|
||||
the first argument, followed by any number of arguments (tensors or other types).
|
||||
`None` can not be included in the returned result.
|
||||
|
||||
Args:
|
||||
*args(tuple): input of PyLayer.
|
||||
**kwargs(dict): input of PyLayer.
|
||||
|
||||
Returns:
|
||||
tensors or other types : output of PyLayer.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"You must implement the forward function for PyLayer."
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def backward(ctx: PyLayerContext, *args: Any) -> Tensor | Sequence[Tensor]:
|
||||
"""
|
||||
This is a function to calculate the gradient. It is to be overloaded by subclasses.
|
||||
It must accept a object of :ref:`api_paddle_autograd_PyLayerContext` as the first
|
||||
argument, and the rest arguments are the gradient of forward's output tensors.
|
||||
Output tensors of backward are the gradient of forward's input tensors.
|
||||
|
||||
Args:
|
||||
*args(tuple): The gradient of forward's output tensor(s).
|
||||
**kwargs(dict): The gradient of forward's output tensor(s).
|
||||
|
||||
Returns:
|
||||
Tensor or list of Tensors: The gradient of forward's input tensor(s).
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_tanh(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, x):
|
||||
... y = paddle.tanh(x)
|
||||
... # Pass tensors to backward.
|
||||
... ctx.save_for_backward(y)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... # Get the tensors passed by forward.
|
||||
... (y,) = ctx.saved_tensor()
|
||||
... grad = dy * (1 - paddle.square(y))
|
||||
... return grad
|
||||
"""
|
||||
|
||||
raise NotImplementedError(
|
||||
"You must implement the backward function for PyLayer."
|
||||
)
|
||||
|
||||
|
||||
def once_differentiable(
|
||||
backward: Callable[Concatenate[PyLayerContext, ...], _RetT],
|
||||
) -> Callable[Concatenate[PyLayerContext, ...], _RetT]:
|
||||
def wrapper(ctx: PyLayerContext, *args: Any) -> _RetT:
|
||||
with paddle.base.dygraph.no_grad():
|
||||
outputs = backward(ctx, *args)
|
||||
return outputs
|
||||
|
||||
return wrapper
|
||||
@@ -0,0 +1,127 @@
|
||||
# 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, Any
|
||||
|
||||
from paddle.base import core
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from paddle import Tensor
|
||||
__all__ = []
|
||||
|
||||
|
||||
class saved_tensors_hooks:
|
||||
"""
|
||||
Dynamic graph, registers a pair of pack / unpack hooks for saved tensors.
|
||||
|
||||
Parameters:
|
||||
pack_hook (function): The pack hook will be called every time the forward
|
||||
operation inputs/outputs tensors need be saved for backward. Then you
|
||||
can save it to CPU or Disk. The input of `pack_hook` is a tensor need
|
||||
be saved. The output of `pack_hook` is then stored information instead
|
||||
of the original tensor. `pack_hook` will also be called while any
|
||||
tensor need be saved by `PyLayerContext.save_for_backward`. If a tensor
|
||||
saved for backward is no need buffer, `pack_hook` will not be called.
|
||||
Only the tensor saved for backward is DenseTensor, `pack_hook` will be
|
||||
called.
|
||||
unpack_hook (function): The unpack hook will be called every time the
|
||||
backward need use the saved inputs/outputs tensors. Then you can reload
|
||||
the tensor and return it to paddle framework. The input of `unpack_hook`
|
||||
is the information returned by `pack_hook`. The output of `unpack_hook`
|
||||
is a tensor reloaded by the information, and the tensor must has the same
|
||||
content as the original tensor passed as input to the corresponding
|
||||
`pack_hook`.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
:name: code-example1
|
||||
|
||||
>>> # Example1
|
||||
>>> import paddle
|
||||
|
||||
>>> def pack_hook(x):
|
||||
... print("Packing", x)
|
||||
... return x.numpy()
|
||||
|
||||
>>> def unpack_hook(x):
|
||||
... print("UnPacking", x)
|
||||
... return paddle.to_tensor(x)
|
||||
|
||||
>>> a = paddle.ones([3, 3])
|
||||
>>> b = paddle.ones([3, 3]) * 2
|
||||
>>> a.stop_gradient = False
|
||||
>>> b.stop_gradient = False
|
||||
>>> with paddle.autograd.saved_tensors_hooks(pack_hook, unpack_hook):
|
||||
... y = paddle.multiply(a, b)
|
||||
>>> y.sum().backward()
|
||||
|
||||
.. code-block:: pycon
|
||||
:name: code-example2
|
||||
|
||||
>>> # Example2
|
||||
>>> import paddle
|
||||
>>> from paddle.autograd import PyLayer
|
||||
|
||||
>>> class cus_multiply(PyLayer):
|
||||
... @staticmethod
|
||||
... def forward(ctx, a, b):
|
||||
... y = paddle.multiply(a, b)
|
||||
... ctx.save_for_backward(a, b)
|
||||
... return y
|
||||
...
|
||||
... @staticmethod
|
||||
... def backward(ctx, dy):
|
||||
... a, b = ctx.saved_tensor()
|
||||
... grad_a = dy * a
|
||||
... grad_b = dy * b
|
||||
... return grad_a, grad_b
|
||||
|
||||
>>> def pack_hook(x):
|
||||
... print("Packing", x)
|
||||
... return x.numpy()
|
||||
|
||||
>>> def unpack_hook(x):
|
||||
... print("UnPacking", x)
|
||||
... return paddle.to_tensor(x)
|
||||
|
||||
>>> a = paddle.ones([3, 3])
|
||||
>>> b = paddle.ones([3, 3]) * 2
|
||||
>>> a.stop_gradient = False
|
||||
>>> b.stop_gradient = False
|
||||
>>> with paddle.autograd.saved_tensors_hooks(pack_hook, unpack_hook):
|
||||
... y = cus_multiply.apply(a, b)
|
||||
>>> y.sum().backward()
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
pack_hook: Callable[[Tensor], Any | None],
|
||||
unpack_hook: Callable[[Any], Tensor | None],
|
||||
) -> None:
|
||||
self.pack_hook = pack_hook
|
||||
self.unpack_hook = unpack_hook
|
||||
|
||||
def __enter__(self) -> None:
|
||||
core.eager.register_saved_tensors_hooks(
|
||||
self.pack_hook, self.unpack_hook
|
||||
)
|
||||
|
||||
def __exit__(self, *args: object) -> None:
|
||||
core.eager.reset_saved_tensors_hooks()
|
||||
@@ -0,0 +1,3 @@
|
||||
proto
|
||||
core.so
|
||||
*.so
|
||||
@@ -0,0 +1,213 @@
|
||||
# Copyright (c) 2018 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.
|
||||
|
||||
import atexit
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
# The legacy core need to be removed before "import core",
|
||||
# in case of users installing paddlepaddle without -U option
|
||||
core_suffix = 'so'
|
||||
if os.name == 'nt':
|
||||
core_suffix = 'pyd'
|
||||
|
||||
legacy_core = (
|
||||
os.path.abspath(os.path.dirname(__file__)) + os.sep + 'core.' + core_suffix
|
||||
)
|
||||
if os.path.exists(legacy_core):
|
||||
sys.stderr.write('Deleting legacy file ' + legacy_core + '\n')
|
||||
try:
|
||||
os.remove(legacy_core)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
# import all class inside framework into base module
|
||||
# import all class inside executor into base module
|
||||
from . import ( # noqa: F401
|
||||
backward,
|
||||
compiler,
|
||||
core,
|
||||
data_feed_desc,
|
||||
dataset,
|
||||
dygraph,
|
||||
executor,
|
||||
framework,
|
||||
incubate,
|
||||
initializer,
|
||||
io,
|
||||
layers,
|
||||
trainer_desc,
|
||||
unique_name,
|
||||
)
|
||||
from .backward import ( # noqa: F401
|
||||
append_backward,
|
||||
gradients,
|
||||
)
|
||||
from .compiler import ( # noqa: F401
|
||||
BuildStrategy,
|
||||
CompiledProgram,
|
||||
IpuCompiledProgram,
|
||||
IpuStrategy,
|
||||
)
|
||||
from .core import ( # noqa: F401
|
||||
CPUPlace,
|
||||
CUDAPinnedPlace,
|
||||
CUDAPlace,
|
||||
CustomPlace,
|
||||
DenseTensor,
|
||||
DenseTensorArray,
|
||||
IPUPlace,
|
||||
Scope,
|
||||
XPUPinnedPlace,
|
||||
XPUPlace,
|
||||
_check_last_cuda_error,
|
||||
_cuda_synchronize,
|
||||
_Scope,
|
||||
_set_warmup,
|
||||
)
|
||||
from .data_feed_desc import DataFeedDesc # noqa: F401
|
||||
from .data_feeder import DataFeeder # noqa: F401
|
||||
from .dataset import ( # noqa: F401
|
||||
DatasetFactory,
|
||||
InMemoryDataset,
|
||||
)
|
||||
from .dygraph.base import disable_dygraph, enable_dygraph
|
||||
from .dygraph.tensor_patch_methods import monkey_patch_tensor
|
||||
from .executor import ( # noqa: F401
|
||||
Executor,
|
||||
global_scope,
|
||||
scope_guard,
|
||||
)
|
||||
from .framework import ( # noqa: F401
|
||||
Program,
|
||||
Variable,
|
||||
cpu_places,
|
||||
cuda_pinned_places,
|
||||
cuda_places,
|
||||
default_main_program,
|
||||
default_startup_program,
|
||||
device_guard,
|
||||
get_flags,
|
||||
in_dygraph_mode,
|
||||
in_dynamic_or_pir_mode,
|
||||
in_pir_mode,
|
||||
ipu_shard_guard,
|
||||
is_compiled_with_cinn,
|
||||
is_compiled_with_cuda,
|
||||
is_compiled_with_rocm,
|
||||
is_compiled_with_xpu,
|
||||
name_scope,
|
||||
process_type_promotion,
|
||||
program_guard,
|
||||
require_version,
|
||||
set_flags,
|
||||
set_ipu_shard,
|
||||
xpu_places,
|
||||
)
|
||||
from .initializer import set_global_initializer # noqa: F401
|
||||
from .layers.math_op_patch import monkey_patch_variable
|
||||
from .lod_tensor import ( # noqa: F401
|
||||
create_lod_tensor,
|
||||
create_random_int_lodtensor,
|
||||
)
|
||||
from .param_attr import ParamAttr, WeightNormParamAttr # noqa: F401
|
||||
from .trainer_desc import ( # noqa: F401
|
||||
MultiTrainer,
|
||||
TrainerDesc,
|
||||
)
|
||||
|
||||
Tensor = DenseTensor
|
||||
enable_imperative = enable_dygraph
|
||||
disable_imperative = disable_dygraph
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def __bootstrap__():
|
||||
"""
|
||||
Enable reading gflags from environment variables.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
num_threads = int(os.getenv('OMP_NUM_THREADS', '1'))
|
||||
except ValueError:
|
||||
num_threads = 1
|
||||
|
||||
if num_threads > 1:
|
||||
print(
|
||||
f'WARNING: OMP_NUM_THREADS set to {num_threads}, not 1. The computation '
|
||||
'speed will not be optimized if you use data parallel. It will '
|
||||
'fail if this PaddlePaddle binary is compiled with OpenBlas since'
|
||||
' OpenBlas does not support multi-threads.',
|
||||
file=sys.stderr,
|
||||
)
|
||||
print('PLEASE USE OMP_NUM_THREADS WISELY.', file=sys.stderr)
|
||||
|
||||
os.environ['OMP_NUM_THREADS'] = str(num_threads)
|
||||
|
||||
flag_prefix = "FLAGS_"
|
||||
read_env_flags = [
|
||||
key[len(flag_prefix) :]
|
||||
for key in core.globals().keys()
|
||||
if key.startswith(flag_prefix)
|
||||
]
|
||||
|
||||
def remove_flag_if_exists(name):
|
||||
if name in read_env_flags:
|
||||
read_env_flags.remove(name)
|
||||
|
||||
sysstr = platform.system()
|
||||
if 'Darwin' in sysstr:
|
||||
remove_flag_if_exists('use_pinned_memory')
|
||||
|
||||
if core.is_compiled_with_ipu():
|
||||
# Currently we request all ipu available for training and testing
|
||||
# finer control of pod of IPUs will be added later
|
||||
read_env_flags += []
|
||||
|
||||
core.init_gflags(["--tryfromenv=" + ",".join(read_env_flags)])
|
||||
# Note(zhouwei25): sys may not have argv in some cases,
|
||||
# Such as: use Python/C API to call Python from C++
|
||||
try:
|
||||
core.init_glog(sys.argv[0])
|
||||
except Exception:
|
||||
sys.argv = [""]
|
||||
core.init_glog(sys.argv[0])
|
||||
# don't init_p2p when in unittest to save time.
|
||||
core.init_memory_method()
|
||||
core.init_devices()
|
||||
core.init_gflags_from_env()
|
||||
core.init_tensor_operants()
|
||||
core.init_default_kernel_signatures()
|
||||
|
||||
|
||||
# TODO(panyx0718): Avoid doing complex initialization logic in __init__.py.
|
||||
# Consider paddle.init(args) or paddle.main(args)
|
||||
monkey_patch_variable()
|
||||
__bootstrap__()
|
||||
monkey_patch_tensor()
|
||||
|
||||
# NOTE(Aurelius84): clean up ExecutorCacheInfo in advance manually.
|
||||
atexit.register(core.clear_executor_cache)
|
||||
atexit.register(core.pir.clear_cinn_compilation_cache)
|
||||
|
||||
# NOTE(Aganlengzi): clean up KernelFactory in advance manually.
|
||||
# NOTE(wangran16): clean up DeviceManager in advance manually.
|
||||
# Keep clear_kernel_factory running before clear_device_manager
|
||||
atexit.register(core.clear_device_manager)
|
||||
atexit.register(core.clear_kernel_factory)
|
||||
atexit.register(core.ProcessGroupIdMap.destroy)
|
||||
Executable
+2946
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,652 @@
|
||||
# Copyright (c) 2019 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.
|
||||
|
||||
import os
|
||||
import platform
|
||||
import site
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
has_paddle_dy_lib = False
|
||||
|
||||
dy_lib_name = 'libpaddle'
|
||||
dy_lib_suffix = 'so'
|
||||
if os.name == 'nt':
|
||||
dy_lib_suffix = 'pyd'
|
||||
|
||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||
if os.path.exists(current_path + os.sep + dy_lib_name + '.' + dy_lib_suffix):
|
||||
has_paddle_dy_lib = True
|
||||
|
||||
|
||||
try:
|
||||
if os.name == 'nt':
|
||||
third_lib_path = current_path + os.sep + '..' + os.sep + 'libs'
|
||||
# Will load shared library from 'path' on windows
|
||||
os.environ['path'] = (
|
||||
current_path + ';' + third_lib_path + ';' + os.environ['path']
|
||||
)
|
||||
sys.path.insert(0, third_lib_path)
|
||||
# Note: from python3.8, PATH will not take effect
|
||||
# https://github.com/python/cpython/pull/12302
|
||||
# Use add_dll_directory to specify dll resolution path
|
||||
os.add_dll_directory(third_lib_path)
|
||||
|
||||
except ImportError as e:
|
||||
if os.name == 'nt':
|
||||
executable_path = os.path.abspath(os.path.dirname(sys.executable))
|
||||
raise ImportError(
|
||||
f"""NOTE: You may need to run \"set PATH={executable_path};%PATH%\"
|
||||
if you encounters \"DLL load failed\" errors. If you have python
|
||||
installed in other directory, replace \"{executable_path}\" with your own
|
||||
directory. The original error is: \n {e}"""
|
||||
)
|
||||
else:
|
||||
raise ImportError(
|
||||
"""NOTE: You may need to run \"export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH\"
|
||||
if you encounters \"libmkldnn.so not found\" errors. If you have python
|
||||
installed in other directory, replace \"/usr/local/lib\" with your own
|
||||
directory. The original error is: \n"""
|
||||
+ str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def avx_supported():
|
||||
"""
|
||||
Whether current system(Linux, MacOS, Windows) is supported with AVX.
|
||||
"""
|
||||
sysstr = platform.system().lower()
|
||||
has_avx = False
|
||||
if sysstr == 'linux':
|
||||
try:
|
||||
pipe = os.popen('cat /proc/cpuinfo | grep -i avx')
|
||||
has_avx = pipe.read() != ''
|
||||
pipe.close()
|
||||
except Exception as e:
|
||||
sys.stderr.write(
|
||||
'Can not get the AVX flag from /proc/cpuinfo.\n'
|
||||
f'The original error is: {e}\n'
|
||||
)
|
||||
return has_avx
|
||||
elif sysstr == 'darwin':
|
||||
try:
|
||||
pipe = os.popen('sysctl machdep.cpu.features | grep -i avx')
|
||||
has_avx = pipe.read() != ''
|
||||
pipe.close()
|
||||
except Exception as e:
|
||||
sys.stderr.write(
|
||||
'Can not get the AVX flag from machdep.cpu.features.\n'
|
||||
f'The original error is: {e}\n'
|
||||
)
|
||||
if not has_avx:
|
||||
import subprocess
|
||||
|
||||
pipe = subprocess.Popen(
|
||||
'sysctl machdep.cpu.leaf7_features | grep -i avx',
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
_ = pipe.communicate()
|
||||
has_avx = True if pipe.returncode == 0 else False
|
||||
return has_avx
|
||||
elif sysstr == 'windows':
|
||||
import ctypes
|
||||
|
||||
ONE_PAGE = ctypes.c_size_t(0x1000)
|
||||
|
||||
def asm_func(code_str, restype=ctypes.c_uint32, argtypes=()):
|
||||
# Call the code_str as a function
|
||||
# Alloc 1 page to ensure the protection
|
||||
pfnVirtualAlloc = ctypes.windll.kernel32.VirtualAlloc
|
||||
pfnVirtualAlloc.restype = ctypes.c_void_p
|
||||
MEM_COMMIT = ctypes.c_ulong(0x1000)
|
||||
PAGE_READWRITE = ctypes.c_ulong(0x4)
|
||||
address = pfnVirtualAlloc(
|
||||
None, ONE_PAGE, MEM_COMMIT, PAGE_READWRITE
|
||||
)
|
||||
if not address:
|
||||
raise Exception("Failed to VirtualAlloc")
|
||||
|
||||
# Copy the code into the memory segment
|
||||
memmove = ctypes.CFUNCTYPE(
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_size_t,
|
||||
)(ctypes._memmove_addr)
|
||||
if memmove(address, code_str, len(code_str)) < 0:
|
||||
raise Exception("Failed to memmove")
|
||||
|
||||
# Enable execute permissions
|
||||
PAGE_EXECUTE = ctypes.c_ulong(0x10)
|
||||
pfnVirtualProtect = ctypes.windll.kernel32.VirtualProtect
|
||||
res = pfnVirtualProtect(
|
||||
ctypes.c_void_p(address),
|
||||
ONE_PAGE,
|
||||
PAGE_EXECUTE,
|
||||
ctypes.byref(ctypes.c_ulong(0)),
|
||||
)
|
||||
if not res:
|
||||
raise Exception("Failed VirtualProtect")
|
||||
|
||||
# Flush instruction cache
|
||||
pfnGetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
|
||||
pfnGetCurrentProcess.restype = ctypes.c_void_p
|
||||
prochandle = ctypes.c_void_p(pfnGetCurrentProcess())
|
||||
res = ctypes.windll.kernel32.FlushInstructionCache(
|
||||
prochandle, ctypes.c_void_p(address), ONE_PAGE
|
||||
)
|
||||
if not res:
|
||||
raise Exception("Failed FlushInstructionCache")
|
||||
|
||||
# Cast the memory to function
|
||||
functype = ctypes.CFUNCTYPE(restype, *argtypes)
|
||||
func = functype(address)
|
||||
return func, address
|
||||
|
||||
# http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
|
||||
# mov eax,0x1; cpuid; mov cx, ax; ret
|
||||
code_str = b"\xb8\x01\x00\x00\x00\x0f\xa2\x89\xc8\xc3"
|
||||
avx_bit = 28
|
||||
retval = 0
|
||||
try:
|
||||
# Convert the code_str into a function that returns uint
|
||||
func, address = asm_func(code_str)
|
||||
retval = func()
|
||||
ctypes.windll.kernel32.VirtualFree(
|
||||
ctypes.c_void_p(address), ctypes.c_size_t(0), ONE_PAGE
|
||||
)
|
||||
except Exception as e:
|
||||
sys.stderr.write(
|
||||
'Failed getting the AVX flag on Windows.\n'
|
||||
f'The original error is: {e}\n'
|
||||
)
|
||||
return (retval & (1 << avx_bit)) > 0
|
||||
else:
|
||||
sys.stderr.write(f'Do not get AVX flag on {sysstr}\n')
|
||||
return False
|
||||
|
||||
|
||||
def run_shell_command(cmd):
|
||||
import subprocess
|
||||
|
||||
out, err = subprocess.Popen(
|
||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
|
||||
).communicate()
|
||||
if err:
|
||||
return None
|
||||
else:
|
||||
return out.decode('utf-8').strip()
|
||||
|
||||
|
||||
def get_dso_path(core_so, dso_name):
|
||||
if core_so and dso_name:
|
||||
return run_shell_command(
|
||||
f"ldd {core_so}|grep {dso_name}|awk '{{print $3}}'"
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def load_dso(dso_absolute_path):
|
||||
if dso_absolute_path:
|
||||
try:
|
||||
from ctypes import cdll
|
||||
|
||||
cdll.LoadLibrary(dso_absolute_path)
|
||||
except:
|
||||
warnings.warn(f"Load {dso_absolute_path} failed")
|
||||
|
||||
|
||||
def pre_load(dso_name):
|
||||
if has_paddle_dy_lib:
|
||||
core_so = current_path + os.sep + dy_lib_name + '.' + dy_lib_suffix
|
||||
else:
|
||||
core_so = None
|
||||
dso_path = get_dso_path(core_so, dso_name)
|
||||
load_dso(dso_path)
|
||||
|
||||
|
||||
def get_libc_ver():
|
||||
ldd_glibc = run_shell_command("ldd --version | awk '/ldd/{print $NF}'")
|
||||
if ldd_glibc is not None:
|
||||
return ("glibc", ldd_glibc)
|
||||
|
||||
ldd_musl = run_shell_command("ldd 2>&1 | awk '/Version/{print $NF}'")
|
||||
if ldd_musl is not None:
|
||||
return ("musl", ldd_musl)
|
||||
return (None, None)
|
||||
|
||||
|
||||
def less_than_ver(a, b):
|
||||
if a is None or b is None:
|
||||
return False
|
||||
|
||||
import operator
|
||||
import re
|
||||
|
||||
def to_list(s):
|
||||
s = re.sub(r'(\.0+)+$', '', s)
|
||||
return [int(x) for x in s.split('.')]
|
||||
|
||||
return operator.lt(to_list(a), to_list(b))
|
||||
|
||||
|
||||
# NOTE(zhiqiu): An error may occurs when import paddle in linux platform with glibc < 2.22,
|
||||
# the error message of which is "dlopen: cannot load any more object with static TLS".
|
||||
# This happens when:
|
||||
# (1) the number of dynamic shared libraries (DSO) loaded > 14,
|
||||
# (2) after that, load a dynamic shared library (DSO) with static TLS.
|
||||
# For paddle, the problem is that 'libgomp' is a DSO with static TLS, and it is loaded after 14 DSOs.
|
||||
# So, here is a tricky way to solve the problem by pre load 'libgomp' before 'libpaddle.so'.
|
||||
# The final solution is to upgrade glibc to > 2.22 on the target system.
|
||||
if platform.system().lower() == 'linux':
|
||||
libc_type, libc_ver = get_libc_ver()
|
||||
if libc_type == 'glibc' and less_than_ver(libc_ver, '2.23'):
|
||||
try:
|
||||
pre_load('libgomp')
|
||||
except Exception as e:
|
||||
# NOTE(zhiqiu): do not abort if failed, since it may success when import libpaddle.so
|
||||
sys.stderr.write('Error: Can not preload libgomp.so')
|
||||
|
||||
try:
|
||||
from . import libpaddle
|
||||
|
||||
if avx_supported() and not libpaddle.is_compiled_with_avx():
|
||||
sys.stderr.write(
|
||||
"Hint: Your machine support AVX, but the installed paddlepaddle doesn't have avx core. "
|
||||
"Hence, no-avx core with worse performance will be imported.\nIf you like, you could "
|
||||
"reinstall paddlepaddle by 'python -m pip install --force-reinstall paddlepaddle-gpu[==version]' "
|
||||
"to get better performance.\n"
|
||||
)
|
||||
|
||||
# assign tensor alias
|
||||
libpaddle.LoDTensor = libpaddle.DenseTensor
|
||||
libpaddle.Tensor = libpaddle.DenseTensor
|
||||
libpaddle.VarDesc.VarType.LOD_TENSOR = (
|
||||
libpaddle.VarDesc.VarType.DENSE_TENSOR
|
||||
)
|
||||
libpaddle.VarDesc.VarType.LOD_TENSOR_ARRAY = (
|
||||
libpaddle.VarDesc.VarType.DENSE_TENSOR_ARRAY
|
||||
)
|
||||
|
||||
from .libpaddle import * # noqa: F403
|
||||
from .libpaddle import ( # noqa: F401
|
||||
__doc__,
|
||||
__file__,
|
||||
__name__,
|
||||
__package__,
|
||||
__unittest_throw_exception__,
|
||||
_append_python_callable_object_and_return_id,
|
||||
_check_last_cuda_error,
|
||||
_cleanup,
|
||||
_create_loaded_parameter,
|
||||
_cuda_synchronize,
|
||||
_device_synchronize,
|
||||
_dygraph_debug_level,
|
||||
_get_all_register_op_kernels,
|
||||
_get_amp_attrs,
|
||||
_get_amp_op_list,
|
||||
_get_current_stream,
|
||||
_get_eager_deletion_vars,
|
||||
_get_legacy_default_stream,
|
||||
_get_phi_kernel_name,
|
||||
_get_registered_phi_kernels,
|
||||
_get_stream_from_external,
|
||||
_get_use_default_grad_op_desc_maker_ops,
|
||||
_has_grad,
|
||||
_is_compiled_with_heterps,
|
||||
_is_dygraph_debug_enabled,
|
||||
_is_program_version_supported,
|
||||
_Profiler,
|
||||
_ProfilerResult,
|
||||
_promote_types_if_complex_exists,
|
||||
_RecordEvent,
|
||||
_Scope,
|
||||
_set_amp_op_list,
|
||||
_set_current_stream,
|
||||
_set_eager_deletion_mode,
|
||||
_set_fuse_parameter_group_size,
|
||||
_set_fuse_parameter_memory_size,
|
||||
_set_has_grad,
|
||||
_set_paddle_lib_path,
|
||||
_set_warmup,
|
||||
_switch_tracer,
|
||||
_test_enforce_gpu_success,
|
||||
_xpu_device_synchronize,
|
||||
_xpu_get_current_stream,
|
||||
_xpu_set_current_stream,
|
||||
)
|
||||
|
||||
# isort: off
|
||||
|
||||
# custom device
|
||||
from .libpaddle import ( # noqa: F401
|
||||
CustomDeviceEvent,
|
||||
CustomDeviceStream,
|
||||
_get_current_custom_device_stream,
|
||||
_set_current_custom_device_stream,
|
||||
_synchronize_custom_device,
|
||||
)
|
||||
|
||||
# prim controller flags
|
||||
from .libpaddle import ( # noqa: F401
|
||||
__set_all_prim_enabled,
|
||||
__set_bwd_prim_enabled,
|
||||
__set_eager_prim_enabled,
|
||||
__set_fwd_prim_enabled,
|
||||
_add_skip_comp_ops,
|
||||
_is_bwd_prim_enabled,
|
||||
_is_eager_prim_enabled,
|
||||
_is_fwd_prim_enabled,
|
||||
_is_all_prim_enabled,
|
||||
_remove_skip_comp_ops,
|
||||
_set_bwd_prim_blacklist,
|
||||
_set_prim_target_grad_name,
|
||||
)
|
||||
|
||||
# type promotion
|
||||
|
||||
# isort: on
|
||||
if sys.platform != 'win32':
|
||||
from .libpaddle import ( # noqa: F401
|
||||
_array_to_share_memory_tensor,
|
||||
_cleanup_mmap_fds,
|
||||
_convert_to_tensor_list,
|
||||
_erase_process_pids,
|
||||
_remove_tensor_list_mmap_fds,
|
||||
_set_max_memory_map_allocation_pool_size,
|
||||
_set_process_pids,
|
||||
_set_process_signal_handler,
|
||||
_throw_error_if_process_failed,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
if has_paddle_dy_lib:
|
||||
sys.stderr.write(
|
||||
'Error: Can not import paddle core while this file exists: '
|
||||
+ current_path
|
||||
+ os.sep
|
||||
+ 'libpaddle.'
|
||||
+ dy_lib_suffix
|
||||
+ '\n'
|
||||
)
|
||||
if not avx_supported() and libpaddle.is_compiled_with_avx():
|
||||
sys.stderr.write(
|
||||
"Error: Your machine doesn't support AVX, but the installed PaddlePaddle is avx core, "
|
||||
"you should reinstall paddlepaddle with no-avx core.\n"
|
||||
)
|
||||
raise e
|
||||
|
||||
|
||||
def set_paddle_custom_device_lib_path(lib_dir):
|
||||
if os.environ.get('CUSTOM_DEVICE_ROOT', None) is not None:
|
||||
# use set environment value
|
||||
return
|
||||
path1 = os.path.normpath(
|
||||
os.path.join(lib_dir, '..', 'paddle_custom_device')
|
||||
)
|
||||
if os.path.exists(path1):
|
||||
# set CUSTOM_DEVICE_ROOT default path (lib_dir/../paddle_custom_device)
|
||||
os.environ['CUSTOM_DEVICE_ROOT'] = path1
|
||||
else:
|
||||
path2 = os.path.normpath(
|
||||
os.path.join(lib_dir, '..', '..', 'paddle_custom_device')
|
||||
)
|
||||
if os.path.exists(path2):
|
||||
# set CUSTOM_DEVICE_ROOT default path (lib_dir/../../paddle_custom_device)
|
||||
os.environ['CUSTOM_DEVICE_ROOT'] = path2
|
||||
else:
|
||||
os.environ['CUSTOM_DEVICE_ROOT'] = ''
|
||||
|
||||
|
||||
# set paddle lib path
|
||||
def set_paddle_lib_path():
|
||||
site_dirs = site.getsitepackages()
|
||||
for site_dir in site_dirs:
|
||||
lib_dir = os.path.sep.join([site_dir, 'paddle', 'libs'])
|
||||
if os.path.exists(lib_dir):
|
||||
_set_paddle_lib_path(lib_dir)
|
||||
set_paddle_custom_device_lib_path(lib_dir)
|
||||
return
|
||||
if hasattr(site, 'USER_SITE') and site.USER_SITE:
|
||||
lib_dir = os.path.sep.join([site.USER_SITE, 'paddle', 'libs'])
|
||||
if os.path.exists(lib_dir):
|
||||
_set_paddle_lib_path(lib_dir)
|
||||
set_paddle_custom_device_lib_path(lib_dir)
|
||||
|
||||
|
||||
set_paddle_lib_path()
|
||||
|
||||
|
||||
# This api is used for check of model output.
|
||||
# In some cases, model does not straightly return data which can be used for check.
|
||||
# When this flag is set true, required data should be returned in model.
|
||||
def _model_return_data():
|
||||
flag = os.getenv("FLAGS_model_return_data")
|
||||
if flag and flag.lower() in ("1", "true"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# This api is used for check whether prim is on
|
||||
def _prim_return_log():
|
||||
flag = os.getenv("FLAGS_prim_log")
|
||||
if flag and flag.lower() in ("1", "true"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# ops in forward_blacklist will not be replaced by composite ops.
|
||||
prim_config = {
|
||||
"forward_blacklist": set(),
|
||||
"composite_ops_record": set(),
|
||||
"backward_blacklist": set(),
|
||||
}
|
||||
|
||||
|
||||
def _get_batch_norm_none_var(op):
|
||||
"""Some outputs of batch_norm's replaced composite rule are not needed and will be removed."""
|
||||
use_run_stat = (
|
||||
op.attr("is_test") and (not op.attr("trainable_statistics"))
|
||||
) or op.attr("use_global_stats")
|
||||
if use_run_stat:
|
||||
return ["ReserveSpace", "SavedMean", "SavedVariance"]
|
||||
else:
|
||||
return ["ReserveSpace"]
|
||||
|
||||
|
||||
# In some case, inputs and outputs of composite op or its replaced composite rule might be None.
|
||||
# It means such arg will be no longer required in processed program by composite mechanism.
|
||||
# Therefore, such special ops should be recorded in advance and be released in args check.
|
||||
ops_contain_none = {
|
||||
"batch_norm": _get_batch_norm_none_var,
|
||||
"flatten_contiguous_range": ["XShape"],
|
||||
"squeeze2": ["XShape"],
|
||||
"unsqueeze2": ["XShape"],
|
||||
}
|
||||
|
||||
|
||||
# some intermediate outputs like xshape will no longer used after decomp, but return none to keep output num the same as origin op
|
||||
# key is the name of op, and value is the index of output in op.outputs
|
||||
decomp_ops_contain_unused_output = {
|
||||
"pd_op.squeeze": [1],
|
||||
"pd_op.unsqueeze": [1],
|
||||
"pd_op.batch_norm": [5],
|
||||
}
|
||||
|
||||
|
||||
# This api is used for development for dynamic shape in prim, and will be removed in future.
|
||||
def _enable_prim_skip_dynamic_shape():
|
||||
from paddle.base.framework import get_flags
|
||||
|
||||
return get_flags("FLAGS_prim_skip_dynamic")["FLAGS_prim_skip_dynamic"]
|
||||
|
||||
|
||||
def _enable_prim_dynamic_shape():
|
||||
from paddle.base.framework import get_flags
|
||||
|
||||
return get_flags("FLAGS_prim_enable_dynamic")["FLAGS_prim_enable_dynamic"]
|
||||
|
||||
|
||||
def _enable_dist_prim_all():
|
||||
flag = os.getenv("FLAGS_dist_prim_all")
|
||||
if flag and flag.lower() in ("1", "true"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def _enable_auto_recompute():
|
||||
flag = os.getenv("FLAGS_enable_auto_recompute")
|
||||
|
||||
# NOTE(chenxi67): open recompute when cinn is enabled
|
||||
from paddle.base.framework import in_cinn_mode
|
||||
|
||||
if in_cinn_mode():
|
||||
if flag and flag.lower() in ("0", "false"):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
if flag and flag.lower() in ("1", "true"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def _set_prim_forward_blacklist(*args):
|
||||
for item in args:
|
||||
if not isinstance(item, str):
|
||||
raise TypeError("ops set in forward_blacklist must belong to str")
|
||||
else:
|
||||
prim_config["forward_blacklist"].add(item)
|
||||
|
||||
|
||||
# Currently, this function is not utilized anywhere in the codebase.
|
||||
# It may be intended for future use or could be removed if unnecessary.
|
||||
# def _reset_prim_forward_blacklist():
|
||||
# prim_config["forward_blacklist"] = set()
|
||||
|
||||
|
||||
def _set_prim_backward_blacklist(*args):
|
||||
ops = set(args)
|
||||
new_ops = set()
|
||||
for item in ops:
|
||||
if not isinstance(item, str):
|
||||
raise TypeError("All items in set must be strings.")
|
||||
item = item.removeprefix("pd_op.")
|
||||
prim_config["backward_blacklist"].add(item)
|
||||
new_ops.add(item)
|
||||
_set_bwd_prim_blacklist(new_ops)
|
||||
|
||||
|
||||
def _set_prim_backward_enabled(value: bool, print_flag: bool = False):
|
||||
assert isinstance(value, bool), (
|
||||
f"value should be bool, but got {type(value)}"
|
||||
)
|
||||
__set_bwd_prim_enabled(value)
|
||||
if _prim_return_log() or print_flag:
|
||||
print("backward prim enabled: ", bool(_is_bwd_prim_enabled()))
|
||||
|
||||
|
||||
def _set_prim_forward_enabled(value: bool, print_flag: bool = False):
|
||||
assert isinstance(value, bool), (
|
||||
f"value should be bool, but got {type(value)}"
|
||||
)
|
||||
__set_fwd_prim_enabled(value)
|
||||
if _prim_return_log() or print_flag:
|
||||
print("forward prim enabled: ", bool(_is_fwd_prim_enabled()))
|
||||
|
||||
|
||||
def set_prim_eager_enabled(value: bool, print_flag: bool = False):
|
||||
assert isinstance(value, bool), (
|
||||
f"value should be bool, but got {type(value)}"
|
||||
)
|
||||
__set_eager_prim_enabled(value)
|
||||
if _prim_return_log() or print_flag:
|
||||
print("eager prim enabled: ", bool(_is_eager_prim_enabled()))
|
||||
|
||||
|
||||
def _set_prim_all_enabled(value: bool, print_flag: bool = False):
|
||||
assert isinstance(value, bool), (
|
||||
f"value should be bool, but got {type(value)}"
|
||||
)
|
||||
__set_all_prim_enabled(value)
|
||||
if _prim_return_log() or print_flag:
|
||||
print(
|
||||
"all prim enabled: ",
|
||||
bool(_is_all_prim_enabled()),
|
||||
)
|
||||
|
||||
|
||||
def __check_and_set_prim_all_enabled(print_flag=False):
|
||||
from paddle.utils.environments import strtobool
|
||||
|
||||
prim_all_env = os.getenv("FLAGS_prim_all")
|
||||
prim_fwd_env = os.getenv("FLAGS_prim_forward")
|
||||
prim_bwd_env = os.getenv("FLAGS_prim_backward")
|
||||
if prim_all_env is not None:
|
||||
prim_all_flag = strtobool(prim_all_env)
|
||||
_set_prim_all_enabled(prim_all_flag, print_flag)
|
||||
|
||||
if prim_fwd_env is not None:
|
||||
prim_fwd_flag = strtobool(prim_fwd_env)
|
||||
_set_prim_forward_enabled(prim_fwd_flag, print_flag)
|
||||
|
||||
if prim_bwd_env is not None:
|
||||
prim_bwd_flag = strtobool(prim_bwd_env)
|
||||
_set_prim_backward_enabled(prim_bwd_flag, print_flag)
|
||||
|
||||
|
||||
__check_and_set_prim_all_enabled(print_flag=True)
|
||||
|
||||
|
||||
SKIPPED_PRIM_VJP_DEFAULT_OPS = ["matmul_grad"]
|
||||
|
||||
|
||||
def _clear_prim_vjp_skip_default_ops():
|
||||
for item in SKIPPED_PRIM_VJP_DEFAULT_OPS:
|
||||
_remove_skip_comp_ops(item)
|
||||
|
||||
|
||||
# Since some decomposition of special ops like matmul_grad will reduce performance and is difficult to optimize currently by CINN.
|
||||
# This api is used for development for in prim and cinn, and will be removed in future.
|
||||
def _check_and_set_prim_vjp_skip_default_ops():
|
||||
flag = os.getenv("FLAGS_prim_vjp_skip_default_ops", "1")
|
||||
if flag and flag.lower() in ("1", "true"):
|
||||
_set_prim_backward_blacklist(*SKIPPED_PRIM_VJP_DEFAULT_OPS)
|
||||
return True
|
||||
else:
|
||||
_clear_prim_vjp_skip_default_ops()
|
||||
return False
|
||||
|
||||
|
||||
_check_and_set_prim_vjp_skip_default_ops()
|
||||
|
||||
|
||||
def _check_prim_vjp_ops():
|
||||
ops_org = os.getenv("FLAGS_prim_backward_blacklist", "")
|
||||
if ops_org:
|
||||
ops = []
|
||||
for item in ops_org.split(";"):
|
||||
ops.append(item.strip())
|
||||
_set_prim_backward_blacklist(*ops)
|
||||
|
||||
|
||||
_check_prim_vjp_ops()
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2024 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 paddle._typing.libs.libpaddle import * # noqa: F403
|
||||
@@ -0,0 +1,252 @@
|
||||
# Copyright (c) 2018 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 google.protobuf import text_format
|
||||
|
||||
from paddle.base.proto import data_feed_pb2
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class DataFeedDesc:
|
||||
r"""
|
||||
:api_attr: Static Graph
|
||||
|
||||
Datafeed descriptor, describing input training data format.
|
||||
|
||||
DataFeedDesc shall be initialized from a valid protobuf message from disk.
|
||||
|
||||
See :code:`paddle/base/framework/data_feed.proto` for message definition.
|
||||
A typical message might look like:
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle.base as base
|
||||
>>> with open("data.proto", "w") as f:
|
||||
... f.write('name: "MultiSlotDataFeed"\n')
|
||||
... f.write('batch_size: 2\n')
|
||||
... f.write('multi_slot_desc {\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "words"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "label"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write('}')
|
||||
>>> data_feed = base.DataFeedDesc('data.proto')
|
||||
|
||||
However, users usually shouldn't care about the message format; instead,
|
||||
they are encouraged to use :code:`Data Generator` as a tool to generate a
|
||||
valid data description, in the process of converting their raw log files to
|
||||
training files acceptable to Executor.
|
||||
|
||||
DataFeedDesc can also be changed during runtime. Once you got familiar with
|
||||
what each field mean, you can modify it to better suit your need. E.g.:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle.base as base
|
||||
>>> data_feed = base.DataFeedDesc('data.proto')
|
||||
>>> data_feed.set_batch_size(128)
|
||||
>>> data_feed.set_dense_slots(['words']) # The slot named 'words' will be dense
|
||||
>>> data_feed.set_use_slots(['words']) # The slot named 'words' will be used
|
||||
|
||||
>>> # Finally, the content can be dumped out for debugging purpose:
|
||||
|
||||
>>> print(data_feed.desc())
|
||||
|
||||
Args:
|
||||
proto_file(string): Disk file containing a data feed description.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, proto_file):
|
||||
self.proto_desc = data_feed_pb2.DataFeedDesc()
|
||||
self.proto_desc.pipe_command = "cat"
|
||||
with open(proto_file, 'r') as f:
|
||||
text_format.Parse(f.read(), self.proto_desc)
|
||||
if self.proto_desc.name == "MultiSlotDataFeed":
|
||||
self.__name_to_index = {
|
||||
slot.name: i
|
||||
for i, slot in enumerate(self.proto_desc.multi_slot_desc.slots)
|
||||
}
|
||||
|
||||
def set_batch_size(self, batch_size):
|
||||
r"""
|
||||
Set :attr:`batch_size` in ``paddle.base.DataFeedDesc`` . :attr:`batch_size` can be changed during training.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle.base as base
|
||||
>>> with open("data.proto", "w") as f:
|
||||
... f.write('name: "MultiSlotDataFeed"\n')
|
||||
... f.write('batch_size: 2\n')
|
||||
... f.write('multi_slot_desc {\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "words"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "label"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write('}')
|
||||
>>> data_feed = base.DataFeedDesc('data.proto')
|
||||
>>> data_feed.set_batch_size(128)
|
||||
|
||||
Args:
|
||||
batch_size (int): The number of batch size.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
|
||||
"""
|
||||
self.proto_desc.batch_size = batch_size
|
||||
|
||||
def set_dense_slots(self, dense_slots_name):
|
||||
r"""
|
||||
Set slots in :attr:`dense_slots_name` as dense slots. **Note: In default, all slots are sparse slots.**
|
||||
|
||||
Features for a dense slot will be fed into a Tensor, while those for a
|
||||
sparse slot will be fed into a DenseTensor.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle.base as base
|
||||
>>> with open("data.proto", "w") as f:
|
||||
... f.write('name: "MultiSlotDataFeed"\n')
|
||||
... f.write('batch_size: 2\n')
|
||||
... f.write('multi_slot_desc {\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "words"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "label"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write('}')
|
||||
>>> data_feed = base.DataFeedDesc('data.proto')
|
||||
>>> data_feed.set_dense_slots(['words'])
|
||||
|
||||
Args:
|
||||
dense_slots_name (list(str)): a list of slot names which will be set dense.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
|
||||
"""
|
||||
if self.proto_desc.name != "MultiSlotDataFeed":
|
||||
raise ValueError(
|
||||
"Only MultiSlotDataFeed needs set_dense_slots, please check your datafeed.proto"
|
||||
)
|
||||
for name in dense_slots_name:
|
||||
self.proto_desc.multi_slot_desc.slots[
|
||||
self.__name_to_index[name]
|
||||
].is_dense = True
|
||||
|
||||
def set_use_slots(self, use_slots_name):
|
||||
r"""
|
||||
Set if a specific slot will be used for training. A dataset shall
|
||||
contain a lot of features, through this function one can select which
|
||||
ones will be used for a specific model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle.base as base
|
||||
>>> with open("data.proto", "w") as f:
|
||||
... f.write('name: "MultiSlotDataFeed"\n')
|
||||
... f.write('batch_size: 2\n')
|
||||
... f.write('multi_slot_desc {\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "words"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "label"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write('}')
|
||||
>>> data_feed = base.DataFeedDesc('data.proto')
|
||||
>>> data_feed.set_use_slots(['words'])
|
||||
|
||||
Args:
|
||||
use_slots_name: a list of slot names which will be used in training
|
||||
|
||||
Note:
|
||||
Default is not used for all slots
|
||||
"""
|
||||
if self.proto_desc.name != "MultiSlotDataFeed":
|
||||
raise ValueError(
|
||||
"Only MultiSlotDataFeed needs set_use_slots, please check your datafeed.proto"
|
||||
)
|
||||
for name in use_slots_name:
|
||||
self.proto_desc.multi_slot_desc.slots[
|
||||
self.__name_to_index[name]
|
||||
].is_used = True
|
||||
|
||||
def desc(self):
|
||||
r"""
|
||||
Returns a protobuf message for this DataFeedDesc
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle.base as base
|
||||
>>> with open("data.proto", "w") as f:
|
||||
... f.write('name: "MultiSlotDataFeed"\n')
|
||||
... f.write('batch_size: 2\n')
|
||||
... f.write('multi_slot_desc {\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "words"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write(' slots {\n')
|
||||
... f.write(' name: "label"\n')
|
||||
... f.write(' type: "uint64"\n')
|
||||
... f.write(' is_dense: false\n')
|
||||
... f.write(' is_used: true\n')
|
||||
... f.write(' }\n')
|
||||
... f.write('}')
|
||||
>>> data_feed = base.DataFeedDesc('data.proto')
|
||||
>>> print(data_feed.desc())
|
||||
|
||||
Returns:
|
||||
A string message
|
||||
"""
|
||||
return text_format.MessageToString(self.proto_desc)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user