chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
# Copyright (c) 2016 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.framework import require_version
from . import ( # noqa: F401
cpp_extension,
decorator_utils,
dlpack,
download,
gpu_utils,
image_util,
layers_utils,
unique_name,
)
from .bwd_graph_utils import capture_backward_subgraph_guard # noqa: F401
from .deprecated import deprecated
from .environments import strtobool as strtobool
from .fwd_graph_utils import capture_forward_subgraph_guard # noqa: F401
from .install_check import run_check
from .layers_utils import ( # noqa: F401
_contain_var,
_convert_to_tensor_list,
_hash_with_id,
_is_symmetric_padding,
_packed_nest_with_indices,
_recursive_assert_same_structure,
_sequence_like,
_yield_flat_nest,
_yield_value,
assert_same_structure,
check_shape,
convert_shape_to_list,
convert_to_list,
copy_mutable_vars,
flatten,
get_inputs_outputs_in_block,
get_int_tensor_list,
get_shape_tensor_inputs,
hold_mutable_vars,
is_same_shape,
is_sequence,
map_structure,
pack_sequence_as,
padding_to_same_structure,
to_sequence,
try_get_constant_shape_from_tensor,
)
from .lazy_import import try_import
from .op_version import OpLastCheckpointChecker # noqa: F401
__all__ = [
'deprecated',
'run_check',
'require_version',
'try_import',
]
def __getattr__(name):
if name == "data":
from importlib import import_module
mod = import_module(".data", package=__name__)
globals()[name] = mod
return mod
raise AttributeError(f"module 'paddle.utils' has no attribute '{name}'")
+33
View File
@@ -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 paddle
from paddle.base.wrapped_decorator import signature_safe_contextmanager
from .download import check_and_create_dir
@signature_safe_contextmanager
def capture_backward_subgraph_guard(
dump_dir_path: str, need_dump_grad_tensors: bool = False
):
assert dump_dir_path is not None, "The dump_dir_path should not be None"
check_and_create_dir(dump_dir_path)
paddle.base.core.eager._start_capture_backward_viz_subgraph(
dump_dir_path, need_dump_grad_tensors
)
try:
yield
finally:
paddle.base.core.eager._stop_capture_backward_viz_subgraph()
@@ -0,0 +1,40 @@
# 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 .cpp_extension import (
CUDA_HOME, # noqa: F401
IS_WINDOWS, # noqa: F401
BuildExtension, # noqa: F401
CppExtension,
CUDAExtension,
_compute_worker_number, # noqa: F401
_get_num_workers, # noqa: F401
_get_pybind11_abi_build_flags, # noqa: F401
load,
setup,
)
from .extension_utils import (
_get_cuda_arch_flags, # noqa: F401
get_build_directory,
load_op_meta_info_and_register_op, # noqa: F401
parse_op_info, # noqa: F401
)
__all__ = [
'CppExtension',
'CUDAExtension',
'load',
'setup',
'get_build_directory',
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
# 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 .dataloader import (
DataLoader,
default_collate,
get_worker_info,
)
from .dataset import (
ChainDataset,
ConcatDataset,
Dataset,
IterableDataset,
Subset,
TensorDataset,
random_split,
)
from .distributed import DistributedSampler
from .sampler import (
BatchSampler,
RandomSampler,
Sampler,
SequentialSampler,
)
__all__ = [
'default_collate',
'get_worker_info',
'ChainDataset',
'ConcatDataset',
'Dataset',
'DataLoader',
'IterableDataset',
'Subset',
'random_split',
'BatchSampler',
'DistributedSampler',
'RandomSampler',
'Sampler',
'SequentialSampler',
'TensorDataset',
]
@@ -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 . import (
collate as collate,
worker as worker,
)
@@ -0,0 +1,17 @@
# 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 paddle.io.dataloader.collate import (
default_collate_fn as default_collate, # noqa: F401
)
+17
View File
@@ -0,0 +1,17 @@
# 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 paddle.io import (
get_worker_info as get_worker_info,
)
+88
View File
@@ -0,0 +1,88 @@
# 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 __future__ import annotations
import warnings
from typing import TYPE_CHECKING, Any
from paddle.io import DataLoader as PaddleDataLoader
from ._utils.collate import (
default_collate as default_collate,
)
from ._utils.worker import (
get_worker_info as get_worker_info,
)
if TYPE_CHECKING:
from collections.abc import Callable
from paddle.io.dataloader import BatchSampler
from paddle.io.dataloader.dataset import Dataset
from paddle.io.reader import _CollateFn
class DataLoader(PaddleDataLoader):
def __init__(
self,
dataset: Dataset[Any],
batch_size: int | None = 1,
shuffle: bool = False,
sampler: BatchSampler | None = None,
batch_sampler: BatchSampler | None = None,
num_workers: int = 0,
collate_fn: _CollateFn | None = None,
pin_memory: bool = False,
drop_last: bool = False,
timeout: float = 0,
worker_init_fn: Callable[[int], None] | None = None,
multiprocessing_context=None,
generator=None,
*,
prefetch_factor: int | None = None,
persistent_workers: bool = False,
pin_memory_device: str = "",
in_order: bool = True,
) -> None:
if (
pin_memory is True
or multiprocessing_context is not None
or generator is not None
or prefetch_factor is not None
or len(pin_memory_device) > 0
or in_order is False
):
warnings.warn(
"pin_memory, multiprocessing_context, generator, prefetch_factor, pin_memory_device, in_order are currently not supported in DataLoader and will be ignored."
)
if sampler is not None:
if batch_sampler is not None:
raise ValueError(
"Cannot specify both sampler and batch_sampler"
)
batch_sampler = sampler
super().__init__(
dataset=dataset,
batch_sampler=batch_sampler,
batch_size=batch_size,
shuffle=shuffle,
drop_last=drop_last,
collate_fn=collate_fn,
num_workers=num_workers,
timeout=timeout,
worker_init_fn=worker_init_fn,
persistent_workers=persistent_workers,
)
+23
View File
@@ -0,0 +1,23 @@
# 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 paddle.io import (
ChainDataset as ChainDataset,
ConcatDataset as ConcatDataset,
Dataset as Dataset,
IterableDataset as IterableDataset,
Subset as Subset,
TensorDataset as TensorDataset,
random_split as random_split,
)
+43
View File
@@ -0,0 +1,43 @@
# 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 __future__ import annotations
from typing import TYPE_CHECKING
from paddle.io import DistributedBatchSampler
if TYPE_CHECKING:
from collections.abc import Sized
class DistributedSampler(DistributedBatchSampler):
def __init__(
self,
dataset: Sized,
num_replicas: int | None = None,
rank: int | None = None,
shuffle: bool = True,
seed: int = 0,
drop_last: bool = False,
) -> None:
super().__init__(
dataset,
1,
num_replicas,
rank,
shuffle,
drop_last,
seed,
)
+20
View File
@@ -0,0 +1,20 @@
# 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 paddle.io import (
BatchSampler as BatchSampler,
RandomSampler as RandomSampler,
Sampler as Sampler,
SequenceSampler as SequentialSampler, # noqa: F401
)
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
# 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.
"""
decorator to deprecate a function or class
"""
from __future__ import annotations
import functools
import inspect
import sys
import warnings
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import ParamSpec
import paddle
if TYPE_CHECKING:
from collections.abc import Callable
_InputT = ParamSpec("_InputT")
_RetT = TypeVar("_RetT")
__all__ = []
class VisibleDeprecationWarning(UserWarning):
"""Visible deprecation warning.
Since Python 3.7, Python only show the DeprecationWarning if the module
is __main__. So we use this warning to make the deprecation warning visible.
See more details from https://peps.python.org/pep-0565/
"""
def deprecated(
update_to: str = "",
since: str = "",
reason: str = "",
level: int = 0,
) -> Callable[[Callable[_InputT, _RetT]], Callable[_InputT, _RetT]]:
"""Decorate a function to signify its deprecation.
This function wraps a method that will soon be removed and does two things:
- The docstring of the API will be modified to include a notice
about deprecation."
- Raises a :class:`~exceptions.DeprecatedWarning` when old API is called.
Args:
since(str, optional): The version at which the decorated method is considered deprecated.
update_to(str, optional): The new API users should use.
reason(str, optional): The reason why the API is deprecated.
level(int, optional): The deprecated warning log level. It must be
an Integer and must be one of 0, 1, 2.
If `level == 0`, the warning message will not be showed.
If `level == 1`, the warning message will be showed normally.
If `level == 2`, it will raise `RuntimeError`.
Returns:
decorator: decorated function or class.
"""
def decorator(func: Callable[_InputT, _RetT]) -> Callable[_InputT, _RetT]:
"""construct warning message, and return a decorated function or class."""
assert isinstance(update_to, str), 'type of "update_to" must be str.'
assert isinstance(since, str), 'type of "since" must be str.'
assert isinstance(reason, str), 'type of "reason" must be str.'
assert isinstance(level, int) and level >= 0 and level < 3, (
'type of "level" must be int and must be one of 0, 1, 2. But '
f'received: {level}.'
)
_since = since.strip()
_update_to = update_to.strip()
_reason = reason.strip()
msg = f'API "{func.__module__}.{func.__name__}" is deprecated'
if len(_since) > 0:
msg += f" since {_since}"
msg += ", and will be removed in future versions."
if len(_update_to) > 0:
assert _update_to.startswith("paddle."), (
f'Argument update_to must start with "paddle.", your value is "{update_to}"'
)
msg += f' Please use "{_update_to}" instead.'
if len(_reason) > 0:
msg += f"\n Reason: {_reason}"
if func.__doc__:
func.__doc__ = (
'\n\nWarning:\n ' + msg + '\n\n'
) + inspect.cleandoc(func.__doc__)
if level == 0:
return func
def parse_version(version: str):
# Split the version string and convert numeric parts to integers
return [int(part) for part in version.split(".") if part.isdigit()]
@functools.wraps(func)
def wrapper(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT:
"""deprecated warning should be fired in 3 circumstances:
1. current version is develop version, i.e. "0.0.0", because we assume develop version is always the latest version.
2. since version is empty, in this case, API is deprecated in all versions.
3. current version is newer than since version.
"""
if level == 2:
raise RuntimeError(
f'API "{func.__module__}.{func.__name__}" has been deprecated.'
)
warningmsg = f"\033[93m\nWarning:\n{msg} \033[0m"
# ensure ANSI escape sequences print correctly in cmd and powershell
if sys.platform.lower() == 'win32':
warningmsg = f"\nWarning:\n{msg} "
v_current = parse_version(paddle.__version__)
v_current += [0] * (4 - len(v_current))
v_since = parse_version(since)
v_since += [0] * (4 - len(v_since))
if (
paddle.__version__ == "0.0.0"
or _since == ""
or v_current >= v_since
):
warnings.warn(
warningmsg, category=VisibleDeprecationWarning, stacklevel=2
)
return func(*args, **kwargs)
return wrapper
return decorator
+275
View File
@@ -0,0 +1,275 @@
# 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 enum
import warnings
from enum import IntEnum
from typing import TYPE_CHECKING, Literal, Protocol, TypeVar
import paddle
from ..base.core import DenseTensor
from ..base.data_feeder import check_type
from ..base.framework import in_dygraph_mode
if TYPE_CHECKING:
from typing_extensions import CapsuleType
from paddle import Tensor
from paddle._typing import PlaceLike
__all__ = [
'to_dlpack',
'from_dlpack',
]
_T_contra = TypeVar("_T_contra", contravariant=True)
class SupportDLPack(Protocol[_T_contra]):
"""
ref:
https://github.com/numpy/numpy/blob/7e6e48ca7aacae9994d18a3dadbabd2b91c32151/numpy/__init__.pyi#L3068-L3077
https://github.com/numpy/numpy/blob/7e6e48ca7aacae9994d18a3dadbabd2b91c32151/numpy/__init__.pyi#L4730-L4731
"""
def __dlpack__(
self,
*,
stream: None | _T_contra = ...,
max_version: tuple[int, int] | None = ...,
dl_device: tuple[IntEnum, int] | None = None,
copy: bool | None = None,
) -> CapsuleType: ...
def __dlpack_device__(self) -> tuple[int, Literal[0]]: ...
class DLDeviceType(enum.IntEnum):
kDLCPU = (1,)
kDLCUDA = (2,)
kDLCUDAHost = (3,)
kDLOpenCL = (4,)
kDLVulkan = (7,)
kDLMetal = (8,)
kDLVPI = (9,)
kDLROCM = (10,)
kDLROCMHost = (11,)
kDLExtDev = (12,)
kDLCUDAManaged = (13,)
kDLOneAPI = (14,)
kDLWebGPU = (15,)
kDLHexagon = (16,)
kDLMAIA = (17,)
kDLTrn = (18,)
def to_dlpack(x: Tensor) -> CapsuleType:
"""
Encodes a tensor to DLPack.
Args:
x (Tensor): The input tensor, and the data type can be ``bool``, ``float16``, ``float32``,
``float64``, ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``complex64``,
``complex128``.
Returns:
dltensor, and the data type is PyCapsule.
Examples:
.. code-block:: pycon
:name: code-paddle-to-paddle
>>> import paddle
>>> # x is a tensor with shape [2, 4]
>>> x = paddle.to_tensor(
... [
... [0.2, 0.3, 0.5, 0.9],
... [0.1, 0.2, 0.6, 0.7],
... ]
... )
>>> dlpack = paddle.to_dlpack(x)
>>> print(dlpack)
>>> # doctest: +SKIP('the address will change in every run')
<capsule object "dltensor" at 0x7f6103c681b0>
>>> # doctest: -SKIP
>>> # dlpack capsule will be renamed to 'used_dltensor' after decoded
>>> y = paddle.from_dlpack(dlpack)
>>> print(dlpack)
>>> # doctest: +SKIP('the address will change in every run')
<capsule object "used_dltensor" at 0x7f6103c681b0>
.. code-block:: pycon
:name: code-paddle-to-torch
>>> # doctest: +SKIP('torch will not be installed')
>>> # type: ignore
>>> # convert tensor from paddle to other framework using to_dlpack
>>> import torch
>>> x = paddle.randn([2, 4]).to(device="cpu")
>>> y = torch.from_dlpack(paddle.to_dlpack(x))
>>> print(y.shape)
torch.Size([2, 4])
>>> # doctest: -SKIP
"""
if in_dygraph_mode():
if not isinstance(x, paddle.Tensor):
raise TypeError(
"The type of 'x' in to_dlpack must be paddle.Tensor,"
f" but received {type(x)}."
)
return x.value().get_tensor()._to_dlpack()
check_type(x, "x", (DenseTensor), "to_dlpack")
return x._to_dlpack()
def from_dlpack(
dlpack: SupportDLPack | CapsuleType,
*,
device: PlaceLike | None = None,
copy: bool | None = None,
) -> Tensor:
"""
Decodes a DLPack to a tensor. The returned Paddle tensor will share the memory with
the tensor from given dlpack.
Args:
dlpack (SupportDLPack | CapsuleType): A PyCapsule object with the dltensor,
or that implements '__dlpack__' and '__dlpack_device__' methods.
If `dlpack` is a tensor (or ndarray) object, it must support
the `__dlpack__` protocol (i.e., have a `dlpack.__dlpack__`
method). Otherwise `dlpack` may be a DLPack capsule, which is
an opaque `PyCapsule` instance, typically produced by a
`to_dlpack` function or method.
device (PlaceLike, optional): The device of the returned tensor. If not
specified, the device will be the same as that of the input `dlpack`.
copy (bool, optional): Whether or not to copy the input.
If True, the output tensor always copied. If False, the output tensor must never
copied, and raise a BufferError in case a copy is deemed necessary. If None, the
output tensor must reuse the existing memory buffer if possible and copy otherwise.
Default: None.
Returns:
out (Tensor): A tensor decoded from DLPack. The data type of returned tensor
can be one of: ``int32``, ``int64``, ``float16``, ``float32`` and ``float64``.
The device of returned tensor can be one of: ``CPU``, ``CUDAPlace``, ``CUDAPinnedPlace``.
Examples:
.. code-block:: pycon
:name: code-paddle-from-paddle
>>> import paddle
>>> # From DLPack capsule
>>> x = paddle.to_tensor(
... [
... [0.2, 0.3, 0.5, 0.9],
... [0.1, 0.2, 0.6, 0.7],
... ],
... place="cpu",
... )
>>> dlpack = paddle.to_dlpack(x)
>>> y = paddle.from_dlpack(dlpack)
>>> # dlpack capsule will be renamed to 'used_dltensor' after decoded
>>> print(dlpack)
>>> # doctest: +SKIP('the address will change in every run')
<capsule object "used_dltensor" at 0x7f6103c681b0>
>>> # doctest: -SKIP
>>> print(y)
Tensor(shape=[2, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[0.20000000, 0.30000001, 0.50000000, 0.89999998],
[0.10000000, 0.20000000, 0.60000002, 0.69999999]])
>>> # data of tensor x is shared with tensor y
>>> y[0, 0] = 10.0
>>> print(x)
Tensor(shape=[2, 4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[[10. , 0.30000001, 0.50000000, 0.89999998],
[0.10000000, 0.20000000, 0.60000002, 0.69999999]])
.. code-block:: pycon
:name: code-paddle-from-numpy
>>> # Directly from external tensor that implements '__dlpack__' and '__dlpack_device__' methods
>>> import paddle
>>> import numpy as np
>>> x = np.array(
... [
... [0.2, 0.3, 0.5, 0.9],
... [0.1, 0.2, 0.6, 0.7],
... ]
... )
>>> y = paddle.from_dlpack(x)
>>> y[0, 0] = 10.0
>>> # data of tensor x is shared with tensor y
>>> print(x)
[[10. 0.3 0.5 0.9]
[ 0.1 0.2 0.6 0.7]]
"""
if hasattr(dlpack, "__dlpack__"):
kwargs = {}
kwargs["max_version"] = (1, 3)
if copy is not None:
kwargs["copy"] = copy
if device is not None:
place = paddle.base.framework._get_paddle_place(device)
kwargs["dl_device"] = paddle.base.core.place_to_dl_device(place)
dlpack_device = dlpack.__dlpack_device__()
# device is CUDA, we need to pass the current
# stream
if dlpack_device[0] in (DLDeviceType.kDLCUDA,):
with warnings.catch_warnings():
# ignore deprecation warning
warnings.filterwarnings("ignore", category=UserWarning)
stream = paddle.device.cuda.current_stream(dlpack_device[1])
# cuda_stream is the pointer to the stream and it is a public
# attribute, but it is not documented
# The array API specify that the default legacy stream must be passed
# with a value of 1 for CUDA
# https://data-apis.org/array-api/latest/API_specification/array_object.html?dlpack-self-stream-none#dlpack-self-stream-none
is_gpu = dlpack_device[0] == DLDeviceType.kDLCUDA
stream_ptr = (
1 if is_gpu and stream.cuda_stream == 0 else stream.cuda_stream
)
kwargs["stream"] = stream_ptr
try:
dlpack_ = dlpack.__dlpack__(**kwargs)
except TypeError:
# Remove the `max_version` argument if it is not supported
kwargs.pop("max_version")
dlpack_ = dlpack.__dlpack__(**kwargs)
else:
# Old versions just call the converter
dlpack_ = dlpack
out: paddle.base.libpaddle.DenseTensor = paddle.base.core.from_dlpack(
dlpack_
)
if in_dygraph_mode():
out: Tensor = paddle.Tensor(out, place=out._place())
return out
+478
View File
@@ -0,0 +1,478 @@
# 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
import hashlib
import os
import os.path as osp
import shutil
import sys
import tarfile
import time
import warnings
import zipfile
from typing import Literal
import httpx
try:
from tqdm import tqdm
except:
class tqdm:
def __init__(self, total=None):
self.total = total
self.n = 0
def update(self, n):
self.n += n
if self.total is None:
sys.stderr.write(f"\r{self.n:.1f} bytes")
else:
sys.stderr.write(f"\r{100 * self.n / float(self.total):.1f}%")
sys.stderr.flush()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stderr.write('\n')
import logging
logger = logging.getLogger(__name__)
__all__ = ['get_weights_path_from_url']
WEIGHTS_HOME = osp.expanduser("~/.cache/paddle/hapi/weights")
DOWNLOAD_RETRY_LIMIT = 3
def is_url(path: str) -> bool:
"""
Whether path is URL.
Args:
path (string): URL string or not.
"""
return path.startswith('http://') or path.startswith('https://')
def get_weights_path_from_url(url: str, md5sum: str | None = None) -> str:
"""Get weights path from WEIGHT_HOME, if not exists,
download it from url.
Args:
url (str): download url
md5sum (str): md5 sum of download package
Returns:
str: a local path to save downloaded weights.
Examples:
.. code-block:: pycon
>>> from paddle.utils.download import get_weights_path_from_url
>>> resnet18_pretrained_weight_url = 'https://paddle-hapi.bj.bcebos.com/models/resnet18.pdparams'
>>> local_weight_path = get_weights_path_from_url(resnet18_pretrained_weight_url)
"""
path = get_path_from_url(url, WEIGHTS_HOME, md5sum)
return path
def _map_path(url, root_dir):
# parse path after download under root_dir
fname = osp.split(url)[-1]
fpath = fname
return osp.join(root_dir, fpath)
def _get_unique_endpoints(trainer_endpoints):
# Sorting is to avoid different environmental variables for each card
trainer_endpoints.sort()
ips = set()
unique_endpoints = set()
for endpoint in trainer_endpoints:
ip = endpoint.split(":")[0]
if ip in ips:
continue
ips.add(ip)
unique_endpoints.add(endpoint)
logger.info(f"unique_endpoints {unique_endpoints}")
return unique_endpoints
def get_path_from_url(
url: str,
root_dir: str,
md5sum: str | None = None,
check_exist: bool = True,
decompress: bool = True,
method: Literal['wget', 'get'] = 'get',
) -> str:
"""Download from given url to root_dir.
if file or directory specified by url is exists under
root_dir, return the path directly, otherwise download
from url and decompress it, return the path.
Args:
url (str): download url
root_dir (str): root dir for downloading, it should be
WEIGHTS_HOME or DATASET_HOME
md5sum (str|None, optional): md5 sum of download package
decompress (bool, optional): decompress zip or tar file. Default is `True`
method (str, optional): which download method to use. Support `wget` and `get`. Default is `get`.
Returns:
str: a local path to save downloaded models & weights & datasets.
"""
from paddle.distributed import ParallelEnv
assert is_url(url), f"downloading from {url} not a url"
# parse path after download to decompress under root_dir
fullpath = _map_path(url, root_dir)
# Mainly used to solve the problem of downloading data from different
# machines in the case of multiple machines. Different ips will download
# data, and the same ip will only download data once.
unique_endpoints = _get_unique_endpoints(ParallelEnv().trainer_endpoints[:])
if osp.exists(fullpath) and check_exist and _md5check(fullpath, md5sum):
logger.info(f"Found {fullpath}")
else:
if ParallelEnv().current_endpoint in unique_endpoints:
fullpath = _download(url, root_dir, md5sum, method=method)
else:
while not os.path.exists(fullpath):
time.sleep(1)
if ParallelEnv().current_endpoint in unique_endpoints:
if decompress and (
tarfile.is_tarfile(fullpath) or zipfile.is_zipfile(fullpath)
):
fullpath = _decompress(fullpath)
return fullpath
def _get_download(url, fullname):
# using requests.get method
fname = osp.basename(fullname)
try:
with httpx.stream(
"GET", url, timeout=None, follow_redirects=True
) as req:
if req.status_code != 200:
raise RuntimeError(
f"Downloading from {url} failed with code "
f"{req.status_code}!"
)
tmp_fullname = fullname + "_tmp"
total_size = req.headers.get('content-length')
with open(tmp_fullname, 'wb') as f:
if total_size:
with tqdm(total=(int(total_size) + 1023) // 1024) as pbar:
for chunk in req.iter_bytes(chunk_size=1024):
f.write(chunk)
pbar.update(1)
else:
for chunk in req.iter_bytes(chunk_size=1024):
if chunk:
f.write(chunk)
shutil.move(tmp_fullname, fullname)
return fullname
except Exception as e: # requests.exceptions.ConnectionError
logger.info(f"Downloading {fname} from {url} failed with exception {e}")
return False
_download_methods = {'get': _get_download}
def _download(url, path, md5sum=None, method='get'):
"""
Download from url, save to path.
url (str): download url
path (str): download to given path
md5sum (str): md5 sum of download package
method (str): which download method to use. Support `wget` and `get`. Default is `get`.
"""
assert method in _download_methods, f'make sure `{method}` implemented'
if not osp.exists(path):
os.makedirs(path)
fname = osp.split(url)[-1]
fullname = osp.join(path, fname)
retry_cnt = 0
logger.info(f"Downloading {fname} from {url}")
while not (osp.exists(fullname) and _md5check(fullname, md5sum)):
logger.info(f"md5check {fullname} and {md5sum}")
if retry_cnt < DOWNLOAD_RETRY_LIMIT:
retry_cnt += 1
else:
raise RuntimeError(
f"Download from {url} failed. Retry limit reached"
)
if not _download_methods[method](url, fullname):
time.sleep(1)
continue
return fullname
def _md5check(fullname, md5sum=None):
if md5sum is None:
return True
logger.info(f"File {fullname} md5 checking...")
md5 = hashlib.md5()
with open(fullname, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b""):
md5.update(chunk)
calc_md5sum = md5.hexdigest()
if calc_md5sum != md5sum:
logger.info(
f"File {fullname} md5 check failed, {calc_md5sum}(calc) != "
f"{md5sum}(base)"
)
return False
return True
def _decompress(fname):
"""
Decompress for zip and tar file
"""
logger.info(f"Decompressing {fname}...")
# For protecting decompressing interrupted,
# decompress to fpath_tmp directory firstly, if decompress
# succeeded, move decompress files to fpath and delete
# fpath_tmp and remove download compress file.
if tarfile.is_tarfile(fname):
uncompressed_path = _uncompress_file_tar(fname)
elif zipfile.is_zipfile(fname):
uncompressed_path = _uncompress_file_zip(fname)
else:
raise TypeError(f"Unsupported compress file type {fname}")
return uncompressed_path
def _uncompress_file_zip(filepath):
with zipfile.ZipFile(filepath, 'r') as files:
file_list = files.namelist()
file_dir = os.path.dirname(filepath)
if _is_a_single_file(file_list):
rootpath = file_list[0]
uncompressed_path = os.path.join(file_dir, rootpath)
_safe_extract_zip(files, file_dir)
elif _is_a_single_dir(file_list):
# `strip(os.sep)` to remove `os.sep` in the tail of path
rootpath = os.path.splitext(file_list[0].strip(os.sep))[0].split(
os.sep
)[-1]
uncompressed_path = os.path.join(file_dir, rootpath)
_safe_extract_zip(files, file_dir)
else:
rootpath = os.path.splitext(filepath)[0].split(os.sep)[-1]
uncompressed_path = os.path.join(file_dir, rootpath)
if not os.path.exists(uncompressed_path):
os.makedirs(uncompressed_path)
_safe_extract_zip(files, os.path.join(file_dir, rootpath))
return uncompressed_path
def _safe_extract_tar(tar, path, members=None, on_unsafe='skip'):
"""
Safely extract tar files to prevent path traversal attacks.
Security measures:
1. Verify resolved paths are within target directory
2. Skip or reject symlinks, hardlinks and other special files
3. Only extract regular files and directories
"""
if on_unsafe not in ('skip', 'raise'):
raise ValueError(
f"on_unsafe must be one of 'skip' or 'raise', but got {on_unsafe!r}"
)
members_to_check = members if members is not None else tar.getmembers()
extract_members = []
for member in members_to_check:
if not _safe_extract_member(member, path, 'tar'):
raise ValueError(
f"Attempted path traversal in tar file: {member.name}"
)
# Skip or reject symlinks, hardlinks, and other special files to prevent symlink attacks
if member.issym():
if on_unsafe == 'raise':
raise ValueError(
"Unsafe tar member rejected for security: "
f"{member.name} (symbolic link)"
)
warnings.warn(
f"Skipping symbolic link in tar for security: {member.name}",
category=UserWarning,
stacklevel=2,
)
continue
elif member.islnk():
if on_unsafe == 'raise':
raise ValueError(
"Unsafe tar member rejected for security: "
f"{member.name} (hard link)"
)
warnings.warn(
f"Skipping hard link in tar for security: {member.name}",
category=UserWarning,
stacklevel=2,
)
continue
elif not (member.isfile() or member.isdir()):
if on_unsafe == 'raise':
raise ValueError(
"Unsafe tar member rejected for security: "
f"{member.name} (special file)"
)
warnings.warn(
f"Skipping special file in tar for security: {member.name}",
category=UserWarning,
stacklevel=2,
)
continue
extract_members.append(member)
tar.extractall(path, members=extract_members)
def _safe_extract_zip(zip, path, members=None):
members_to_check = members if members is not None else zip.infolist()
for member in members_to_check:
if not _safe_extract_member(member, path, 'zip'):
raise ValueError(
f"Attempted path traversal in zip file: {member.filename}"
)
zip.extractall(path, members=members_to_check)
def _uncompress_file_tar(filepath, mode="r:*"):
with tarfile.open(filepath, mode) as files:
file_list = files.getnames()
file_dir = os.path.dirname(filepath)
if _is_a_single_file(file_list):
rootpath = file_list[0]
uncompressed_path = os.path.join(file_dir, rootpath)
_safe_extract_tar(files, file_dir)
elif _is_a_single_dir(file_list):
rootpath = os.path.splitext(file_list[0].strip(os.sep))[0].split(
os.sep
)[-1]
uncompressed_path = os.path.join(file_dir, rootpath)
_safe_extract_tar(files, file_dir)
else:
rootpath = os.path.splitext(filepath)[0].split(os.sep)[-1]
uncompressed_path = os.path.join(file_dir, rootpath)
if not os.path.exists(uncompressed_path):
os.makedirs(uncompressed_path)
_safe_extract_tar(files, os.path.join(file_dir, rootpath))
return uncompressed_path
def _safe_extract_member(member, target_dir, archive_type='tar'):
# Get member name
if archive_type == 'tar':
member_name = member.name
else: # zip
member_name = member.filename
# Reject absolute paths
if os.path.isabs(member_name):
warnings.warn(
f"Rejected absolute path in archive: {member_name}",
category=UserWarning,
stacklevel=2,
)
return False
# Resolve target path and normalize
target_path = os.path.normpath(os.path.join(target_dir, member_name))
target_path = os.path.abspath(target_path)
# Ensure resolved path is within target_dir
if not target_path.startswith(os.path.abspath(target_dir) + os.sep):
warnings.warn(
f"Rejected path traversal attempt: {member_name} -> {target_path}",
category=UserWarning,
stacklevel=2,
)
return False
return True
def _is_a_single_file(file_list):
if len(file_list) == 1 and file_list[0].find(os.sep) < 0:
return True
return False
def _is_a_single_dir(file_list):
new_file_list = []
for file_path in file_list:
if '/' in file_path:
file_path = file_path.replace('/', os.sep)
elif '\\' in file_path:
file_path = file_path.replace('\\', os.sep)
new_file_list.append(file_path)
file_name = new_file_list[0].split(os.sep)[0]
for i in range(1, len(new_file_list)):
if file_name != new_file_list[i].split(os.sep)[0]:
return False
return True
def check_and_create_dir(path):
if path is None:
return
assert isinstance(path, str), "path must be string type"
if os.path.exists(path):
if not os.path.isdir(path):
raise NotADirectoryError(f" path:'{path}' must be directory ")
else:
try:
os.makedirs(path)
except Exception as e:
raise OSError(f"Create '{path}' failed : {e}")
+155
View File
@@ -0,0 +1,155 @@
# 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 os
from typing import Generic, TypeVar
from typing_extensions import Self
T = TypeVar("T")
def strtobool(val):
val = val.lower()
if val in ['y', 'yes', 't', 'true', 'on', '1']:
return True
elif val in ['n', 'no', 'f', 'false', 'off', '0']:
return False
else:
raise ValueError(f"Invalid truth value {val!r}")
class EnvironmentVariable(Generic[T]):
name: str
default: T
def __init__(self, name: str, default: T):
self.name = name
self.default = default
self._last_env_value: str | None = None
self._cached_value: T | None = None
def get(self) -> T:
_current_env_value = os.getenv(self.name)
if (
self._cached_value is None
or self._last_env_value != _current_env_value
):
self._cached_value = self.parse_from_string()
self._last_env_value = _current_env_value
return self._cached_value
def set(self, value: T) -> None:
os.environ[self.name] = self.convert_to_string(value)
self._cached_value = value
def parse_from_string(self) -> T:
raise NotImplementedError
def convert_to_string(self, value: T) -> str:
raise NotImplementedError
def delete(self) -> None:
del os.environ[self.name]
def __repr__(self) -> str:
return f"Env({self.name}={self.get()!r})"
class StringEnvironmentVariable(EnvironmentVariable[str]):
def __init__(self, name: str, default: str):
super().__init__(name, default)
assert isinstance(default, str), "default must be a string"
def parse_from_string(self) -> str:
return os.getenv(self.name, self.default)
def convert_to_string(self, value: str) -> str:
assert isinstance(value, str), "value must be a string"
return value
class BooleanEnvironmentVariable(EnvironmentVariable[bool]):
def __init__(self, name: str, default: bool):
super().__init__(name, default)
assert isinstance(default, bool), "default must be a boolean"
def parse_from_string(self) -> bool:
default = str(self.default)
env_str = os.getenv(self.name, default)
return strtobool(env_str)
def convert_to_string(self, value: bool) -> str:
assert isinstance(value, bool), "value must be a boolean"
return str(value).lower()
def __bool__(self) -> bool:
raise ValueError(
"BooleanEnvironmentVariable does not support bool(), "
"please use get() instead."
)
class IntegerEnvironmentVariable(EnvironmentVariable[int]):
def __init__(self, name: str, default: int):
super().__init__(name, default)
assert isinstance(default, int) and not isinstance(default, bool), (
"default must be an integer"
)
def parse_from_string(self) -> int:
try:
return int(os.getenv(self.name, str(self.default)))
except ValueError:
return self.default
def convert_to_string(self, value: int) -> str:
assert isinstance(value, int) and not isinstance(value, bool), (
"value must be an integer"
)
return str(value)
class StringListEnvironmentVariable(EnvironmentVariable[list[str]]):
def __init__(self, name: str, default: list[str]):
super().__init__(name, default)
assert isinstance(default, list), "default must be a list"
def parse_from_string(self) -> list[str]:
return os.getenv(self.name, ",".join(self.default)).split(",")
def convert_to_string(self, value: list[str]) -> str:
assert isinstance(value, list), "value must be a list"
assert all(isinstance(x, str) for x in value), (
"value must be a list of strings"
)
return ",".join(value)
class EnvironmentVariableGuard(Generic[T]):
variable: EnvironmentVariable[T]
original_value: T
def __init__(self, variable: EnvironmentVariable[T], value: T):
self.variable = variable
self.original_value = variable.get()
self.variable.set(value)
def __enter__(self) -> Self:
return self
def __exit__(self, exc_type, exc_value, traceback) -> None:
self.variable.set(self.original_value)
+377
View File
@@ -0,0 +1,377 @@
# 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.
import copy
_FLOPS_COMPUTE_FUNC_MAP = {}
def prod(s):
p = 1
for v in s:
p *= v
return p
def flops(op_type: str, input_shapes: dict, attrs: dict) -> int:
"""
count FLOPs for operation.
Args:
op_type (str): the type of operation.
input_shapes (dict): the shapes of inputs.
attrs (dict): the attributes of the operation.
Returns:
the total FLOPs of the operation.
"""
if op_type not in _FLOPS_COMPUTE_FUNC_MAP:
return 0
else:
func = _FLOPS_COMPUTE_FUNC_MAP[op_type]
try:
flops = func(input_shapes, attrs)
except Exception as e:
return 0
return flops
def register_flops(op_type):
"""
register flops computation function for operation.
"""
def register(func):
global _FLOPS_COMPUTE_FUNC_MAP
_FLOPS_COMPUTE_FUNC_MAP[op_type] = func
return func
return register
@register_flops("c_embedding")
def _c_embedding_flops(input_shapes, attrs):
"""FLOPs computation for c_embedding op.
For c_embedding(input):
equation: flops = 0
"""
return 0
@register_flops("conv2d")
def _conv2d_flops(input_shapes, attrs):
"""FLOPs computation for conv2d op.
For conv2d(input,filter):
active_elements = batch_size * numel(output)
conv_flops = 2 * macs_per_position_conv * active_elements
bias_flops = out_channels * active_elements
equation: flops = conv_flops + bias_flops
"""
bias = (
input_shapes.get('Bias')[0]
if len(input_shapes.get('Bias')) > 0
else None
)
input = input_shapes.get('Input')[0]
weight = input_shapes.get('Filter')[0]
padding = attrs.get('paddings')
stride = attrs.get('strides')
dilation = attrs.get('dilations')
groups = attrs.get('groups')
batch_size = input[0]
in_channels = input[1]
out_channels = weight[0]
kernel_dims = list(weight[2:])
input_dims = list(input[2:])
length = len(input_dims)
paddings = (
padding
if isinstance(padding, list)
else [
padding,
]
* length
)
strides = (
stride
if isinstance(stride, list)
else [
stride,
]
* length
)
dilations = (
dilation
if isinstance(dilation, list)
else [
dilation,
]
* length
)
output_dims = []
for idx, input_dim in enumerate(input_dims):
output_dim = (
input_dim
+ 2 * paddings[idx]
- (dilations[idx] * (kernel_dims[idx] - 1) + 1)
) // strides[idx] + 1
output_dims.append(output_dim)
filters_per_channel = out_channels // groups
macs_conv_per_position = (
prod(kernel_dims) * in_channels * filters_per_channel
)
active_elements = batch_size * prod(output_dims)
overall_conv_macs = macs_conv_per_position * active_elements
overall_conv_flops = 2 * overall_conv_macs
overall_bias_flops = 0
if bias is not None:
overall_bias_flops = out_channels * active_elements
return overall_conv_flops + overall_bias_flops
@register_flops("dropout")
def _dropout_flops(input_shapes, attrs):
"""FLOPs computation for dropout op.
For dropout(input):
equation: flops = 0
"""
return 0
def _elementwise_flops_compute(input_shapes, attrs):
input_x = input_shapes.get("X")[0]
input_y = input_shapes.get("Y")[0]
dim_x = len(input_x)
dim_y = len(input_y)
dim_output = max(dim_x, dim_y)
output = []
for i in range(dim_output):
in_x = input_x[dim_x - 1 - i] if i < dim_x else 1
in_y = input_y[dim_y - 1 - i] if i < dim_y else 1
output.append(max(in_x, in_y))
return prod(output)
@register_flops("elementwise_add")
def _elementwise_add_flops(input_shapes, attrs):
"""FLOPs computation for elementwise_add op.
For elementwise_add(input,other):
input_shapes = [shape_of_input, shape_of_other]
shape_of_input = [dim1, dim2, dim3 ...]
shape_of_other = [odim1, odim2, odim3...]
equation: flops = max(dim1, odim1) * max(dim2, odim2) * max()...
"""
return _elementwise_flops_compute(input_shapes, attrs)
@register_flops("elementwise_mul")
def _elementwise_mul_flops(input_shapes, attrs):
"""FLOPs computation for elementwise_mul op.
For elementwise_mul(input,other):
input_shapes = [shape_of_input, shape_of_other]
shape_of_input = [dim1, dim2, dim3 ...]
shape_of_other = [odim1, odim2, odim3...]
equation: flops = max(dim1, odim1) * max(dim2, odim2)* max()...
"""
return _elementwise_flops_compute(input_shapes, attrs)
@register_flops("elementwise_div")
def _elementwise_div_flops(input_shapes, attrs):
"""FLOPs computation for elementwise_div op.
For elementwise_div(input,other):
input_shapes = [shape_of_input, shape_of_other]
shape_of_input = [dim1, dim2, dim3 ...]
shape_of_other = [odim1, odim2, odim3...]
equation: flops = max(dim1,odim1)*max(dim2,odim2)*max()...
"""
return _elementwise_flops_compute(input_shapes, attrs)
@register_flops("gelu")
def _gelu_flops(input_shapes, attrs):
"""FLOPs computation for gelu op.
For gelu(input):
equation: flops = 5 * (numel)total number of elements in the input tensor.
"""
input = input_shapes.get('X')[0]
return prod(input) * 5
@register_flops("layer_norm")
def _layer_norm_flops(input_shapes, attrs):
"""FLOPs computation for layer_norm op.
For layer_norm(input):
equation:
1): WITHOUT epsilon flops = 7 * (numel)total number of elements in the input tensor.
2): WITH epsilon flops = 8 * (numel)total number of elements in the input tensor.
"""
input = input_shapes.get('X')[0]
flops = prod(input) * 7
if attrs.get('epsilon'):
flops += prod(input)
return flops
@register_flops("matmul")
def _matmul_flops(input_shapes, attrs):
"""FLOPs computation for matmul op.
For matmul(input,other):
input_shapes = [shape_of_input, shape_of_other]
shape_of_input = [dim1,dim2 ...dim_n_1,dim_n] length:n
shape_of_other = [odim1,odim2 ... odim(n-m)... odim_m_1,dim_m] length:m
suppose n > m and dim_n = odim_m_1:
shape_of_output = [dim1, dim2 ... max(dim(n-m), odim(n-m)), max(dim(n-m+1), odim(n-m+1)) ... dim_n_1, dim_m]
equation: flops = 2 * numel(output) * dim_n
"""
x_shape = copy.deepcopy(
input_shapes.get("X", input_shapes.get("x", [[0]]))[0]
)
y_shape = copy.deepcopy(
input_shapes.get("Y", input_shapes.get("y", [[0]]))[0]
)
if attrs.get('transpose_X') or attrs.get('transpose_x'):
x_shape[-1], x_shape[-2] = x_shape[-2], x_shape[-1]
if attrs.get('transpose_Y') or attrs.get('transpose_y'):
y_shape[-1], y_shape[-2] = y_shape[-2], y_shape[-1]
dim_x = len(x_shape)
dim_y = len(y_shape)
output_len = max(dim_x, dim_y)
output_shape = []
for idx in range(output_len, 2, -1):
x_idx = x_shape[dim_x - idx] if idx <= dim_x else 1
y_idx = y_shape[dim_y - idx] if idx <= dim_y else 1
output_shape.append(max(x_idx, y_idx))
macs = prod(output_shape) * x_shape[-2] * x_shape[-1] * y_shape[-1]
return 2 * macs
@register_flops("matmul_v2")
def _matmul_v2_flops(input_shapes, attrs):
"""FLOPs computation for matmul_v2 op.
For matmul_v2(input,other):
input_shapes = [shape_of_input, shape_of_other]
shape_of_input = [dim1, dim2 ...dim_n_1, dim_n] length:n
shape_of_other = [odim1, odim2 ... odim(n-m) ... odim_m_1, dim_m] length:m
suppose n > m and dim_n = odim_m_1:
shape_of_output = [dim1, dim2 ... max(dim(n-m), odim(n-m)), max(dim(n-m+1), odim(n-m+1))...dim_n_1, dim_m]
equation: flops = 2 * numel(outputs) * dim_n
"""
x_shape = copy.deepcopy(input_shapes.get('X')[0])
y_shape = copy.deepcopy(input_shapes.get('Y')[0])
if attrs.get('trans_x'):
x_shape[-1], x_shape[-2] = x_shape[-2], x_shape[-1]
if attrs.get('trans_y'):
y_shape[-1], y_shape[-2] = y_shape[-2], y_shape[-1]
dim_x = len(x_shape)
dim_y = len(y_shape)
output_len = max(dim_x, dim_y)
output_shape = []
for idx in range(output_len, 2, -1):
x_idx = x_shape[dim_x - idx] if idx <= dim_x else 1
y_idx = y_shape[dim_y - idx] if idx <= dim_y else 1
output_shape.append(max(x_idx, y_idx))
macs = prod(output_shape) * x_shape[-2] * x_shape[-1] * y_shape[-1]
return 2 * macs
def _relu_class_flops(input_shapes, attrs):
"""FLOPs computation for relu_like ops.
For elu/leaky_relu/prelu/relu/relu6/silu (input):
equation: flops = (numel)total number of elements in the input tensor.
"""
input = input_shapes.get('X')[0]
return prod(input)
@register_flops("elu")
def _elu_flops(input_shapes, attrs):
return _relu_class_flops(input_shapes, attrs)
@register_flops("leaky_relu")
def _leaky_relu_flops(input_shapes, attrs):
return _relu_class_flops(input_shapes, attrs)
@register_flops("prelu")
def _prelu_flops(input_shapes, attrs):
return _relu_class_flops(input_shapes, attrs)
@register_flops("relu")
def _relu_flops(input_shapes, attrs):
return _relu_class_flops(input_shapes, attrs)
@register_flops("relu6")
def _relu6_flops(input_shapes, attrs):
return _relu_class_flops(input_shapes, attrs)
@register_flops("silu")
def _silu_flops(input_shapes, attrs):
return _relu_class_flops(input_shapes, attrs)
@register_flops("reshape2")
def _reshape2_flops(input_shapes, attrs):
"""FLOPs computation for reshape2 op.
For reshape2(input):
equation: flops = 0
"""
return 0
@register_flops("softmax")
def _softmax_flops(input_shapes, attrs):
"""FLOPs computation for softmax op.
For softmax(input):
equation: flops = 3 * (numel)total number of elements in the input tensor.
"""
input = input_shapes.get('X')[0]
return prod(input) * 3
@register_flops("transpose2")
def _transpose2_flops(input_shapes, attrs):
"""FLOPs computation for transpose2 op.
For transpose2(input):
equation: flops = 0
"""
return 0
@register_flops("pool")
def _pool_flops(input_shapes, attrs):
"""FLOPs computation for pool op.
For pool(input):
equation: flops = (numel)total number of elements in the input tensor.
"""
input = input_shapes.get('X')[0]
return prod(input)
+282
View File
@@ -0,0 +1,282 @@
# 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 io
import os
import re
import sys
import tempfile
from contextlib import contextmanager
import paddle
from paddle.base.wrapped_decorator import signature_safe_contextmanager
def _parse_tensors(input_str):
input_tuples = re.findall(
r"\(\s*(\w+)\s*,\s*(\{.*?\}|\[.*?\])\s*\)", input_str, re.DOTALL
)
tensors_list = []
for var_name, value_str in input_tuples:
# Only one Tensor
if value_str.startswith('{'):
tensor_info = _parse_tensor_info(value_str)
tensors_list.append((var_name, [tensor_info]))
# Tensor list
elif value_str.startswith('['):
list_content = value_str.strip('[]')
dict_strs = _split_list_elements(list_content)
tensor_list = []
for dict_str in dict_strs:
tensor_info = _parse_tensor_info(dict_str)
if tensor_info:
tensor_list.append(tensor_info)
tensors_list.append((var_name, tensor_list))
return tensors_list
def _parse_api_name(debug_str):
api_name_match = re.search(r"API_Name:\s*(\w+)", debug_str)
return api_name_match.group(1) if api_name_match else None
def _parse_input_tensors(debug_str):
input_match = re.search(r"Input:\s*(\[.*?\] )", debug_str, re.DOTALL)
return _parse_tensors(input_match.group(1)) if input_match else []
def _parse_output_tensors(debug_str):
output_match = re.search(r"Output:\s*(\[.*?\] )", debug_str, re.DOTALL)
return _parse_tensors(output_match.group(1)) if output_match else []
def _parse_tensor_info(dict_str):
result = {}
lines = dict_str.strip().split('\n')
for line in lines:
line = line.strip()
if line and ':' in line:
key, value = line.split(':', 1)
key = key.strip()
value = value.strip()
if key == "Place":
place_match = re.search(r"Place\((\w+:\d+)\)", value)
if place_match:
value = place_match.group(1)
elif key == "Shape":
value = [int(x.strip()) for x in value.split(',') if x.strip()]
elif value == "None":
value = None
result[key] = value
return result
def _split_list_elements(list_str):
elements = []
current = []
brace_count = 0
for char in list_str:
if char == '{':
brace_count += 1
elif char == '}':
brace_count -= 1
if char == ',' and brace_count == 0:
elements.append(''.join(current).strip())
current = []
else:
current.append(char)
if current:
elements.append(''.join(current).strip())
return elements
def parse_debug_info(debug_str):
result = {"API_Name": None, "Input": [], "Output": []}
result["API_Name"] = _parse_api_name(debug_str)
result["Input"] = _parse_input_tensors(debug_str)
result["Output"] = _parse_output_tensors(debug_str)
return result
class Edge:
def __init__(self, tensor_info: dict, source=""):
self.name = tensor_info["Name"]
self.shape = tensor_info["Shape"]
self.dtype = tensor_info["Dtype"]
self.source = source
def get_name(self) -> str:
return self.name
def get_source(self) -> str:
return self.source
def set_source(self, source):
self.source = source
def __str__(self) -> str:
return f"Edge(name='{self.name}', source='{self.source}',shape='{self.shape}',dtype='{self.dtype}')"
def get_edge_info(self):
return f"{self.name}\nshape:{self.shape}\ndtype:{self.dtype}"
class Graph:
def __init__(self):
from graphviz import Digraph
self.dot = Digraph()
self.orange_box_attrs = {
'style': 'rounded,filled,bold',
'shape': 'box',
'color': '#FFE4B5',
'fillcolor': '#FFE4B5',
'fontcolor': '#ffffff',
'width': '1.3',
'height': '0.84',
'fontname': 'Arial',
}
self.grey_box_attrs = {
'style': 'rounded,filled,bold',
'shape': 'box',
'color': '#999999',
'fillcolor': '#999999',
'fontcolor': '#ffffff',
'width': '1.3',
'height': '0.84',
'fontname': 'Arial',
}
self.edges = {}
self.nodes = []
def add_node(self, name: str, node_attr=""):
if not node_attr:
node_attr = self.grey_box_attrs
self.dot.node(name, name, **node_attr)
self.nodes.append(name)
# Link the src and dst node by edge
def add_edge(self, dst: str, edge: Edge):
edge_name = edge.get_name()
if edge_name not in self.edges:
# The Edge is not stored in the graph,
# so the Edge's source node maybe not in the graph
if not edge.get_source():
src_name = edge.get_name() + "_SourceNode"
edge.set_source(src_name)
self.add_node(src_name, self.orange_box_attrs)
else:
edge = self.edges[edge_name]
src = edge.get_source()
self.dot.edge(src, dst, label=edge.get_edge_info())
# Store an edge, not link the src and dst node
def store_edge(self, edge: Edge):
edge_name = edge.get_name()
self.edges[edge_name] = edge
def render(self, file_path):
self.dot.render(file_path, format='svg')
class GraphBuilder:
def __init__(self):
self.graph = Graph()
def build_graph(self, forward_debug_infos: list):
for info in forward_debug_infos:
debug_info = parse_debug_info(info)
api_name = debug_info['API_Name']
# Add a node for the API
self.graph.add_node(api_name)
# Store the Edge
for out_param in debug_info["Output"]:
var_name = out_param[0]
tensors = out_param[1]
for tensor_info in tensors:
# When we do not know the edge's dst, we should store it to the Graph
edge = Edge(tensor_info, api_name)
self.graph.store_edge(edge)
# Link the Edge
for input_param in debug_info["Input"]:
var_name = input_param[0]
tensors = input_param[1]
for tensor_info in tensors:
edge = Edge(tensor_info)
self.graph.add_edge(dst=api_name, edge=edge)
def save_graph(self, file_path):
self.graph.render(file_path)
@contextmanager
def capture_stderr():
with tempfile.TemporaryFile(mode='w+b') as temp_file:
original_stderr_fd = sys.stderr.fileno()
saved_stderr_fd = os.dup(original_stderr_fd)
stderr_output = io.StringIO()
try:
os.dup2(temp_file.fileno(), original_stderr_fd)
sys.stderr.flush()
yield stderr_output
finally:
sys.stderr.flush()
os.dup2(saved_stderr_fd, original_stderr_fd)
os.close(saved_stderr_fd)
temp_file.seek(0)
stderr_output.write(temp_file.read().decode())
@signature_safe_contextmanager
def capture_forward_subgraph_guard(file_path: str):
log = ""
stderr_buffer = io.StringIO()
origin_enable_unique_name_status = paddle.framework.get_flags(
"FLAGS_enable_unique_name"
)["FLAGS_enable_unique_name"]
try:
paddle.set_flags({"FLAGS_enable_unique_name": True})
# Redirect the stderr to the buffer,because the glog info will be printed to the stderr
with (
capture_stderr() as stderr_buffer,
paddle.base.framework.vlog_guard(3),
):
yield
finally:
paddle.set_flags(
{"FLAGS_enable_unique_name": origin_enable_unique_name_status}
)
log = stderr_buffer.getvalue()
builder = GraphBuilder()
def get_first_indent(s):
match = re.match(r'^\t*', s)
return match.group(0)
# Find the parts describing the input and output of the API in the massive logs
indent = get_first_indent(log.lstrip('\n'))
pattern_str = r'\n' + indent + r'Forward Debug Info \{.*? ] } '
pattern = re.compile(pattern_str, re.DOTALL)
matches = pattern.findall(log)
# Build the forward graph
builder.build_graph(matches)
builder.save_graph(file_path)
+33
View File
@@ -0,0 +1,33 @@
# Copyright (c) 2016, Serge Guelton
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of HPCProject, Serge Guelton nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
# representation. See https://github.com/serge-sans-paille/gast for details.
from .gast import *
from ast import NodeVisitor, NodeTransformer, iter_fields
+602
View File
@@ -0,0 +1,602 @@
# Copyright (c) 2016, Serge Guelton
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of HPCProject, Serge Guelton nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
# representation. See https://github.com/serge-sans-paille/gast for details.
from .astn import AstToGAst, GAstToAst
from . import gast
import ast
import sys
class Ast3ToGAst(AstToGAst):
if sys.version_info.minor == 12:
def visit_TypeVar(self, node):
new_node = gast.TypeVar(
self._visit(node.name),
self._visit(node.bound),
None
)
return gast.copy_location(new_node, node)
def visit_TypeVarTuple(self, node):
new_node = gast.TypeVarTuple(
self._visit(node.name),
None
)
return gast.copy_location(new_node, node)
def visit_ParamSpec(self, node):
new_node = gast.ParamSpec(
self._visit(node.name),
None
)
return gast.copy_location(new_node, node)
if sys.version_info.minor < 10:
def visit_alias(self, node):
new_node = gast.alias(
self._visit(node.name),
self._visit(node.asname),
)
new_node.lineno = new_node.col_offset = None
new_node.end_lineno = new_node.end_col_offset = None
return new_node
if sys.version_info.minor < 9:
def visit_ExtSlice(self, node):
new_node = gast.Tuple(self._visit(node.dims), gast.Load())
return gast.copy_location(new_node, node)
def visit_Index(self, node):
return self._visit(node.value)
def visit_Assign(self, node):
new_node = gast.Assign(
self._visit(node.targets),
self._visit(node.value),
None, # type_comment
)
gast.copy_location(new_node, node)
new_node.end_lineno = new_node.end_col_offset = None
return new_node
if sys.version_info.minor < 8:
def visit_Module(self, node):
new_node = gast.Module(
self._visit(node.body),
[] # type_ignores
)
return new_node
def visit_Num(self, node):
new_node = gast.Constant(
node.n,
None,
)
return gast.copy_location(new_node, node)
def visit_Ellipsis(self, node):
new_node = gast.Constant(
Ellipsis,
None,
)
gast.copy_location(new_node, node)
new_node.end_lineno = new_node.end_col_offset = None
return new_node
def visit_Str(self, node):
new_node = gast.Constant(
node.s,
None,
)
return gast.copy_location(new_node, node)
def visit_Bytes(self, node):
new_node = gast.Constant(
node.s,
None,
)
return gast.copy_location(new_node, node)
def visit_FunctionDef(self, node):
new_node = gast.FunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
None, # type_comment
[], # type_params
)
return gast.copy_location(new_node, node)
def visit_AsyncFunctionDef(self, node):
new_node = gast.AsyncFunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
None, # type_comment
[], # type_params
)
return gast.copy_location(new_node, node)
def visit_For(self, node):
new_node = gast.For(
self._visit(node.target),
self._visit(node.iter),
self._visit(node.body),
self._visit(node.orelse),
None, # type_comment
)
return gast.copy_location(new_node, node)
def visit_AsyncFor(self, node):
new_node = gast.AsyncFor(
self._visit(node.target),
self._visit(node.iter),
self._visit(node.body),
self._visit(node.orelse),
None, # type_comment
)
return gast.copy_location(new_node, node)
def visit_With(self, node):
new_node = gast.With(
self._visit(node.items),
self._visit(node.body),
None, # type_comment
)
return gast.copy_location(new_node, node)
def visit_AsyncWith(self, node):
new_node = gast.AsyncWith(
self._visit(node.items),
self._visit(node.body),
None, # type_comment
)
return gast.copy_location(new_node, node)
def visit_Call(self, node):
if sys.version_info.minor < 5:
if node.starargs:
star = gast.Starred(self._visit(node.starargs),
gast.Load())
gast.copy_location(star, node)
starred = [star]
else:
starred = []
if node.kwargs:
kw = gast.keyword(None, self._visit(node.kwargs))
gast.copy_location(kw, node.kwargs)
kwargs = [kw]
else:
kwargs = []
else:
starred = kwargs = []
new_node = gast.Call(
self._visit(node.func),
self._visit(node.args) + starred,
self._visit(node.keywords) + kwargs,
)
return gast.copy_location(new_node, node)
def visit_NameConstant(self, node):
if node.value is None:
new_node = gast.Constant(None, None)
elif node.value is True:
new_node = gast.Constant(True, None)
elif node.value is False:
new_node = gast.Constant(False, None)
return gast.copy_location(new_node, node)
def visit_arguments(self, node):
new_node = gast.arguments(
self._visit(node.args),
[], # posonlyargs
self._visit(node.vararg),
self._visit(node.kwonlyargs),
self._visit(node.kw_defaults),
self._visit(node.kwarg),
self._visit(node.defaults),
)
return gast.copy_location(new_node, node)
def visit_Name(self, node):
new_node = gast.Name(
node.id, # micro-optimization here, don't call self._visit
self._visit(node.ctx),
None,
None,
)
return ast.copy_location(new_node, node)
def visit_arg(self, node):
if sys.version_info.minor < 8:
extra_arg = None
else:
extra_arg = self._visit(node.type_comment)
new_node = gast.Name(
node.arg, # micro-optimization here, don't call self._visit
gast.Param(),
self._visit(node.annotation),
extra_arg # type_comment
)
return ast.copy_location(new_node, node)
def visit_ExceptHandler(self, node):
if node.name:
new_node = gast.ExceptHandler(
self._visit(node.type),
gast.Name(node.name, gast.Store(), None, None),
self._visit(node.body))
return ast.copy_location(new_node, node)
else:
return self.generic_visit(node)
if sys.version_info.minor < 6:
def visit_comprehension(self, node):
new_node = gast.comprehension(
target=self._visit(node.target),
iter=self._visit(node.iter),
ifs=self._visit(node.ifs),
is_async=0,
)
return ast.copy_location(new_node, node)
if 8 <= sys.version_info.minor < 12:
def visit_FunctionDef(self, node):
new_node = gast.FunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
self._visit(node.type_comment),
[], # type_params
)
return gast.copy_location(new_node, node)
def visit_AsyncFunctionDef(self, node):
new_node = gast.AsyncFunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
self._visit(node.type_comment),
[], # type_params
)
return gast.copy_location(new_node, node)
if sys.version_info.minor < 12:
def visit_ClassDef(self, node):
new_node = gast.ClassDef(
self._visit(node.name),
self._visit(node.bases),
self._visit(node.keywords),
self._visit(node.body),
self._visit(node.decorator_list),
[], # type_params
)
return gast.copy_location(new_node, node)
class GAstToAst3(GAstToAst):
if sys.version_info.minor == 12:
def visit_TypeVar(self, node):
new_node = ast.TypeVar(
self._visit(node.name),
self._visit(node.bound)
)
return ast.copy_location(new_node, node)
def visit_TypeVarTuple(self, node):
new_node = ast.TypeVarTuple(
self._visit(node.name),
)
return ast.copy_location(new_node, node)
def visit_ParamSpec(self, node):
new_node = ast.ParamSpec(
self._visit(node.name),
)
return ast.copy_location(new_node, node)
if sys.version_info.minor < 10:
def visit_alias(self, node):
new_node = ast.alias(
self._visit(node.name),
self._visit(node.asname)
)
return new_node
if sys.version_info.minor < 9:
def visit_Subscript(self, node):
def adjust_slice(s):
if isinstance(s, ast.Slice):
return s
else:
return ast.Index(s)
if isinstance(node.slice, gast.Tuple):
if any(isinstance(elt, gast.slice) for elt in node.slice.elts):
new_slice = ast.ExtSlice(
[adjust_slice(x) for x in
self._visit(node.slice.elts)])
else:
value = ast.Tuple(self._visit(node.slice.elts), ast.Load())
ast.copy_location(value, node.slice)
new_slice = ast.Index(value)
else:
new_slice = adjust_slice(self._visit(node.slice))
ast.copy_location(new_slice, node.slice)
new_node = ast.Subscript(
self._visit(node.value),
new_slice,
self._visit(node.ctx),
)
return ast.copy_location(new_node, node)
def visit_Assign(self, node):
new_node = ast.Assign(
self._visit(node.targets),
self._visit(node.value),
)
return ast.copy_location(new_node, node)
if sys.version_info.minor < 8:
def visit_Module(self, node):
new_node = ast.Module(self._visit(node.body))
return new_node
def visit_Constant(self, node):
if node.value is None:
new_node = ast.NameConstant(node.value)
elif node.value is Ellipsis:
new_node = ast.Ellipsis()
elif isinstance(node.value, bool):
new_node = ast.NameConstant(node.value)
elif isinstance(node.value, (int, float, complex)):
new_node = ast.Num(node.value)
elif isinstance(node.value, str):
new_node = ast.Str(node.value)
else:
new_node = ast.Bytes(node.value)
return ast.copy_location(new_node, node)
def _make_arg(self, node):
if node is None:
return None
if sys.version_info.minor < 8:
extra_args = tuple()
else:
extra_args = self._visit(node.type_comment),
new_node = ast.arg(
self._visit(node.id),
self._visit(node.annotation),
*extra_args
)
return ast.copy_location(new_node, node)
def visit_Name(self, node):
new_node = ast.Name(
self._visit(node.id),
self._visit(node.ctx),
)
return ast.copy_location(new_node, node)
def visit_ExceptHandler(self, node):
if node.name:
new_node = ast.ExceptHandler(
self._visit(node.type),
node.name.id,
self._visit(node.body))
return ast.copy_location(new_node, node)
else:
return self.generic_visit(node)
if sys.version_info.minor < 5:
def visit_Call(self, node):
if node.args and isinstance(node.args[-1], gast.Starred):
args = node.args[:-1]
starargs = node.args[-1].value
else:
args = node.args
starargs = None
if node.keywords and node.keywords[-1].arg is None:
keywords = node.keywords[:-1]
kwargs = node.keywords[-1].value
else:
keywords = node.keywords
kwargs = None
new_node = ast.Call(
self._visit(node.func),
self._visit(args),
self._visit(keywords),
self._visit(starargs),
self._visit(kwargs),
)
return ast.copy_location(new_node, node)
def visit_ClassDef(self, node):
self.generic_visit(node)
new_node = ast.ClassDef(
name=self._visit(node.name),
bases=self._visit(node.bases),
keywords=self._visit(node.keywords),
body=self._visit(node.body),
decorator_list=self._visit(node.decorator_list),
starargs=None,
kwargs=None,
)
return ast.copy_location(new_node, node)
elif sys.version_info.minor < 8:
def visit_FunctionDef(self, node):
new_node = ast.FunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
)
return ast.copy_location(new_node, node)
def visit_AsyncFunctionDef(self, node):
new_node = ast.AsyncFunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
)
return ast.copy_location(new_node, node)
def visit_For(self, node):
new_node = ast.For(
self._visit(node.target),
self._visit(node.iter),
self._visit(node.body),
self._visit(node.orelse),
)
return ast.copy_location(new_node, node)
def visit_AsyncFor(self, node):
new_node = ast.AsyncFor(
self._visit(node.target),
self._visit(node.iter),
self._visit(node.body),
self._visit(node.orelse),
None, # type_comment
)
return ast.copy_location(new_node, node)
def visit_With(self, node):
new_node = ast.With(
self._visit(node.items),
self._visit(node.body),
)
return ast.copy_location(new_node, node)
def visit_AsyncWith(self, node):
new_node = ast.AsyncWith(
self._visit(node.items),
self._visit(node.body),
)
return ast.copy_location(new_node, node)
def visit_Call(self, node):
new_node = ast.Call(
self._visit(node.func),
self._visit(node.args),
self._visit(node.keywords),
)
return ast.copy_location(new_node, node)
if 5 <= sys.version_info.minor < 12:
def visit_ClassDef(self, node):
new_node = ast.ClassDef(
self._visit(node.name),
self._visit(node.bases),
self._visit(node.keywords),
self._visit(node.body),
self._visit(node.decorator_list),
)
return ast.copy_location(new_node, node)
if 8 <= sys.version_info.minor < 12:
def visit_FunctionDef(self, node):
new_node = ast.FunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
self._visit(node.type_comment),
)
return ast.copy_location(new_node, node)
def visit_AsyncFunctionDef(self, node):
new_node = ast.AsyncFunctionDef(
self._visit(node.name),
self._visit(node.args),
self._visit(node.body),
self._visit(node.decorator_list),
self._visit(node.returns),
self._visit(node.type_comment),
)
return ast.copy_location(new_node, node)
def visit_arguments(self, node):
extra_args = [self._make_arg(node.vararg),
[self._make_arg(n) for n in node.kwonlyargs],
self._visit(node.kw_defaults),
self._make_arg(node.kwarg),
self._visit(node.defaults), ]
if sys.version_info.minor >= 8:
new_node = ast.arguments(
[self._make_arg(arg) for arg in node.posonlyargs],
[self._make_arg(n) for n in node.args],
*extra_args
)
else:
new_node = ast.arguments(
[self._make_arg(n) for n in node.args],
*extra_args
)
return new_node
def ast_to_gast(node):
return Ast3ToGAst().visit(node)
def gast_to_ast(node):
return GAstToAst3().visit(node)
+74
View File
@@ -0,0 +1,74 @@
# Copyright (c) 2016, Serge Guelton
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of HPCProject, Serge Guelton nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
# representation. See https://github.com/serge-sans-paille/gast for details.
import ast
from . import gast
def _generate_translators(to):
class Translator(ast.NodeTransformer):
def _visit(self, node):
if isinstance(node, ast.AST):
return self.visit(node)
elif isinstance(node, list):
return [self._visit(n) for n in node]
else:
return node
def generic_visit(self, node):
class_name = type(node).__name__
if not hasattr(to, class_name):
# handle nodes that are not part of the AST
return
cls = getattr(to, class_name)
new_node = cls(
**{
field: self._visit(getattr(node, field))
for field in node._fields
if hasattr(node, field)
}
)
for attr in node._attributes:
try:
setattr(new_node, attr, getattr(node, attr))
except AttributeError:
pass
return new_node
return Translator
AstToGAst = _generate_translators(gast)
GAstToAst = _generate_translators(ast)
+718
View File
@@ -0,0 +1,718 @@
# Copyright (c) 2016, Serge Guelton
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of HPCProject, Serge Guelton nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
# representation. See https://github.com/serge-sans-paille/gast for details.
import sys as _sys
import ast as _ast
from ast import boolop, cmpop, excepthandler, expr, expr_context, operator
from ast import slice, stmt, unaryop, mod, AST
from ast import iter_child_nodes, walk
try:
from ast import TypeIgnore
except ImportError:
class TypeIgnore(AST):
pass
try:
from ast import pattern
except ImportError:
class pattern(AST):
pass
try:
from ast import type_param
except ImportError:
class type_param(AST):
pass
def _make_node(Name, Fields, Attributes, Bases):
# This constructor is used a lot during conversion from ast to gast,
# then as the primary way to build ast nodes. So we tried to optimized it
# for speed and not for readability.
def create_node(self, *args, **kwargs):
if len(args) > len(Fields):
raise TypeError(
"{} constructor takes at most {} positional arguments".
format(Name, len(Fields)))
# it's faster to iterate rather than zipping or enumerate
for i in range(len(args)):
setattr(self, Fields[i], args[i])
if kwargs: # cold branch
self.__dict__.update(kwargs)
setattr(_sys.modules[__name__],
Name,
type(Name,
Bases,
{'__init__': create_node,
'_fields': Fields,
'_field_types': {},
'_attributes': Attributes}))
def _fill_field_types(Name, FieldTypes):
node = getattr(_sys.modules[__name__], Name)
assert len(node._fields) == len(FieldTypes), Name
node._field_types.update(zip(node._fields, FieldTypes))
_nodes = (
# mod
('Module', (('body', 'type_ignores'), (), (mod,))),
('Interactive', (('body',), (), (mod,))),
('Expression', (('body',), (), (mod,))),
('FunctionType', (('argtypes', 'returns'), (), (mod,))),
('Suite', (('body',), (), (mod,))),
# stmt
('FunctionDef', (('name', 'args', 'body', 'decorator_list', 'returns',
'type_comment', 'type_params'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('AsyncFunctionDef', (('name', 'args', 'body', 'decorator_list', 'returns',
'type_comment', 'type_params',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset',),
(stmt,))),
('ClassDef', (('name', 'bases', 'keywords', 'body', 'decorator_list',
'type_params',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Return', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Delete', (('targets',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Assign', (('targets', 'value', 'type_comment'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('TypeAlias', (('name', 'type_params', 'value'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('AugAssign', (('target', 'op', 'value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('AnnAssign', (('target', 'annotation', 'value', 'simple',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Print', (('dest', 'values', 'nl',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('For', (('target', 'iter', 'body', 'orelse', 'type_comment'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('AsyncFor', (('target', 'iter', 'body', 'orelse', 'type_comment'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('While', (('test', 'body', 'orelse',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('If', (('test', 'body', 'orelse',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('With', (('items', 'body', 'type_comment'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('AsyncWith', (('items', 'body', 'type_comment'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Match', (('subject', 'cases'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Raise', (('exc', 'cause',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Try', (('body', 'handlers', 'orelse', 'finalbody',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('TryStar', (('body', 'handlers', 'orelse', 'finalbody',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Assert', (('test', 'msg',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Import', (('names',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('ImportFrom', (('module', 'names', 'level',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Exec', (('body', 'globals', 'locals',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Global', (('names',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Nonlocal', (('names',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Expr', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Pass', ((), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Break', ((), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
('Continue', ((),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(stmt,))),
# expr
('BoolOp', (('op', 'values',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('NamedExpr', (('target', 'value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('BinOp', (('left', 'op', 'right',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('UnaryOp', (('op', 'operand',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Lambda', (('args', 'body',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('IfExp', (('test', 'body', 'orelse',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Dict', (('keys', 'values',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Set', (('elts',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('ListComp', (('elt', 'generators',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('SetComp', (('elt', 'generators',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('DictComp', (('key', 'value', 'generators',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('GeneratorExp', (('elt', 'generators',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset',),
(expr,))),
('Await', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Yield', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('YieldFrom', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Compare', (('left', 'ops', 'comparators',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Call', (('func', 'args', 'keywords',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Repr', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('FormattedValue', (('value', 'conversion', 'format_spec',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset',),
(expr,))),
('Interpolation', (('value', 'str', 'conversion', 'format_spec',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset',),
(expr,))),
('JoinedStr', (('values',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('TemplateStr', (('values',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Constant', (('value', 'kind'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Attribute', (('value', 'attr', 'ctx',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Subscript', (('value', 'slice', 'ctx',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Starred', (('value', 'ctx',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Name', (('id', 'ctx', 'annotation', 'type_comment'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('List', (('elts', 'ctx',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
('Tuple', (('elts', 'ctx',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(expr,))),
# expr_context
('Load', ((), (), (expr_context,))),
('Store', ((), (), (expr_context,))),
('Del', ((), (), (expr_context,))),
('AugLoad', ((), (), (expr_context,))),
('AugStore', ((), (), (expr_context,))),
('Param', ((), (), (expr_context,))),
# slice
('Slice', (('lower', 'upper', 'step'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
(slice,))),
# boolop
('And', ((), (), (boolop,))),
('Or', ((), (), (boolop,))),
# operator
('Add', ((), (), (operator,))),
('Sub', ((), (), (operator,))),
('Mult', ((), (), (operator,))),
('MatMult', ((), (), (operator,))),
('Div', ((), (), (operator,))),
('Mod', ((), (), (operator,))),
('Pow', ((), (), (operator,))),
('LShift', ((), (), (operator,))),
('RShift', ((), (), (operator,))),
('BitOr', ((), (), (operator,))),
('BitXor', ((), (), (operator,))),
('BitAnd', ((), (), (operator,))),
('FloorDiv', ((), (), (operator,))),
# unaryop
('Invert', ((), (), (unaryop, AST,))),
('Not', ((), (), (unaryop, AST,))),
('UAdd', ((), (), (unaryop, AST,))),
('USub', ((), (), (unaryop, AST,))),
# cmpop
('Eq', ((), (), (cmpop,))),
('NotEq', ((), (), (cmpop,))),
('Lt', ((), (), (cmpop,))),
('LtE', ((), (), (cmpop,))),
('Gt', ((), (), (cmpop,))),
('GtE', ((), (), (cmpop,))),
('Is', ((), (), (cmpop,))),
('IsNot', ((), (), (cmpop,))),
('In', ((), (), (cmpop,))),
('NotIn', ((), (), (cmpop,))),
# comprehension
('comprehension', (('target', 'iter', 'ifs', 'is_async'), (), (AST,))),
# excepthandler
('ExceptHandler', (('type', 'name', 'body'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(excepthandler,))),
# arguments
('arguments', (('args', 'posonlyargs', 'vararg', 'kwonlyargs',
'kw_defaults', 'kwarg', 'defaults'), (), (AST,))),
# keyword
('keyword', (('arg', 'value'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset'),
(AST,))),
# alias
('alias', (('name', 'asname'),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset'),
(AST,))),
# withitem
('withitem', (('context_expr', 'optional_vars'), (), (AST,))),
# match_case
('match_case', (('pattern', 'guard', 'body'), (), (AST,))),
# pattern
('MatchValue', (('value',),
('lineno', 'col_offset', 'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchSingleton', (('value',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchSequence', (('patterns',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchMapping', (('keys', 'patterns', 'rest'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchClass', (('cls', 'patterns', 'kwd_attrs', 'kwd_patterns'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchStar', (('name',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchAs', (('pattern', 'name'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
('MatchOr', (('patterns',),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(pattern,))),
# type_ignore
('type_ignore', ((), ('lineno', 'tag'), (TypeIgnore,))),
# type_param
('TypeVar', (('name', 'bound', 'default_value'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(type_param,))),
('ParamSpec', (('name', 'default_value'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(type_param,))),
('TypeVarTuple', (('name', 'default_value'),
('lineno', 'col_offset',
'end_lineno', 'end_col_offset'),
(type_param,))),
)
for _name, _descr in _nodes:
_make_node(_name, *_descr)
# As an exception to gast rule that states that all nodes are identical for all
# python version, we don't fill the field type for python with a version lower
# than 3.10. Those version lack type support to be compatible with the more
# modern representation anyway. The _field_types still exists though, but it's
# always empty.
if _sys.version_info >= (3, 10):
_node_types = (
# mod
('Module', (list[stmt], list[type_ignore])),
('Interactive', (list[stmt],)),
('Expression', (expr,)),
('FunctionType', ('argtypes', 'returns'),),
('Suite', (list[stmt],),),
# stmt
('FunctionDef', (str, arguments, list[stmt], list[expr], expr | None, str | None, list[type_param]),),
('AsyncFunctionDef', (str, arguments, list[stmt], list[expr], expr | None, str | None, list[type_param]),),
('ClassDef', (str, list[expr], list[keyword], list[stmt], list[expr], list[type_param])),
('Return', (expr | None,)),
('Delete', (list[expr],)),
('Assign', (list[expr], expr, str | None),),
('TypeAlias', (expr, list[type_param], expr),),
('AugAssign', (expr, operator, expr), ),
('AnnAssign', (expr, expr, expr | None, int), ),
('Print', (expr | None, list[expr], bool), ),
('For', (expr, expr, list[stmt], list[stmt], str | None), ),
('AsyncFor', (expr, expr, list[stmt], list[stmt], str | None), ),
('While', (expr, list[stmt], list[stmt]), ),
('If', (expr, list[stmt], list[stmt]), ),
('With', (list[withitem], list[stmt], str | None), ),
('AsyncWith', (list[withitem], list[stmt], str | None), ),
('Match', (expr, match_case), ),
('Raise', (expr | None, expr | None), ),
('Try', (list[stmt], list[excepthandler], list[stmt], list[stmt]), ),
('TryStar', (list[stmt], list[excepthandler], list[stmt], list[stmt]), ),
('Assert', (expr, expr | None), ),
('Import', (list[alias],), ),
('ImportFrom', (str|None, list[alias], int | None), ),
('Exec', (expr, expr | None, expr | None), ),
('Global', (list[str],), ),
('Nonlocal', (list[str],), ),
('Expr', (expr,), ),
# expr
('BoolOp', (boolop, list[expr]), ),
('NamedExpr', (expr, expr), ),
('BinOp', (expr, operator, expr), ),
('UnaryOp', (unaryop, expr), ),
('Lambda', (arguments, expr), ),
('IfExp', (expr, expr, expr), ),
('Dict', (list[expr], list[expr]), ),
('Set', (list[expr],), ),
('ListComp', (expr, list[comprehension]), ),
('SetComp', (expr, list[comprehension]), ),
('DictComp', (expr, expr, list[comprehension]), ),
('GeneratorExp', (expr, list[comprehension]), ),
('Await', (expr,), ),
('Yield', (expr | None,), ),
('YieldFrom', (expr,), ),
('Compare', (expr, list[cmpop], list[expr]), ),
('Call', (expr, list[expr], list[keyword]), ),
('Repr', (expr,), ),
('FormattedValue', (expr, int, expr | None), ),
('Interpolation', (expr, str, int, expr | None), ),
('JoinedStr', (list[expr],), ),
('TemplateStr', (list[expr],), ),
('Constant', (object, str | None), ),
('Attribute', (expr, str, expr_context), ),
('Subscript', (expr, expr, expr_context), ),
('Starred', (expr, expr_context), ),
('Name', (str, expr_context, expr, str | None), ),
('List', (list[expr], expr_context), ),
('Tuple', (list[expr], expr_context), ),
('Slice', (expr | None, expr | None, expr | None), ),
# comprehension
('comprehension', (expr, expr, list[expr], int), ),
# excepthandler
('ExceptHandler', (expr | None, str | None, list[stmt]), ),
# arguments
('arguments', (list[expr], list[expr], expr | None, list[expr], list[expr], expr | None, list[expr]), ),
# keyword
('keyword', (str | None, expr), ),
# alias
('alias', (str, str | None), ),
# withitem
('withitem', (expr, expr | None), ),
# match_case
('match_case', (pattern, expr, list[stmt]), ),
# pattern
('MatchValue', (expr,), ),
('MatchSingleton', (object,), ),
('MatchSequence', (list[pattern],), ),
('MatchMapping', (list[expr], list[pattern], str | None), ),
('MatchClass', (expr, list[pattern], list[str], list[pattern]), ),
('MatchStar', (str | None,), ),
('MatchAs', (pattern | None, str | None), ),
('MatchOr', (list[pattern],), ),
# type_param
('TypeVar', (str, expr | None, expr | None), ),
('ParamSpec', (str, expr | None), ),
('TypeVarTuple', (str, expr | None), ),
)
for _name, _types in _node_types:
_fill_field_types(_name, _types)
from .ast3 import ast_to_gast, gast_to_ast
def parse(*args, **kwargs):
return ast_to_gast(_ast.parse(*args, **kwargs))
def literal_eval(node_or_string):
if isinstance(node_or_string, AST):
node_or_string = gast_to_ast(node_or_string)
return _ast.literal_eval(node_or_string)
def get_docstring(node, clean=True):
if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
if not(node.body and isinstance(node.body[0], Expr)):
return None
node = node.body[0].value
if isinstance(node, Constant) and isinstance(node.value, str):
text = node.value
else:
return None
if clean:
import inspect
text = inspect.cleandoc(text)
return text
# the following are directly imported from python3.8's Lib/ast.py #
def copy_location(new_node, old_node):
"""
Copy source location (`lineno`, `col_offset`, `end_lineno`, and
`end_col_offset` attributes) from *old_node* to *new_node* if possible,
and return *new_node*.
"""
for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
if attr in old_node._attributes and attr in new_node._attributes \
and hasattr(old_node, attr):
setattr(new_node, attr, getattr(old_node, attr))
return new_node
def fix_missing_locations(node):
"""
When you compile a node tree with compile(), the compiler expects lineno
and col_offset attributes for every node that supports them. This is
rather tedious to fill in for generated nodes, so this helper adds these
attributes recursively where not already set, by setting them to the values
of the parent node. It works recursively starting at *node*.
"""
def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
if 'lineno' in node._attributes:
if not hasattr(node, 'lineno'):
node.lineno = lineno
else:
lineno = node.lineno
if 'end_lineno' in node._attributes:
if not hasattr(node, 'end_lineno'):
node.end_lineno = end_lineno
else:
end_lineno = node.end_lineno
if 'col_offset' in node._attributes:
if not hasattr(node, 'col_offset'):
node.col_offset = col_offset
else:
col_offset = node.col_offset
if 'end_col_offset' in node._attributes:
if not hasattr(node, 'end_col_offset'):
node.end_col_offset = end_col_offset
else:
end_col_offset = node.end_col_offset
for child in iter_child_nodes(node):
_fix(child, lineno, col_offset, end_lineno, end_col_offset)
_fix(node, 1, 0, 1, 0)
return node
if _sys.version_info.major == 3 and _sys.version_info.minor >= 8:
get_source_segment = _ast.get_source_segment
else:
# No end_lineno no end_col_offset info set for those version, so always
# return None
def get_source_segment(source, node, padded=False):
return None
def increment_lineno(node, n=1):
"""
Increment the line number and end line number of each node in the tree
starting at *node* by *n*. This is useful to "move code" to a different
location in a file.
"""
for child in walk(node):
if 'lineno' in child._attributes:
child.lineno = (getattr(child, 'lineno', 0) or 0) + n
if 'end_lineno' in child._attributes:
child.end_lineno = (getattr(child, 'end_lineno', 0) or 0) + n
return node
# Code import from Lib/ast.py
#
# minor changes: getattr(x, y, ...) is None => getattr(x, y, 42) is None
#
def dump(
node, annotate_fields=True, include_attributes=False,
# *, # removed for compatibility with python2 :-/
indent=None, show_empty=False,
):
"""
Return a formatted dump of the tree in node. This is mainly useful for
debugging purposes. If annotate_fields is true (by default),
the returned string will show the names and the values for fields.
If annotate_fields is false, the result string will be more compact by
omitting unambiguous field names. Attributes such as line
numbers and column offsets are not dumped by default. If this is wanted,
include_attributes can be set to true. If indent is a non-negative
integer or string, then the tree will be pretty-printed with that indent
level. None (the default) selects the single line representation.
If show_empty is False, then empty lists and fields that are None
will be omitted from the output for better readability.
"""
def _format(node, level=0):
if indent is not None:
level += 1
prefix = '\n' + indent * level
sep = ',\n' + indent * level
else:
prefix = ''
sep = ', '
if isinstance(node, AST):
cls = type(node)
args = []
args_buffer = []
allsimple = True
keywords = annotate_fields
for name in node._fields:
try:
value = getattr(node, name)
except AttributeError:
keywords = True
continue
if value is None and getattr(cls, name, 42) is None:
keywords = True
continue
if not show_empty:
if value == []:
if not keywords:
args_buffer.append(repr(value))
continue
if not keywords:
args.extend(args_buffer)
args_buffer = []
value, simple = _format(value, level)
allsimple = allsimple and simple
if keywords:
args.append('%s=%s' % (name, value))
else:
args.append(value)
if include_attributes and node._attributes:
for name in node._attributes:
try:
value = getattr(node, name)
except AttributeError:
continue
if value is None and getattr(cls, name, 42) is None:
continue
value, simple = _format(value, level)
allsimple = allsimple and simple
args.append('%s=%s' % (name, value))
if allsimple and len(args) <= 3:
return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
elif isinstance(node, list):
if not node:
return '[]', True
return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
return repr(node), True
if not isinstance(node, AST):
raise TypeError('expected AST, got %r' % node.__class__.__name__)
if indent is not None and not isinstance(indent, str):
indent = ' ' * indent
return _format(node)[0]
+291
View File
@@ -0,0 +1,291 @@
# 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 __future__ import annotations
import re
import numpy as np
from paddle.base import core
def _print_tensor_in_gpu(tensor):
"""
Print a GPU tensor's dtype, shape, and all data values directly from
the device using a single-thread CUDA kernel (device-side printf).
This function is **CUDA Graph safe**: no host/device memory transfer
is performed (shape is passed via kernel-argument registers), so it
can be called inside a CUDA Graph capture region.
Args:
tensor (paddle.Tensor): A GPU DenseTensor to print. Must already
reside on a CUDA device (call ``tensor.cuda()`` first if needed).
Raises:
ValueError: If PaddlePaddle is not compiled with CUDA support.
InvalidArgument: If the tensor is not a DenseTensor or not on GPU.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> paddle.device.set_device('gpu')
>>> x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
>>> paddle.utils.gpu_utils._print_tensor_in_gpu(x)
"""
if not core.is_compiled_with_cuda():
raise ValueError(
"paddle.utils._print_tensor_in_gpu is not supported in "
"CPU-only PaddlePaddle. Please reinstall PaddlePaddle with GPU "
"support to call this API."
)
core.eager._print_tensor_in_gpu(tensor)
# Mapping from the dtype name printed by DebugPrintGPUTensor to a numpy dtype.
_DTYPE_STR_TO_NUMPY = {
'FLOAT32': np.float32,
'FLOAT64': np.float64,
'FLOAT16': np.float16,
'BFLOAT16': np.float32, # NumPy has no bfloat16; promote to float32
'INT32': np.int32,
'INT64': np.int64,
'INT16': np.int16,
'INT8': np.int8,
'UINT8': np.uint8,
'BOOL': np.bool_,
}
# Mapping from the dtype name to the paddle dtype string.
_DTYPE_STR_TO_PADDLE = {
'FLOAT32': 'float32',
'FLOAT64': 'float64',
'FLOAT16': 'float16',
'BFLOAT16': 'bfloat16',
'INT32': 'int32',
'INT64': 'int64',
'INT16': 'int16',
'INT8': 'int8',
'UINT8': 'uint8',
'BOOL': 'bool',
}
def _parse_tensor_from_gpu_print(text):
"""
Reconstruct a ``paddle.Tensor`` from the text output produced by
:func:`paddle.utils.gpu_utils._print_tensor_in_gpu`.
The expected input format is the output written to stdout by the
``DebugPrintGPUTensor`` CUDA kernel::
[TensorDebug] dtype : FLOAT32
[TensorDebug] shape : [2, 3]
[TensorDebug] numel : 6
[TensorDebug] data :
[[1, 2, 3],
[4, 5, 6]]
Special cases handled:
* **Scalar (0-D tensor)** the data line is ``[TensorDebug] data : <value>``
(no newline before the value).
* **Empty tensor (numel == 0)** the data line ends with ``[]``.
Args:
text (str): The captured stdout string from a call to
``paddle.utils.gpu_utils._print_tensor_in_gpu``.
Returns:
paddle.Tensor: A GPU tensor with the dtype and values recovered from
*text*. Call ``.cpu()`` on the result if you need a CPU tensor.
Raises:
ValueError: If *text* cannot be parsed (missing header lines, unknown
dtype, mismatched numel, …).
Examples:
.. code-block:: pycon
>>> import paddle
>>> import os, sys, tempfile
>>> # Capture the C-level stdout written by the CUDA printf kernel.
>>> def capture(tensor):
... with tempfile.NamedTemporaryFile(mode='w+', suffix='.txt', delete=False) as f:
... path = f.name
... sys.stdout.flush()
... old = os.dup(1)
... fd = os.open(path, os.O_WRONLY | os.O_TRUNC)
... os.dup2(fd, 1)
... os.close(fd)
... paddle.utils.gpu_utils._print_tensor_in_gpu(tensor)
... paddle.device.synchronize()
... sys.stdout.flush()
... os.dup2(old, 1)
... os.close(old)
... text = open(path).read()
... os.remove(path)
... return text
>>> x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
>>> text = capture(x)
>>> y = paddle.utils.gpu_utils._parse_tensor_from_gpu_print(text)
>>> print(y)
"""
import paddle # local import to avoid circular imports at module load time
# ------------------------------------------------------------------ #
# 1. Extract header fields #
# ------------------------------------------------------------------ #
dtype_m = re.search(r'\[TensorDebug\] dtype\s*:\s*(\w+)', text)
shape_m = re.search(r'\[TensorDebug\] shape\s*:\s*\[([^\]]*)\]', text)
numel_m = re.search(r'\[TensorDebug\] numel\s*:\s*(\d+)', text)
data_m = re.search(r'\[TensorDebug\] data\s*:(.*)', text, re.DOTALL)
if not dtype_m:
raise ValueError(
"_parse_tensor_from_gpu_print: could not find "
"'[TensorDebug] dtype' line in the provided text."
)
if not shape_m:
raise ValueError(
"_parse_tensor_from_gpu_print: could not find "
"'[TensorDebug] shape' line in the provided text."
)
if not numel_m:
raise ValueError(
"_parse_tensor_from_gpu_print: could not find "
"'[TensorDebug] numel' line in the provided text."
)
if not data_m:
raise ValueError(
"_parse_tensor_from_gpu_print: could not find "
"'[TensorDebug] data' line in the provided text."
)
dtype_str = dtype_m.group(1).strip().upper()
shape_raw = shape_m.group(1).strip()
numel = int(numel_m.group(1).strip())
data_raw = data_m.group(1).strip()
if dtype_str not in _DTYPE_STR_TO_NUMPY:
raise ValueError(
f"_parse_tensor_from_gpu_print: unknown dtype '{dtype_str}'. "
f"Supported: {list(_DTYPE_STR_TO_NUMPY.keys())}"
)
np_dtype = _DTYPE_STR_TO_NUMPY[dtype_str]
paddle_dtype = _DTYPE_STR_TO_PADDLE[dtype_str]
# Parse shape: "" means scalar (0-D), otherwise comma-separated ints.
if shape_raw == '':
shape = []
else:
shape = [int(s.strip()) for s in shape_raw.split(',') if s.strip()]
# ------------------------------------------------------------------ #
# 2. Parse data section #
# ------------------------------------------------------------------ #
gpu_place = paddle.CUDAPlace(0)
if numel == 0:
# Empty tensor no data values to parse.
arr = np.empty(shape, dtype=np_dtype)
t = paddle.to_tensor(arr, dtype=paddle_dtype, place=gpu_place)
return t
if len(shape) == 0:
# Scalar (0-D tensor): data_raw is the single value directly.
value_str = data_raw
arr = np.array(_parse_value(value_str, dtype_str), dtype=np_dtype)
t = paddle.to_tensor(arr, dtype=paddle_dtype, place=gpu_place)
return t
# General N-D case: strip all brackets, whitespace, and split by commas.
# The nested bracket notation produced by PrintTensorKernel uses standard
# Python list syntax, so ast.literal_eval can reconstruct the nested list
# directly but only for numeric dtypes. For 'BOOL', PrintValue emits
# "True"/"False" which are valid Python literals too.
flat_values = _extract_flat_values(data_raw, dtype_str, numel)
arr = np.array(flat_values, dtype=np_dtype).reshape(shape)
t = paddle.to_tensor(arr, dtype=paddle_dtype, place=gpu_place)
return t
def _parse_value(value_str, dtype_str):
"""Parse a single scalar value string from the debug output."""
value_str = value_str.strip()
if dtype_str == 'BOOL':
if value_str == 'True':
return True
elif value_str == 'False':
return False
else:
raise ValueError(
f"_parse_tensor_from_gpu_print: cannot parse bool value "
f"'{value_str}'."
)
return float(value_str)
def _extract_flat_values(data_raw, dtype_str, numel):
"""
Extract a flat list of Python scalars from the nested-bracket string
produced by PrintTensorKernel.
Strategy: remove all bracket characters and split the remaining string
on commas / whitespace to get individual token strings, then convert
each token to the appropriate scalar type.
"""
# Remove all '[' and ']' characters.
flat_str = data_raw.replace('[', '').replace(']', '')
# Split on commas (values are separated by ", " or ",\n indent").
tokens = re.split(r'[,\s]+', flat_str)
tokens = [t.strip() for t in tokens if t.strip()]
if len(tokens) != numel:
raise ValueError(
f"_parse_tensor_from_gpu_print: expected {numel} values but "
f"found {len(tokens)} tokens in the data section. "
f"Parsed tokens: {tokens[:20]}{'...' if len(tokens) > 20 else ''}"
)
if dtype_str == 'BOOL':
result = []
for tok in tokens:
if tok == 'True':
result.append(True)
elif tok == 'False':
result.append(False)
else:
raise ValueError(
f"_parse_tensor_from_gpu_print: cannot parse bool token "
f"'{tok}'."
)
return result
# Integer dtypes must preserve exact values.
# GPU printf may emit scientific notation (e.g. "1e+06"), so use
# Decimal for lossless string-to-integer conversion.
from decimal import Decimal
_INT_DTYPES = {'INT8', 'INT16', 'INT32', 'INT64', 'UINT8'}
if dtype_str in _INT_DTYPES:
return [int(Decimal(t)) for t in tokens]
return [float(t) for t in tokens]
+235
View File
@@ -0,0 +1,235 @@
# Copyright (c) 2016 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 io import StringIO
import numpy as np
from PIL import Image
__all__ = []
def resize_image(img, target_size):
"""
Resize an image so that the shorter edge has length target_size.
img: the input image to be resized.
target_size: the target resized image size.
"""
percent = target_size / float(min(img.size[0], img.size[1]))
resized_size = (
int(round(img.size[0] * percent)),
int(round(img.size[1] * percent)),
)
img = img.resize(resized_size, Image.ANTIALIAS)
return img
def flip(im):
"""
Return the flipped image.
Flip an image along the horizontal direction.
im: input image, (K x H x W) ndarrays
"""
if len(im.shape) == 3:
return im[:, :, ::-1]
else:
return im[:, ::-1]
def crop_img(im, inner_size, color=True, test=True):
"""
Return cropped image.
The size of the cropped image is inner_size * inner_size.
im: (K x H x W) ndarrays
inner_size: the cropped image size.
color: whether it is color image.
test: whether in test mode.
If False, does random cropping and flipping.
If True, crop the center of images.
"""
if color:
height, width = (
max(inner_size, im.shape[1]),
max(inner_size, im.shape[2]),
)
padded_im = np.zeros((3, height, width))
startY = (height - im.shape[1]) / 2
startX = (width - im.shape[2]) / 2
endY, endX = startY + im.shape[1], startX + im.shape[2]
padded_im[:, startY:endY, startX:endX] = im
else:
im = im.astype('float32')
height, width = (
max(inner_size, im.shape[0]),
max(inner_size, im.shape[1]),
)
padded_im = np.zeros((height, width))
startY = (height - im.shape[0]) / 2
startX = (width - im.shape[1]) / 2
endY, endX = startY + im.shape[0], startX + im.shape[1]
padded_im[startY:endY, startX:endX] = im
if test:
startY = (height - inner_size) / 2
startX = (width - inner_size) / 2
else:
startY = np.random.randint(0, height - inner_size + 1)
startX = np.random.randint(0, width - inner_size + 1)
endY, endX = startY + inner_size, startX + inner_size
if color:
pic = padded_im[:, startY:endY, startX:endX]
else:
pic = padded_im[startY:endY, startX:endX]
if (not test) and (np.random.randint(2) == 0):
pic = flip(pic)
return pic
def decode_jpeg(jpeg_string):
np_array = np.array(Image.open(StringIO(jpeg_string)))
if len(np_array.shape) == 3:
np_array = np.transpose(np_array, (2, 0, 1))
return np_array
def preprocess_img(im, img_mean, crop_size, is_train, color=True):
"""
Does data augmentation for images.
If is_train is false, cropping the center region from the image.
If is_train is true, randomly crop a region from the image,
and random does flipping.
im: (K x H x W) ndarrays
"""
im = im.astype('float32')
test = not is_train
pic = crop_img(im, crop_size, color, test)
pic -= img_mean
return pic.flatten()
def load_meta(meta_path, mean_img_size, crop_size, color=True):
"""
Return the loaded meta file.
Load the meta image, which is the mean of the images in the dataset.
The mean image is subtracted from every input image so that the expected mean
of each input image is zero.
"""
mean = np.load(meta_path)['data_mean']
border = (mean_img_size - crop_size) / 2
if color:
assert mean_img_size * mean_img_size * 3 == mean.shape[0]
mean = mean.reshape(3, mean_img_size, mean_img_size)
mean = mean[
:, border : border + crop_size, border : border + crop_size
].astype('float32')
else:
assert mean_img_size * mean_img_size == mean.shape[0]
mean = mean.reshape(mean_img_size, mean_img_size)
mean = mean[
border : border + crop_size, border : border + crop_size
].astype('float32')
return mean
def load_image(img_path, is_color=True):
"""
Load image and return.
img_path: image path.
is_color: is color image or not.
"""
img = Image.open(img_path)
img.load()
return img
def oversample(img, crop_dims):
"""
image : iterable of (H x W x K) ndarrays
crop_dims: (height, width) tuple for the crops.
Returned data contains ten crops of input image, namely,
four corner patches and the center patch as well as their
horizontal reflections.
"""
# Dimensions and center.
im_shape = np.array(img[0].shape)
crop_dims = np.array(crop_dims)
im_center = im_shape[:2] / 2.0
# Make crop coordinates
h_indices = (0, im_shape[0] - crop_dims[0])
w_indices = (0, im_shape[1] - crop_dims[1])
crops_ix = np.empty((5, 4), dtype=int)
curr = 0
for i in h_indices:
for j in w_indices:
crops_ix[curr] = (i, j, i + crop_dims[0], j + crop_dims[1])
curr += 1
crops_ix[4] = np.tile(im_center, (1, 2)) + np.concatenate(
[-crop_dims / 2.0, crop_dims / 2.0]
)
crops_ix = np.tile(crops_ix, (2, 1))
# Extract crops
crops = np.empty(
(10 * len(img), crop_dims[0], crop_dims[1], im_shape[-1]),
dtype=np.float32,
)
ix = 0
for im in img:
for crop in crops_ix:
crops[ix] = im[crop[0] : crop[2], crop[1] : crop[3], :]
ix += 1
crops[ix - 5 : ix] = crops[ix - 5 : ix, :, ::-1, :] # flip for mirrors
return crops
class ImageTransformer:
def __init__(
self, transpose=None, channel_swap=None, mean=None, is_color=True
):
self.is_color = is_color
self.set_transpose(transpose)
self.set_channel_swap(channel_swap)
self.set_mean(mean)
def set_transpose(self, order):
if order is not None:
if self.is_color:
assert 3 == len(order)
self.transpose = order
def set_channel_swap(self, order):
if order is not None:
if self.is_color:
assert 3 == len(order)
self.channel_swap = order
def set_mean(self, mean):
if mean is not None:
# mean value, may be one value per channel
if mean.ndim == 1:
mean = mean[:, np.newaxis, np.newaxis]
else:
# elementwise mean
if self.is_color:
assert len(mean.shape) == 3
self.mean = mean
def transformer(self, data):
if self.transpose is not None:
data = data.transpose(self.transpose)
if self.channel_swap is not None:
data = data[self.channel_swap, :, :]
if self.mean is not None:
data -= self.mean
return data
+94
View File
@@ -0,0 +1,94 @@
# 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
import warnings
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import ParamSpec
import paddle # noqa: F401
from paddle.base.framework import stride_ops
from paddle.base.wrapped_decorator import wrap_decorator
from paddle.framework import in_dynamic_mode, in_pir_mode
_InputT = ParamSpec("_InputT")
_RetT = TypeVar("_RetT")
if TYPE_CHECKING:
from collections.abc import Callable
from paddle.pir import Value
def check_view_value(value: Value) -> bool:
# check if the value is a view tensor
if value.get_defining_op().name() in stride_ops:
# TODO(ooooo-create): The `x = stride_op(x)` shouldn't return True.
return True
all_used_ops = value.all_used_ops()
if len(all_used_ops) == 0:
return False
for op in all_used_ops:
if op.name() in stride_ops and op.operand_source(0).is_same(value):
# TODO(ooooo-create): The `y = stride_op(x).clone()` and `y = stride_op(x) + op` should also return False.
# Now is True.
return True
return False
# NOTE(pangyoki): The Inplace APIs with underline(`_`) is only valid for the method of calling `_C_ops`
# in dygraph mode. If static graph mode is used, the inplace mechanism will not be used, and the static method
# of the original API will be called.
# NOTE(GGBond8488): Simply run the original version of the API under the static graph mode has a low
# probability that the result is inconsistent with the dynamic graph.
def _inplace_apis_in_dygraph_only_(
func: Callable[_InputT, _RetT],
) -> Callable[_InputT, _RetT]:
def __impl__(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT:
if not in_dynamic_mode():
origin_api_name = func.__name__[:-1]
warnings.warn(
f"In static graph mode, {func.__name__}() is the same as {origin_api_name}() and does not perform inplace operation."
)
from ..base.dygraph.base import in_to_static_mode
if in_to_static_mode():
stride_in_no_check_dy2st_diff = os.environ.get(
"stride_in_no_check_dy2st_diff", "0"
)
if in_pir_mode():
if (
stride_in_no_check_dy2st_diff != '1'
and check_view_value(args[0])
):
raise ValueError(
f'Sorry about what\'s happened. In to_static mode, {func.__name__}\'s output variable is a viewed Tensor in dygraph. This will result in inconsistent calculation behavior between dynamic and static graphs. You must find the location of the strided API be called, and call paddle.assign() before inplace input.'
)
else:
for arg in args:
if hasattr(arg, "is_view_var") and arg.is_view_var:
raise ValueError(
f'Sorry about what\'s happened. In to_static mode, {func.__name__}\'s output variable {arg.name} is a viewed Tensor in dygraph. This will result in inconsistent calculation behavior between dynamic and static graphs. You must find the location of the strided API be called, and call {arg.name} = paddle.assign({arg.name}).'
)
origin_func = f"{func.__module__}.{origin_api_name}"
return eval(origin_func)(*args, **kwargs)
return func(*args, **kwargs)
return __impl__
inplace_apis_in_dygraph_only = wrap_decorator(_inplace_apis_in_dygraph_only_)
+302
View File
@@ -0,0 +1,302 @@
# 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
import logging
import numpy as np
import paddle
__all__ = []
def _simple_network():
"""
Define a simple network composed by a single linear layer.
"""
input = paddle.static.data(
name="input", shape=[None, 2, 2], dtype="float32"
)
weight = paddle.create_parameter(
shape=[2, 3],
dtype="float32",
attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Constant(0.1)),
)
bias = paddle.create_parameter(shape=[3], dtype="float32")
linear_out = paddle.nn.functional.linear(x=input, weight=weight, bias=bias)
out = paddle.tensor.sum(linear_out)
return input, out, weight
def _prepare_data():
"""
Prepare feeding data for simple network. The shape is [1, 2, 2].
"""
# Prepare the feeding data.
np_input_single = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32)
return np_input_single.reshape(1, 2, 2)
def _is_cuda_available():
"""
Check whether CUDA is available.
"""
try:
assert len(paddle.static.cuda_places()) > 0
return True
except Exception as e:
logging.warning(
"You are using GPU version PaddlePaddle, but there is no GPU "
"detected on your machine. Maybe CUDA devices is not set properly."
f"\n Original Error is {e}"
)
return False
def _is_xpu_available():
"""
Check whether XPU is available.
"""
try:
assert len(paddle.static.xpu_places()) > 0
return True
except Exception as e:
logging.warning(
"You are using XPU version PaddlePaddle, but there is no XPU "
"detected on your machine. Maybe XPU devices is not set properly."
f"\n Original Error is {e}"
)
return False
def _run_dygraph_single(use_cuda, use_xpu, use_custom, custom_device_name):
"""
Testing the simple network in dygraph mode using one CPU/GPU/XPU.
Args:
use_cuda (bool): Whether running with CUDA.
use_xpu (bool): Whether running with XPU.
"""
paddle.disable_static()
if use_cuda:
paddle.set_device('gpu')
elif use_xpu:
paddle.set_device('xpu')
elif use_custom:
paddle.set_device(custom_device_name)
else:
paddle.set_device('cpu')
weight_attr = paddle.ParamAttr(
name="weight", initializer=paddle.nn.initializer.Constant(value=0.5)
)
bias_attr = paddle.ParamAttr(
name="bias", initializer=paddle.nn.initializer.Constant(value=1.0)
)
linear = paddle.nn.Linear(
2, 4, weight_attr=weight_attr, bias_attr=bias_attr
)
input_np = _prepare_data()
input_tensor = paddle.to_tensor(input_np)
linear_out = linear(input_tensor)
out = paddle.tensor.sum(linear_out)
out.backward()
opt = paddle.optimizer.Adam(
learning_rate=0.001, parameters=linear.parameters()
)
opt.step()
def _run_static_single(use_cuda, use_xpu, use_custom, custom_device_name):
"""
Testing the simple network with executor running directly, using one CPU/GPU/XPU.
Args:
use_cuda (bool): Whether running with CUDA.
use_xpu (bool): Whether running with XPU.
"""
paddle.enable_static()
with paddle.static.scope_guard(paddle.static.Scope()):
train_prog = paddle.static.Program()
startup_prog = paddle.static.Program()
startup_prog.random_seed = 1
with paddle.static.program_guard(train_prog, startup_prog):
input, out, weight = _simple_network()
param_grads = paddle.static.append_backward(
out, parameter_list=[weight]
)[0]
if use_cuda:
place = paddle.CUDAPlace(0)
elif use_xpu:
place = paddle.XPUPlace(0)
elif use_custom:
place = paddle.CustomPlace(custom_device_name, 0)
else:
place = paddle.CPUPlace()
exe = paddle.static.Executor(place)
exe.run(startup_prog)
exe.run(
train_prog,
feed={input.name: _prepare_data()},
fetch_list=[out, param_grads[1]],
)
paddle.disable_static()
def train_for_run_parallel():
"""
train script for parallel training check
"""
# to avoid cyclic import
class LinearNet(paddle.nn.Layer):
"""
simple fc network for parallel training check
"""
def __init__(self):
super().__init__()
self._linear1 = paddle.nn.Linear(10, 10)
self._linear2 = paddle.nn.Linear(10, 1)
def forward(self, x):
"""
forward
"""
return self._linear2(self._linear1(x))
paddle.distributed.init_parallel_env()
layer = LinearNet()
dp_layer = paddle.DataParallel(layer)
loss_fn = paddle.nn.MSELoss()
adam = paddle.optimizer.Adam(
learning_rate=0.001, parameters=dp_layer.parameters()
)
inputs = paddle.randn([10, 10], 'float32')
outputs = dp_layer(inputs)
labels = paddle.randn([10, 1], 'float32')
loss = loss_fn(outputs, labels)
loss.backward()
adam.step()
adam.clear_grad()
def _run_parallel(device_list):
"""
Testing the simple network in data parallel mode, using multiple CPU/GPU.
Args:
use_cuda (bool): Whether running with CUDA.
use_xpu (bool): Whether running with XPU.
device_list (int): The specified devices.
"""
paddle.distributed.spawn(train_for_run_parallel, nprocs=len(device_list))
def run_check() -> None:
"""
Check whether PaddlePaddle is installed correctly and running successfully
on your system.
Examples:
.. code-block:: pycon
>>> import paddle
>>> # doctest: +SKIP('the output will change in different run')
>>> paddle.utils.run_check()
Running verify PaddlePaddle program ...
I0818 15:35:08.335391 30540 program_interpreter.cc:173] New Executor is Running.
I0818 15:35:08.398319 30540 interpreter_util.cc:529] Standalone Executor is Used.
PaddlePaddle works well on 1 CPU.
PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.
"""
print("Running verify PaddlePaddle program ... ")
use_cuda = False
use_xpu = False
use_custom = False
custom_device_name = None
if paddle.is_compiled_with_cuda():
use_cuda = _is_cuda_available()
elif paddle.is_compiled_with_xpu():
use_xpu = _is_xpu_available()
elif len(paddle.framework.core.get_all_custom_device_type()) > 0:
use_custom = True
if len(paddle.framework.core.get_all_custom_device_type()) > 1:
logging.warning(
f"More than one kind of custom devices detected, but run check would only be executed on {paddle.framework.core.get_all_custom_device_type()[0]}."
)
if use_cuda:
device_str = "GPU"
device_list = paddle.static.cuda_places()
elif use_xpu:
device_str = "XPU"
device_list = paddle.static.xpu_places()
elif use_custom:
device_str = paddle.framework.core.get_all_custom_device_type()[0]
custom_device_name = device_str
device_list = list(
range(
paddle.framework.core.get_custom_device_count(
custom_device_name
)
)
)
else:
device_str = "CPU"
device_list = paddle.static.cpu_places(device_count=1)
device_count = len(device_list)
_run_static_single(use_cuda, use_xpu, use_custom, custom_device_name)
_run_dygraph_single(use_cuda, use_xpu, use_custom, custom_device_name)
print(f"PaddlePaddle works well on 1 {device_str}.")
try:
if len(device_list) > 1:
if use_custom:
import os
os.environ['PADDLE_DISTRI_BACKEND'] = "xccl"
_run_parallel(device_list)
print(f"PaddlePaddle works well on {device_count} {device_str}s.")
print(
"PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now."
)
except Exception as e:
logging.warning(
f"PaddlePaddle meets some problem with {device_count} {device_str}s. This may be caused by:"
"\n 1. There is not enough GPUs visible on your system"
"\n 2. Some GPUs are occupied by other process now"
"\n 3. NVIDIA-NCCL2 is not installed correctly on your system. Please follow instruction on https://github.com/NVIDIA/nccl-tests "
"\n to test your NCCL, or reinstall it following https://docs.nvidia.com/deeplearning/sdk/nccl-install-guide/index.html"
)
logging.warning(f"\n Original Error is: {e}")
print(
f"PaddlePaddle is installed successfully ONLY for single {device_str}! "
"Let's start deep learning with PaddlePaddle now."
)
raise e
+607
View File
@@ -0,0 +1,607 @@
# 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 __future__ import annotations
import copy
import typing
from collections import defaultdict
from collections.abc import Callable, Sequence
from typing import Any, TypeGuard, TypeVar
from uuid import uuid4
from weakref import WeakKeyDictionary
import numpy as np
import paddle
from paddle.pir.core import convert_nptype_to_datatype
from ..base.data_feeder import check_dtype, convert_dtype
from ..base.framework import (
Block,
Variable,
in_dygraph_mode,
)
from ..pir import Value
if typing.TYPE_CHECKING:
from paddle._typing import NestedStructure, ShapeLike
_T = TypeVar("_T")
_U = TypeVar("_U")
class NotSupportedTensorArgumentError(TypeError):
def __init__(self, msg, name: str):
super().__init__(msg)
self.name = name
def convert_to_list(value, n, name, dtype=int):
"""
Converts a single numerical type or iterable of numerical
types into a numerical type list.
Arguments:
value: The value to validate and convert. Could an int, or any iterable
of ints.
n: The size of the list to be returned.
name: The name of the argument being validated, e.g. "stride" or
"filter_size". This is only used to format error messages.
dtype: the numerical type of the element of the list to be returned.
Returns:
A list of n dtypes.
Raises:
ValueError: If something else than an int/long or iterable thereof was
passed.
"""
if isinstance(value, dtype):
return [value] * n
else:
if isinstance(value, (Variable, paddle.pir.Value)):
raise NotSupportedTensorArgumentError(
f"`{name}` required numerical type with `{dtype}`, but received Tensor.",
name,
)
try:
value_list = list(value)
except TypeError:
raise ValueError(
f"The {name}'s type must be list or tuple. Received: {value}"
)
if len(value_list) != n:
raise ValueError(
f"The {name}'s length must be {n}. Received: {value}"
)
for single_value in value_list:
if isinstance(single_value, (Variable, paddle.pir.Value)):
raise NotSupportedTensorArgumentError(
f"`{name}` required numerical type with `{dtype}`, but received Tensor.",
name,
)
try:
dtype(single_value)
except (ValueError, TypeError):
raise ValueError(
f"The {name}'s type must be a list or tuple of {n} {dtype}. "
+ f"Received: {value} including element {single_value} of type {type(single_value)}"
)
return value_list
def is_sequence(seq: Any) -> TypeGuard[typing.Sequence[Any] | dict[str, Any]]:
"""
Whether `seq` is an entry or nested structure
"""
if isinstance(seq, dict):
return True
return isinstance(seq, Sequence) and not isinstance(seq, str)
class UniqueIdMap(WeakKeyDictionary):
def __init__(self):
super().__init__(self)
self.data = defaultdict(uuid4)
uniqueidmap = UniqueIdMap()
def uniqueid(obj):
if isinstance(obj, str):
return (hash(obj),)
elif isinstance(obj, list):
return (id(obj),)
else:
return (uniqueidmap[obj].int,)
def _hash_with_id(*args):
"""
Return int hash value calculated by id(arg) or tuple(id1,id2, ...).
"""
assert len(args) > 0
info = ()
for v in args:
info = info + uniqueid(v)
return hash(info)
def _sorted(dict_):
"""
Returns a sorted list of the dict keys, with error if keys not sortable.
"""
try:
return sorted(dict_.keys())
except TypeError:
raise TypeError("nest only supports dicts with sortable keys.")
def _yield_value(iterable):
if isinstance(iterable, dict):
for key in _sorted(iterable):
yield iterable[key]
else:
yield from iterable
def _yield_flat_nest(nest):
for n in _yield_value(nest):
if is_sequence(n):
yield from _yield_flat_nest(n)
else:
yield n
def to_sequence(nest):
if is_sequence(nest):
return nest
else:
return [nest]
def flatten(nest: NestedStructure[_T]) -> typing.Sequence[_T]:
"""
:alias_main: paddle.flatten
:alias: paddle.flatten,paddle.tensor.flatten,paddle.tensor.manipulation.flatten
:old_api: paddle.base.layers.flatten
Traverse all entries in the nested structure and put them into an list.
"""
if is_sequence(nest):
return list(_yield_flat_nest(nest))
else:
return [nest]
def _sequence_like(instance, args):
"""
Convert the sequence `args` to the same type as `instance`.
"""
if isinstance(instance, dict):
result = dict(zip(_sorted(instance), args))
return type(instance)((key, result[key]) for key in instance.keys())
elif (
isinstance(instance, tuple)
and hasattr(instance, "_fields")
and isinstance(instance._fields, Sequence)
and all(isinstance(f, str) for f in instance._fields)
):
# This is a namedtuple
return type(instance)(*args)
else:
# Not a namedtuple
return type(instance)(args)
def _packed_nest_with_indices(structure, flat, index):
"""
Helper function for pack_sequence_as.
"""
packed = []
for s in _yield_value(structure):
if is_sequence(s):
new_index, child = _packed_nest_with_indices(s, flat, index)
packed.append(_sequence_like(s, child))
index = new_index
else:
# Paddle requires python version > 3.7, so dict is always OrderedDict
packed.append(
flat[index]
if not isinstance(flat, dict)
else list(flat.values())[index]
)
index += 1
return index, packed
def pack_sequence_as(structure, flat_sequence):
"""
Pack a given flattened sequence into a given structure.
"""
if not is_sequence(flat_sequence):
raise TypeError("flat_sequence must be a sequence")
if not is_sequence(structure):
if len(flat_sequence) != 1:
raise ValueError(
f"Structure is a scalar but len(flat_sequence) == {len(flat_sequence)} > 1"
)
return flat_sequence[0]
flat_structure = flatten(structure)
if len(flat_structure) != len(flat_sequence):
raise ValueError(
f"Could not pack sequence. Structure had {len(flat_structure)} elements, but flat_sequence "
f"had {len(flat_sequence)} elements. Structure: {structure}, flat_sequence: {flat_sequence}."
)
_, packed = _packed_nest_with_indices(structure, flat_sequence, 0)
return _sequence_like(structure, packed)
def map_structure(
func: Callable[[_T], _U], *structure: NestedStructure[_T]
) -> NestedStructure[_U]:
"""
Apply `func` to each entry in `structure` and return a new structure.
"""
flat_structure = [flatten(s) for s in structure]
entries = zip(*flat_structure)
return pack_sequence_as(structure[0], [func(*x) for x in entries])
def hold_mutable_vars(structure):
"""
Returns whether structure holds sequence like `list/dict`.
"""
for s in structure:
if is_sequence(s):
return True
return False
def copy_mutable_vars(structure):
"""
Returns vars copied from sequence without mutable property.
"""
flat_structure = copy.copy(flatten(structure))
return pack_sequence_as(structure, flat_structure)
def _recursive_assert_same_structure(nest1, nest2, check_types, skip_if):
"""
Helper function for `assert_same_structure`.
"""
if skip_if is not None and (skip_if(nest1) or skip_if(nest2)):
return
is_sequence_nest1 = is_sequence(nest1)
if is_sequence_nest1 != is_sequence(nest2):
raise ValueError(
"The two structures don't have the same nested structure.\n\n"
f"First structure: {nest1}\n\nSecond structure: {nest2}."
)
if not is_sequence_nest1:
return # finished checking
if check_types:
type_nest1 = type(nest1)
type_nest2 = type(nest2)
if type_nest1 != type_nest2:
raise TypeError(
"The two structures don't have the same sequence type. First "
f"structure has type {type_nest1}, while second structure has type {type_nest2}."
)
if isinstance(nest1, dict):
keys1 = set(nest1.keys())
keys2 = set(nest2.keys())
if keys1 != keys2:
raise ValueError(
"The two dictionaries don't have the same set of keys. First "
f"structure has keys {keys1}, while second structure has keys {keys2}."
)
nest1_as_sequence = list(_yield_value(nest1))
nest2_as_sequence = list(_yield_value(nest2))
if len(nest1_as_sequence) != len(nest2_as_sequence):
raise ValueError(
"The two structures don't have the same number of elements.\n\n"
f"First structure ({len(nest1_as_sequence)} elements): {nest1}\n\n"
f"Second structure ({len(nest2_as_sequence)} elements): {nest2}"
)
for n1, n2 in zip(nest1_as_sequence, nest2_as_sequence):
_recursive_assert_same_structure(n1, n2, check_types, skip_if)
def padding_to_same_structure(nest1, nest2, obj=None):
def _padding_to_same_structure_single(value, obj):
def change_none_to_obj(x):
if x is None:
return obj
return x
if is_sequence(value):
value = pack_sequence_as(
value, [change_none_to_obj(item) for item in flatten(value)]
)
else:
value = change_none_to_obj(value)
return value
nest1 = _padding_to_same_structure_single(nest1, obj)
nest2 = _padding_to_same_structure_single(nest2, obj)
return nest1, nest2
def assert_same_structure(nest1, nest2, check_types=True, skip_if=None):
"""
Confirm two nested structures with the same structure.
"""
if skip_if is not None and (skip_if(nest1) or skip_if(nest2)):
return
len_nest1 = len(flatten(nest1)) if is_sequence(nest1) else 1
len_nest2 = len(flatten(nest2)) if is_sequence(nest2) else 1
if len_nest1 != len_nest2 and skip_if is None:
raise ValueError(
"The two structures don't have the same number of "
f"elements.\n\nFirst structure ({len_nest1} elements): {nest1}\n\n"
f"Second structure ({len_nest2} elements): {nest2}"
)
_recursive_assert_same_structure(nest1, nest2, check_types, skip_if)
def _is_symmetric_padding(padding, data_dim):
"""
Check whether padding is symmetrical.
"""
assert len(padding) == data_dim * 2 or len(padding) == data_dim
is_sys = True
if len(padding) == data_dim * 2:
for i in range(data_dim):
if padding[i * 2] != padding[i * 2 + 1]:
is_sys = False
return is_sys
def _contain_var(list_or_tuple):
"""
Check whether list or tuple contains variable / Value.
"""
for item in list_or_tuple:
if isinstance(item, (Variable, paddle.pir.Value)):
return True
return False
def get_int_tensor_list(ele_list, default_dtype='int64'):
int_tensor_list = []
for ele in ele_list:
if isinstance(ele, paddle.pir.Value):
ele.stop_gradient = True
if convert_dtype(ele.dtype) != default_dtype:
ele = paddle.cast(x=ele, dtype=default_dtype)
if ele.shape != []:
ele = paddle.reshape(ele, [])
int_tensor_list.append(ele)
else:
temp_out = paddle.tensor.fill_constant(
shape=[],
dtype=convert_nptype_to_datatype(np.dtype(default_dtype)),
value=ele,
force_cpu=True,
)
int_tensor_list.append(temp_out)
return int_tensor_list
def get_shape_tensor_inputs(inputs, attrs, shape, op_type):
from paddle.tensor import fill_constant
def _get_attr_shape(list_shape):
attr_shape = []
for idx, dim in enumerate(list_shape):
if isinstance(dim, Variable):
attr_shape.append(-1)
else:
attr_shape.append(dim)
return attr_shape
def _get_shape_tensor(list_shape):
shape_tensor_list = []
for idx, dim in enumerate(list_shape):
if isinstance(dim, Variable):
dim.stop_gradient = True
check_dtype(
dim.dtype,
'shape[' + str(idx) + ']',
['int32', 'int64'],
op_type,
f'(When type of shape in {op_type} is list or tuple.)',
)
if convert_dtype(dim.dtype) == 'int64':
dim = paddle.cast(x=dim, dtype='int32')
shape_tensor_list.append(dim)
else:
temp_out = fill_constant([], 'int32', dim, force_cpu=True)
shape_tensor_list.append(temp_out)
return shape_tensor_list
if isinstance(shape, Variable):
shape.stop_gradient = True
check_dtype(
shape.dtype,
'shape',
['int32', 'int64'],
'fill_constant',
f'(When type of shape in {op_type} is Variable.)',
)
if convert_dtype(shape.dtype) == 'int64':
shape = paddle.cast(shape, 'int32')
inputs["ShapeTensor"] = shape
elif isinstance(shape, (list, tuple)):
attrs["shape"] = _get_attr_shape(shape)
if _contain_var(shape):
inputs['ShapeTensorList'] = _get_shape_tensor(shape)
else:
raise TypeError("Shape only supports Variable, or list, or tuple.")
def _convert_to_tensor_list(old_list, dtype="int32"):
"""
Converts all elements of a list to Variable / Value.
"""
from paddle.tensor import fill_constant
if _contain_var(old_list):
for ele in old_list:
if isinstance(ele, paddle.pir.Value):
dtype = ele.dtype
new_list_tensor = []
for ele in old_list:
if isinstance(ele, (Variable, paddle.pir.Value)):
ele.stop_gradient = True
new_list_tensor.append(ele)
else:
assert isinstance(ele, int)
temp_out = fill_constant([1], dtype, ele, force_cpu=True)
new_list_tensor.append(temp_out)
return new_list_tensor
def convert_shape_to_list(shape):
"""
Convert shape(list, tuple, variable) to list in imperative mode
"""
if isinstance(shape, (list, tuple)):
shape_out = []
for x in shape:
if isinstance(x, Variable):
# skip item if size = 0
if x.size > 0:
shape_out.append(x.item(0))
else:
shape_out.append(x)
shape = shape_out
else:
if in_dygraph_mode():
shape = shape.astype(int).tolist()
return shape
def check_shape(shape):
"""
Check shape type and shape elements type before passing it to fill_constant
"""
if isinstance(shape, (Variable, Value)):
check_dtype(shape.dtype, 'shape', ['int32', 'int64'], 'fill_constant')
elif isinstance(shape, (list, tuple)):
for ele in shape:
if not isinstance(ele, (Variable, Value)):
if ele < 0:
raise ValueError(
"All elements in ``shape`` must be positive when it's a list or tuple"
)
if not isinstance(ele, int):
raise TypeError(
"All elements in ``shape`` must be integers when it's a list or tuple"
)
else:
check_dtype(
ele.dtype,
'element of shape',
['int32', 'int64'],
'fill_constant',
)
def try_get_constant_shape_from_tensor(shape_tensor):
"""Try to get shape from a tensor with constant value.
For example,
import paddle
paddle.enable_static()
data = paddle.static.data(name="x", shape=[-1, 2], dtype='float32')
shape = paddle.shape(data) # shape should be [-1, 2] instead of [-1, -1]
x = paddle.uniform(shape)
print(x.shape)
# (-1, 2)
"""
if not in_dygraph_mode():
try:
if shape_tensor.op is not None:
generate_op = shape_tensor.op
if generate_op.type == 'shape':
var = shape_tensor.block.vars[
generate_op.input_arg_names[0]
]
return var.shape
except:
return None
return None
def get_inputs_outputs_in_block(block):
"""
Returns the inputs and outputs variable used in this block but not
created in this block.
"""
assert isinstance(block, Block), (
"input non-Block argument for get_inputs_outputs_in_block."
)
assert block.parent_idx != -1, (
"input block should be a sub-block, not main block."
)
# Find input/output var names of all ops in block
inner_inputs = set()
inner_outputs = set()
for op in block.ops:
for iname in op.input_names:
for in_var_name in op.input(iname):
if not block.has_var(in_var_name):
# variable not created in this block
inner_inputs.add(in_var_name)
for oname in op.output_names:
for out_var_name in op.output(oname):
if not block.has_var(out_var_name):
# variable not created in this block
inner_outputs.add(out_var_name)
return inner_inputs, inner_outputs
def is_same_shape(shape1: ShapeLike, shape2: ShapeLike) -> bool:
"""
Check whether two shapes are the same. Deal with the dynamic shape.
"""
if paddle.in_dynamic_mode():
return shape1 == shape2
def is_tensor(x):
return isinstance(x, (paddle.static.Variable, paddle.pir.Value))
def is_dynamic_axis(axis):
return is_tensor(axis) or axis == -1
if is_tensor(shape1) or is_tensor(shape2):
return True
if len(shape1) != len(shape2):
return False
for s1, s2 in zip(shape1, shape2):
if is_dynamic_axis(s1) or is_dynamic_axis(s2):
continue
if s1 != s2:
return False
return True
+48
View File
@@ -0,0 +1,48 @@
# 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.
"""Lazy imports for heavy dependencies."""
from __future__ import annotations
import importlib
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from types import ModuleType
__all__ = []
def try_import(module_name: str, err_msg: str | None = None) -> ModuleType:
"""Try importing a module, with an informative error message on failure."""
install_name = module_name
if module_name.find('.') > -1:
install_name = module_name.split('.')[0]
if module_name == 'cv2':
install_name = 'opencv-python'
try:
mod = importlib.import_module(module_name)
return mod
except ImportError:
if err_msg is None:
err_msg = (
f"Failed importing {module_name}. This likely means that some paddle modules "
"require additional dependencies that have to be "
f"manually installed (usually with `pip install {install_name}`). "
)
raise ImportError(err_msg)
+71
View File
@@ -0,0 +1,71 @@
# 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 ..base import core
__all__ = []
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
class OpUpdateInfoHelper:
def __init__(self, info):
self._info = info
def verify_key_value(self, name=''):
result = False
key_funcs = {
core.OpAttrInfo: 'name',
core.OpInputOutputInfo: 'name',
}
if name == '':
result = True
elif type(self._info) in key_funcs:
if getattr(self._info, key_funcs[type(self._info)])() == name:
result = True
return result
@Singleton
class OpLastCheckpointChecker:
def __init__(self):
self.raw_version_map = core.get_op_version_map()
self.checkpoints_map = {}
self._construct_map()
def _construct_map(self):
for op_name in self.raw_version_map:
last_checkpoint = self.raw_version_map[op_name].checkpoints()[-1]
infos = last_checkpoint.version_desc().infos()
self.checkpoints_map[op_name] = infos
def filter_updates(self, op_name, type=core.OpUpdateType.kInvalid, key=''):
updates = []
if op_name in self.checkpoints_map:
for update in self.checkpoints_map[op_name]:
if (update.type() == type) or (
type == core.OpUpdateType.kInvalid
):
if OpUpdateInfoHelper(update.info()).verify_key_value(key):
updates.append(update.info())
return updates
+22
View File
@@ -0,0 +1,22 @@
# 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.unique_name import (
generate,
generate_with_ignorable_key, # noqa: F401
guard,
switch,
)
__all__ = ['generate', 'switch', 'guard']