diff --git a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.cc b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.cc index 6142138eead108..3209cb8a877b32 100644 --- a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.cc +++ b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.cc @@ -163,6 +163,7 @@ OP_SAME_OPERANDS_AND_RESULT(Selu_) OP_SAME_OPERANDS_AND_RESULT(ShadowFeed) OP_SAME_OPERANDS_AND_RESULT(ShareData_) OP_SAME_OPERANDS_AND_RESULT(Sign) +OP_SAME_OPERANDS_AND_RESULT(Sign_) OP_SAME_OPERANDS_AND_RESULT(Sin) OP_SAME_OPERANDS_AND_RESULT(Sin_) OP_SAME_OPERANDS_AND_RESULT(Sinh) diff --git a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.h b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.h index f92ce2f5ef6bdb..a704905a48fa7c 100644 --- a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.h +++ b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/same_operands_result.h @@ -154,6 +154,7 @@ OP_DECLARE_INFER_SYMBOLIC_SHAPE(Select) OP_DECLARE_INFER_SYMBOLIC_SHAPE(ShadowFeed) OP_DECLARE_INFER_SYMBOLIC_SHAPE(ShareData_) OP_DECLARE_INFER_SYMBOLIC_SHAPE(Sign) +OP_DECLARE_INFER_SYMBOLIC_SHAPE(Sign_) OP_DECLARE_INFER_SYMBOLIC_SHAPE(Silu) OP_DECLARE_INFER_SYMBOLIC_SHAPE(Silu_) OP_DECLARE_INFER_SYMBOLIC_SHAPE(Sin) diff --git a/paddle/phi/ops/yaml/ops.yaml b/paddle/phi/ops/yaml/ops.yaml index 63b7c403028e74..589be97caae6da 100644 --- a/paddle/phi/ops/yaml/ops.yaml +++ b/paddle/phi/ops/yaml/ops.yaml @@ -5136,6 +5136,7 @@ spmd_rule : ElementwiseUnaryInferSpmd kernel : func : sign + inplace: (x -> out) backward : sign_grad interfaces : paddle::dialect::InferSymbolicShapeInterface traits: pir::UnaryElementWiseTrait diff --git a/paddle/phi/ops/yaml/python_api_info.yaml b/paddle/phi/ops/yaml/python_api_info.yaml index fa79d453f5dbe5..b4f77f5d124581 100644 --- a/paddle/phi/ops/yaml/python_api_info.yaml +++ b/paddle/phi/ops/yaml/python_api_info.yaml @@ -1,10 +1,10 @@ - op : abs - name : [paddle.abs, paddle.Tensor.abs] + name : [paddle.abs, paddle.Tensor.abs, paddle.absolute, paddle.Tensor.absolute] args_alias : use_default_mapping : True - op : abs_ - name : [paddle.abs_, paddle.Tensor.abs_] + name : [paddle.abs_, paddle.Tensor.abs_, paddle.absolute_, paddle.Tensor.absolute_] args_alias : use_default_mapping : True @@ -470,7 +470,7 @@ - op : inverse name : [paddle.inverse, paddle.Tensor.inverse, paddle.linalg.inv] args_alias: - use_default_mapping : True + x : [input, A] - op : lgamma name : [paddle.lgamma, paddle.Tensor.lgamma] @@ -580,6 +580,11 @@ args_alias : use_default_mapping : True +- op : imag + name : [paddle.imag, paddle.Tensor.imag] + args_alias : + use_default_mapping : True + - op : pixel_shuffle name : [paddle.nn.functional.pixel_shuffle] args_alias : @@ -598,7 +603,7 @@ use_default_mapping : True - op : remainder_ - name : [paddle.remainder_, paddle.Tensor.remainder_, paddle.mod_, paddle.Tensor.mod_, paddle.floor_mod_, paddle.Tensor.floor_mod_] + name : [paddle.remainder_, paddle.Tensor.remainder_, paddle.mod_, paddle.Tensor.mod_, paddle.floor_mod_, paddle.Tensor.floor_mod_, paddle.fmod_, paddle.Tensor.fmod_] args_alias : use_default_mapping : True pre_process : @@ -647,6 +652,11 @@ args_alias : use_default_mapping : True +- op : sign_ + name : [paddle.sign_, paddle.Tensor.sign_] + args_alias : + use_default_mapping : True + - op : sin name : [paddle.sin, paddle.Tensor.sin] args_alias : diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index ae49d88c399c2d..d96156edbf2e68 100644 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -343,6 +343,7 @@ def new_init(self, *args, **kwargs): load, save, set_default_dtype, + set_default_tensor_type, ) from .framework.random import ( Generator, @@ -435,6 +436,7 @@ def new_init(self, *args, **kwargs): cdist, cholesky, cross, + det, diagonal, dist, dot, @@ -447,6 +449,7 @@ def new_init(self, *args, **kwargs): mv, norm, permute, + pinv, t, t_, transpose, @@ -532,10 +535,12 @@ def new_init(self, *args, **kwargs): moveaxis, narrow, put_along_axis, + put_along_axis_, ravel, repeat_interleave, reshape, reshape_, + resize_as_, roll, rot90, row_stack, @@ -546,6 +551,7 @@ def new_init(self, *args, **kwargs): scatter_nd, scatter_nd_add, scatter_reduce, + scatter_reduce_, select_scatter, shard_index, slice, @@ -584,6 +590,8 @@ def new_init(self, *args, **kwargs): acosh_, add, add_n, + addcdiv, + addcdiv_, addmm, addmm_, addmv, @@ -737,6 +745,7 @@ def new_init(self, *args, **kwargs): scale, sgn, sign, + sign_, signbit, sin, sin_, @@ -1063,11 +1072,13 @@ def __dir__(self): movedim = moveaxis mod = remainder floor_mod = remainder +fmod = remainder fix = trunc fix_ = trunc_ mvlgamma = multigammaln mvlgamma_ = multigammaln_ negative_ = neg_ +pinverse = pinv __all__ = [ 'block_diag', @@ -1100,6 +1111,8 @@ def __dir__(self): 'complex128', 'pstring', 'raw', + 'addcdiv', + 'addcdiv_', 'addmm', 'addmm_', 'addmv', @@ -1115,6 +1128,7 @@ def __dir__(self): 'add', 'subtract', 'subtract_', + 'det', 'diag', 'diagflat', 'diag_embed', @@ -1300,6 +1314,8 @@ def __dir__(self): 'inference_mode', 'mod', 'mod_', + 'fmod', + 'fmod_', 'abs', 'abs_', 'tril', @@ -1312,6 +1328,7 @@ def __dir__(self): 'index_select', 'CPUPlace', 'matmul', + 'pinverse', 'seed', 'acos', 'acos_', @@ -1437,6 +1454,7 @@ def __dir__(self): 'enable_static', 'scatter_nd', 'set_default_dtype', + 'set_default_tensor_type', 'disable_signal_handler', 'expand_as', 'stack', @@ -1451,6 +1469,7 @@ def __dir__(self): 'logspace', 'reshape', 'reshape_', + 'resize_as_', 'atleast_1d', 'atleast_2d', 'atleast_3d', @@ -1512,7 +1531,9 @@ def __dir__(self): 'take_along_axis', 'take_along_dim', 'scatter_reduce', + 'scatter_reduce_', 'put_along_axis', + 'put_along_axis_', 'scatter_add', 'select_scatter', 'multigammaln', diff --git a/python/paddle/_paddle_docs.py b/python/paddle/_paddle_docs.py index ade0d826743d85..41bfd57f2fb640 100644 --- a/python/paddle/_paddle_docs.py +++ b/python/paddle/_paddle_docs.py @@ -3966,6 +3966,60 @@ def angle( """, ) +add_doc_and_signature( + "imag", + r""" + Returns a new tensor containing imaginary values of input tensor. + + Args: + x (Tensor): the input tensor, its data type could be complex64 or complex128. + name (str|None, optional): The default value is None. Normally there is no need for + user to set this property. For more information, please refer to :ref:`api_guide_Name` . + + Keyword args: + out(Tensor, optional): The output tensor. + + Returns: + Tensor: a tensor containing imaginary values of the input tensor. + + Examples: + .. code-block:: pycon + + >>> import paddle + + >>> x = paddle.to_tensor( + ... [ + ... [1 + 6j, 2 + 5j, 3 + 4j], + ... [4 + 3j, 5 + 2j, 6 + 1j], + ... ] + ... ) + >>> print(x) + Tensor(shape=[2, 3], dtype=complex64, place=Place(cpu), stop_gradient=True, + [[(1.00000000+6.00000000j), (2.00000000+5.00000000j), (3.00000000+4.00000000j)], + [(4.00000000+3.00000000j), (5.00000000+2.00000000j), (6.00000000+1.00000000j)]]) + + >>> imag_res = paddle.imag(x) + >>> print(imag_res) + Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True, + [[6., 5., 4.], + [3., 2., 1.]]) + + >>> imag_t = x.imag() + >>> print(imag_t) + Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True, + [[6., 5., 4.], + [3., 2., 1.]]) +""", + """ +def imag( + x: Tensor, + name: str | None = None, + *, + out: Tensor | None = None, +) -> Tensor +""", +) + add_doc_and_signature( "real", r""" diff --git a/python/paddle/compat/nn/__init__.py b/python/paddle/compat/nn/__init__.py index 7f0c1f3cb39bef..edbb2a84df3b09 100644 --- a/python/paddle/compat/nn/__init__.py +++ b/python/paddle/compat/nn/__init__.py @@ -744,7 +744,7 @@ def forward(self, input: Tensor) -> Tensor: return functional.softmax(input, self._dim) def extra_repr(self) -> str: - return f"dim={self.dim}" + return f"dim={self._dim}" class SmoothL1Loss(nn.Layer): diff --git a/python/paddle/framework/__init__.py b/python/paddle/framework/__init__.py index e90124af450432..b5dd046620575c 100755 --- a/python/paddle/framework/__init__.py +++ b/python/paddle/framework/__init__.py @@ -80,7 +80,11 @@ # isort: on from ..base.param_attr import ParamAttr # noqa: F401 from . import random # noqa: F401 -from .framework import get_default_dtype, set_default_dtype # noqa: F401 +from .framework import ( # noqa: F401 + get_default_dtype, + set_default_dtype, + set_default_tensor_type, +) from .io import load, save # noqa: F401 from .io_utils import ( # noqa: F401 _clone_var_in_block_, diff --git a/python/paddle/framework/framework.py b/python/paddle/framework/framework.py index b3a9b392664976..046fbd4842c052 100644 --- a/python/paddle/framework/framework.py +++ b/python/paddle/framework/framework.py @@ -77,6 +77,53 @@ def set_default_dtype(d: DTypeLike) -> None: LayerHelperBase.set_default_dtype(d) +def set_default_tensor_type(t: DTypeLike | str, /) -> None: + """ + Set the default tensor type. + + .. warning:: + This API is deprecated. Please use ``paddle.set_default_dtype`` instead. + + Args: + t (dtype or str): The default tensor type. It can be a dtype like + ``paddle.float32`` or a string like ``"paddle.float32"`` or + ``"paddle.FloatTensor"``. Only float dtypes are supported. + + Returns: + None. + + Examples: + .. code-block:: pycon + + >>> import paddle + >>> paddle.set_default_tensor_type("paddle.FloatTensor") + >>> paddle.set_default_tensor_type(paddle.FloatTensor) + """ + if isinstance(t, type): + t = t.__name__ + + if isinstance(t, str): + t = t.replace('torch.', '').replace('paddle.', '').replace('cuda.', '') + dtype_map = { + "FloatTensor": "float32", + "DoubleTensor": "float64", + "HalfTensor": "float16", + "BFloat16Tensor": "bfloat16", + } + if t in dtype_map: + t = dtype_map[t] + else: + raise TypeError( + f"set_default_tensor_type only supports DtypeTensor, but received {t}" + ) + else: + raise TypeError( + f"set_default_tensor_type only supports DtypeTensor or str, but received {t}" + ) + + set_default_dtype(t) + + def get_default_dtype() -> _DTypeLiteral: """ Get the current default dtype. The default dtype is initially float32. diff --git a/python/paddle/nn/functional/__init__.py b/python/paddle/nn/functional/__init__.py index 1da830766c2320..2a47e5a7733570 100644 --- a/python/paddle/nn/functional/__init__.py +++ b/python/paddle/nn/functional/__init__.py @@ -179,6 +179,7 @@ conv_transpose1d = conv1d_transpose conv_transpose2d = conv2d_transpose conv_transpose3d = conv3d_transpose +huber_loss = smooth_l1_loss multilabel_margin_loss = multi_label_margin_loss multilabel_soft_margin_loss = multi_label_soft_margin_loss __all__ = [ @@ -324,4 +325,5 @@ "flash_attention_v3_varlen", 'flash_attn_varlen_qkvpacked', 'group_norm', + 'huber_loss', ] diff --git a/python/paddle/nn/functional/loss.py b/python/paddle/nn/functional/loss.py index 2de571f8587169..ddc8b9d60fa85a 100644 --- a/python/paddle/nn/functional/loss.py +++ b/python/paddle/nn/functional/loss.py @@ -21,7 +21,10 @@ from paddle import _C_ops, base, in_dynamic_mode from paddle.static.nn.control_flow import Assert from paddle.utils import deprecated -from paddle.utils.decorator_utils import legacy_reduction_decorator +from paddle.utils.decorator_utils import ( + ParamAliasDecorator, + legacy_reduction_decorator, +) from ...base.data_feeder import check_type, check_variable_and_dtype from ...base.framework import ( @@ -2063,6 +2066,12 @@ def mse_loss( ) +@ParamAliasDecorator( + alias_mapping={ + 'labels': ['targets'], + 'label_lengths': ['target_lengths'], + } +) def ctc_loss( log_probs: Tensor, labels: Tensor, diff --git a/python/paddle/nn/init.py b/python/paddle/nn/init.py index e577e585746ffa..62b0c3ea6c83af 100644 --- a/python/paddle/nn/init.py +++ b/python/paddle/nn/init.py @@ -14,7 +14,19 @@ from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable + from typing import TypeVar + + from typing_extensions import ParamSpec + + _InputT = ParamSpec("_InputT") + _RetT = TypeVar("_RetT") + import math +import warnings import numpy as np @@ -422,3 +434,39 @@ def sparse_( zero_indices = row_indices[:num_zeros] tensor[zero_indices, col_idx] = 0 return tensor + + +def _make_deprecate(func: Callable[_InputT, _RetT]) -> Callable[_InputT, _RetT]: + new_name = func.__name__ + old_name = new_name[:-1] + + def deprecated_init(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT: + warnings.warn( + f"`nn.init.{old_name}` is now deprecated in favor of `nn.init.{new_name}`.", + FutureWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + deprecated_init.__doc__ = rf""" + {old_name}(...) + + .. warning:: + This method is now deprecated in favor of :func:`paddle.nn.init.{new_name}`. + + See :func:`~paddle.nn.init.{new_name}` for details.""" + deprecated_init.__name__ = old_name + return deprecated_init + + +uniform = _make_deprecate(uniform_) +normal = _make_deprecate(normal_) +constant = _make_deprecate(constant_) +eye = _make_deprecate(eye_) +dirac = _make_deprecate(dirac_) +xavier_uniform = _make_deprecate(xavier_uniform_) +xavier_normal = _make_deprecate(xavier_normal_) +kaiming_uniform = _make_deprecate(kaiming_uniform_) +kaiming_normal = _make_deprecate(kaiming_normal_) +orthogonal = _make_deprecate(orthogonal_) +sparse = _make_deprecate(sparse_) diff --git a/python/paddle/nn/layer/activation.py b/python/paddle/nn/layer/activation.py index bdb478beb2433d..ffe46072d7d02a 100644 --- a/python/paddle/nn/layer/activation.py +++ b/python/paddle/nn/layer/activation.py @@ -1564,10 +1564,10 @@ class Softmax(Layer): [0.72747516, 0.72747516, 0.72747516, 0.72747516]]] Parameters: - axis (int, optional): The axis along which to perform log_softmax + axis (int, optional): The axis along which to perform softmax calculations. It should be in range [-D, D), where D is the dimensions of ``x`` . If ``axis`` < 0, it works the same way as - :math:`axis + D` . Default is -1. + :math:`axis + D` . Default is -1. Alias: ``dim``. name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`. diff --git a/python/paddle/nn/layer/common.py b/python/paddle/nn/layer/common.py index f8319f8603c1d2..efa788f69a6545 100644 --- a/python/paddle/nn/layer/common.py +++ b/python/paddle/nn/layer/common.py @@ -18,7 +18,11 @@ import paddle from paddle import in_dynamic_mode -from paddle.utils.decorator_utils import ParamAliasDecorator, param_one_alias +from paddle.utils.decorator_utils import ( + ParamAliasDecorator, + param_one_alias, + param_two_alias, +) from .. import functional as F from .layers import Layer @@ -2764,7 +2768,9 @@ class Flatten(Layer): Parameters: start_axis(int): first dim to flatten (default = 1) + Alias: ``start_dim``. stop_axis(int): last dim to flatten (default = -1). + Alias: ``end_dim``. Returns: None @@ -2786,6 +2792,7 @@ class Flatten(Layer): start_axis: int stop_axis: int + @param_two_alias(["start_axis", "start_dim"], ["stop_axis", "end_dim"]) def __init__(self, start_axis: int = 1, stop_axis: int = -1) -> None: super().__init__() self.start_axis = start_axis diff --git a/python/paddle/nn/layer/container.py b/python/paddle/nn/layer/container.py index 6d03dd2b25d916..76067756df223a 100644 --- a/python/paddle/nn/layer/container.py +++ b/python/paddle/nn/layer/container.py @@ -469,6 +469,7 @@ class ParameterList(Layer): Parameters: parameters (iterable, optional): Iterable Parameters to be added. + Alias: ``values``. Examples: .. code-block:: pycon @@ -508,6 +509,7 @@ class ParameterList(Layer): paddle.Size([5, 4]) """ + @param_one_alias(["parameters", "values"]) def __init__(self, parameters: Iterable[Tensor] | None = None) -> None: super().__init__() if parameters is not None: @@ -533,16 +535,33 @@ def __iter__(self) -> Iterator[Tensor]: with param_guard(self._parameters): return iter(self._parameters.values()) + @param_one_alias(["parameter", "value"]) def append(self, parameter: Tensor) -> Self: """Appends a given parameter at the end of the list. Parameters: - parameter (Parameter): parameter to append + parameter (Parameter): parameter to append. + Alias: ``value``. """ idx = len(self._parameters) self.add_parameter(str(idx), parameter) return self + @param_one_alias(["parameters", "values"]) + def extend(self, parameters: Iterable[Tensor]) -> Self: + """Append values from a Python iterable to the end of the list. + + Parameters: + parameters (iterable): iterable of values to append. + Alias: ``values``. + """ + for v in parameters: + self.append(v) + return self + + def __iadd__(self, parameters: Iterable[Tensor]) -> Self: + return self.extend(parameters) + class LayerList(Layer): """ diff --git a/python/paddle/nn/layer/rnn.py b/python/paddle/nn/layer/rnn.py index d54a5a19cba892..e2b20f68218656 100644 --- a/python/paddle/nn/layer/rnn.py +++ b/python/paddle/nn/layer/rnn.py @@ -17,7 +17,7 @@ import math from collections.abc import Sequence from functools import partial, reduce -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, overload import numpy as np from typing_extensions import Self @@ -38,6 +38,7 @@ initializer as I, ) from paddle.tensor.manipulation import tensor_array_to_tensor +from paddle.utils.decorator_utils import gru_decorator from .container import LayerList from .layers import Layer @@ -527,6 +528,10 @@ def split_states( hidden size of the RNN cell. """ if state_components == 1: + # For state_components == 1 (GRU/SimpleRNN), states should be a tensor + # If it's a tuple/list with one element, extract it + if isinstance(states, (tuple, list)) and len(states) == 1: + states = states[0] states = paddle.unstack(states) if not bidirectional: return states @@ -1285,13 +1290,7 @@ def __init__( default_initializer=I.Uniform(-std, std), ) else: - self.bias_ih = self.create_parameter( - (3 * hidden_size,), - None, - is_bias=True, - default_initializer=I.Constant(0.0), - ) - self.bias_ih.stop_gradient = True + self.bias_ih = None if bias_hh_attr is not False: self.bias_hh = self.create_parameter( @@ -1301,13 +1300,7 @@ def __init__( default_initializer=I.Uniform(-std, std), ) else: - self.bias_hh = self.create_parameter( - (3 * hidden_size,), - None, - is_bias=True, - default_initializer=I.Constant(0.0), - ) - self.bias_hh.stop_gradient = True + self.bias_hh = None self.hidden_size = hidden_size self.input_size = input_size @@ -1615,8 +1608,8 @@ def __init__( ) self.could_use_cudnn = True - self.could_use_cudnn &= len(self.parameters()) == num_layers * 4 * ( - 2 if direction in bidirectional_list else 1 + self.could_use_cudnn &= ( + len(self.parameters()) == 4 * num_layers * self.num_directions ) # Expose params as RNN's attribute, which can make it compatible when @@ -2202,8 +2195,11 @@ class GRU(RNNBase): `bias_ih` of each cells. Default: None. bias_hh_attr (ParamAttr|None, optional): The parameter attribute for the `bias_hh` of each cells. Default: None. - name (str|None, optional): Name for the operation (optional, default is - None). For more information, please refer to :ref:`api_guide_Name`. + + Keyword Args: + bias (bool, optional): If False, then the layer does not use bias weights `bias_ih` and `bias_hh`. Default: True. + device (str, optional): The device to execute the layer. Default: None. + dtype (str, optional): The data type of the layer. Default: None. Inputs: - **inputs** (Tensor): the input sequence. If `time_major` is True, the shape is `[time_steps, batch_size, input_size]`, else, the shape is `[batch_size, time_steps, input_size]`. `time_steps` means the length of the input sequence. @@ -2242,6 +2238,7 @@ class GRU(RNNBase): """ + @overload def __init__( self, input_size: int, @@ -2254,8 +2251,48 @@ def __init__( weight_hh_attr: ParamAttrLike | None = None, bias_ih_attr: ParamAttrLike | None = None, bias_hh_attr: ParamAttrLike | None = None, - name: str | None = None, + *, + bias: bool = True, + device=None, + dtype=None, + ) -> None: ... + + @overload + def __init__( + self, + input_size: int, + hidden_size: int, + num_layers: int, + bias: bool, + batch_first: bool, + dropout: float = 0.0, + bidirectional: bool = False, + device=None, + dtype=None, + ) -> None: ... + + @gru_decorator() + def __init__( + self, + input_size: int, + hidden_size: int, + num_layers: int = 1, + direction: _DirectionType | str = "forward", + time_major: bool = False, + dropout: float = 0.0, + weight_ih_attr: ParamAttrLike | None = None, + weight_hh_attr: ParamAttrLike | None = None, + bias_ih_attr: ParamAttrLike | None = None, + bias_hh_attr: ParamAttrLike | None = None, + *, + bias: bool = True, + device=None, + dtype=None, ) -> None: + if not bias: + bias_ih_attr = False + bias_hh_attr = False + super().__init__( "GRU", input_size, @@ -2269,4 +2306,6 @@ def __init__( bias_ih_attr, bias_hh_attr, 0, # proj_size + device=device, + dtype=dtype, ) diff --git a/python/paddle/nn/utils/__init__.py b/python/paddle/nn/utils/__init__.py index 75a610aade6951..d9413ff42bbc15 100644 --- a/python/paddle/nn/utils/__init__.py +++ b/python/paddle/nn/utils/__init__.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from . import rnn # noqa: F401 from .clip_grad_norm_ import clip_grad_norm_ from .clip_grad_value_ import clip_grad_value_ -from .rnn import pad_sequence, unpad_sequence from .spectral_norm_hook import spectral_norm from .transform_parameters import ( _stride_column, # noqa: F401 @@ -31,6 +31,4 @@ 'vector_to_parameters', 'clip_grad_norm_', 'clip_grad_value_', - 'pad_sequence', - 'unpad_sequence', ] diff --git a/python/paddle/nn/utils/rnn.py b/python/paddle/nn/utils/rnn.py index 4639dcdb222247..040a9286babb45 100644 --- a/python/paddle/nn/utils/rnn.py +++ b/python/paddle/nn/utils/rnn.py @@ -14,6 +14,7 @@ from __future__ import annotations +from collections.abc import Iterable from typing import TYPE_CHECKING import paddle @@ -21,11 +22,333 @@ if TYPE_CHECKING: from paddle import Tensor -__all__: list[str] = [] +__all__ = [ + "PackedSequence", + "invert_permutation", + "pack_padded_sequence", + "pad_packed_sequence", + "pad_sequence", + "unpad_sequence", + "pack_sequence", + "unpack_sequence", +] + + +def invert_permutation(permutation: Tensor | None) -> Tensor | None: + """Returns the inverse of ``permutation``. + + This is useful for converting between sorted and unsorted indices in + a :class:`~nn.utils.rnn.PackedSequence`. + + Args: + permutation (Tensor|None): a 1-D tensor of indices to invert. + + Returns: + Tensor|None: the inverse permutation tensor, or None if input is None. + + Examples: + >>> import paddle + """ + if permutation is None: + return None + # Use paddle.scatter instead of scatter_ for better static mode support + output = paddle.scatter( + paddle.zeros_like(permutation), + permutation, + paddle.arange( + 0, + permutation.numel(), + dtype=permutation.dtype, + device=permutation.place, + ), + overwrite=True, + ) + return output + + +class PackedSequence: + """Holds the data and batch sizes of a packed sequence. + + PackedSequence is used to represent a packed sequence, which is typically + produced by ``pack_padded_sequence`` and consumed by ``pad_packed_sequence``. + + Args: + data (Tensor): The packed data tensor. + batch_sizes (Tensor): A tensor containing the batch size at each step. + sorted_indices (Tensor|None, optional): The indices used to sort the sequences. + unsorted_indices (Tensor|None, optional): The indices to restore the original order. + + Examples: + .. code-block:: pycon + + >>> import paddle + + """ + + def __init__( + self, + data: Tensor, + batch_sizes: Tensor, + sorted_indices: Tensor | None = None, + unsorted_indices: Tensor | None = None, + ): + self.data = data + self.batch_sizes = batch_sizes + self.sorted_indices = sorted_indices + self.unsorted_indices = unsorted_indices + + @property + def is_pinned(self) -> bool: + return ( + self.data.place.is_cuda_pinned_place() + or self.data.place.is_xpu_pinned_place() + ) + + def to(self, *args, **kwargs) -> PackedSequence: + data = self.data.to(*args, **kwargs) + if data is self.data: + return self + # Only convert indices to same device as data, not dtype + target_device = data.place + sorted_indices = ( + self.sorted_indices.to(target_device) + if self.sorted_indices is not None + else None + ) + unsorted_indices = ( + self.unsorted_indices.to(target_device) + if self.unsorted_indices is not None + else None + ) + return PackedSequence( + data, self.batch_sizes, sorted_indices, unsorted_indices + ) + + def cuda(self) -> PackedSequence: + return self.to(device="gpu") + + def cpu(self) -> PackedSequence: + return self.to(device="cpu") + + def __repr__(self) -> str: + return ( + f"PackedSequence(data={self.data}, batch_sizes={self.batch_sizes}, " + f"sorted_indices={self.sorted_indices}, unsorted_indices={self.unsorted_indices})" + ) + + def pin_memory(self) -> PackedSequence: + return PackedSequence( + self.data.pin_memory(), + self.batch_sizes, + self.sorted_indices.pin_memory() + if self.sorted_indices is not None + else None, + self.unsorted_indices.pin_memory() + if self.unsorted_indices is not None + else None, + ) + + @property + def is_cuda(self) -> bool: + return self.data.is_cuda + + def double(self) -> PackedSequence: + return self.to(dtype=paddle.float64) + + def float(self) -> PackedSequence: + return self.to(dtype=paddle.float32) + + def half(self) -> PackedSequence: + return self.to(dtype=paddle.float16) + + def long(self) -> PackedSequence: + return self.to(dtype=paddle.int64) + + def int(self) -> PackedSequence: + return self.to(dtype=paddle.int32) + + def short(self) -> PackedSequence: + return self.to(dtype=paddle.int16) + + def char(self) -> PackedSequence: + return self.to(dtype=paddle.int8) + + def byte(self) -> PackedSequence: + return self.to(dtype=paddle.uint8) + + +def pack_padded_sequence( + input: Tensor, + lengths: Tensor | list[int], + batch_first: bool = False, + enforce_sorted: bool = True, +) -> PackedSequence: + r"""Packs a Tensor containing padded sequences of variable length. + + This function packs a Tensor containing padded sequences into a PackedSequence + object, which can be used as input to a recurrent neural network. + + Args: + input (Tensor): The padded sequence tensor. Shape is ``T x B x *`` if + ``batch_first`` is False, or ``B x T x *`` if ``batch_first`` is True, + where ``T`` is the length of the longest sequence, ``B`` is the batch size. + lengths (Tensor|list[int]): The lengths of each sequence in the batch. + batch_first (bool, optional): If True, the input is expected to be in + ``B x T x *`` format. Default: False. + enforce_sorted (bool, optional): If True, the input is expected to contain + sequences sorted by length in descending order. Default: True. + + Returns: + PackedSequence: A PackedSequence object containing the packed data. + + Examples: + .. code-block:: pycon + + >>> import paddle + + """ + if batch_first: + input = input.transpose([1, 0, *range(2, len(input.shape))]) + + if isinstance(lengths, paddle.Tensor): + lengths = lengths.tolist() + + batch_size = input.shape[1] + + if len(lengths) != batch_size: + raise ValueError( + f"Length of lengths ({len(lengths)}) does not match batch size ({batch_size})" + ) + + sorted_indices = None + unsorted_indices = None + + if not enforce_sorted: + sorted_lengths = sorted( + enumerate(lengths), key=lambda x: x[1], reverse=True + ) + sorted_indices = paddle.to_tensor( + [i for i, _ in sorted_lengths], place=input.place + ) + unsorted_indices = paddle.argsort(sorted_indices) + lengths = [l for _, l in sorted_lengths] + # Use index_select to reorder along batch dimension (axis=1) + input = paddle.index_select(input, sorted_indices, axis=1) + + packed_data_list = [] + batch_sizes_list = [] + + # num_steps may be different from actual input shape[0] after sorting + # We need to iterate over the actual sequence length + actual_num_steps = input.shape[0] + for step in range(actual_num_steps): + batch_size_at_step = sum(1 for l in lengths if l > step) + if batch_size_at_step > 0: + packed_data_list.append(input[step, :batch_size_at_step]) + batch_sizes_list.append(batch_size_at_step) + + packed_data = paddle.concat(packed_data_list, axis=0) + batch_sizes = paddle.to_tensor(batch_sizes_list, dtype="int64") + + return PackedSequence( + packed_data, batch_sizes, sorted_indices, unsorted_indices + ) + + +def pad_packed_sequence( + sequence: PackedSequence, + batch_first: bool = False, + padding_value: float = 0.0, + total_length: int | None = None, +) -> tuple[Tensor, Tensor]: + r"""Pads a packed sequence to a Tensor of padded sequences. + + This function is the inverse of ``pack_padded_sequence``. It takes a PackedSequence + and returns a padded Tensor and a list of lengths. + + Args: + sequence (PackedSequence): The packed sequence to pad. + batch_first (bool, optional): If True, the output will be in ``B x T x *`` + format. Default: False. + padding_value (float, optional): The value to use for padding. Default: 0.0. + total_length (int|None, optional): If not None, the output will be padded to + this length. Default: None. + + Returns: + tuple[Tensor, Tensor]: A tuple containing: + - The padded sequence tensor. + - A tensor of sequence lengths. + + Examples: + .. code-block:: pycon + + >>> import paddle + + """ + if not isinstance(sequence, PackedSequence): + raise TypeError(f"Expected PackedSequence, got {type(sequence)}") + + data = sequence.data + batch_sizes = sequence.batch_sizes.tolist() + unsorted_indices = sequence.unsorted_indices + + max_seq_len = len(batch_sizes) + max_batch_size = batch_sizes[0] + + if total_length is not None: + if total_length < max_seq_len: + raise ValueError( + f"total_length ({total_length}) must be >= max sequence length ({max_seq_len})" + ) + + trailing_dims = list(data.shape[1:]) + + if total_length is not None and total_length > max_seq_len: + output = paddle.full( + [total_length, max_batch_size, *trailing_dims], + padding_value, + dtype=data.dtype, + device=data.place, + ) + else: + output = paddle.full( + [max_seq_len, max_batch_size, *trailing_dims], + padding_value, + dtype=data.dtype, + device=data.place, + ) + + data_offset = 0 + for step, batch_size in enumerate(batch_sizes): + output[step, :batch_size] = data[data_offset : data_offset + batch_size] + data_offset += batch_size + + # Calculate lengths from batch_sizes + # batch_sizes is in descending order, e.g., [3, 2, 1] means: + # - First time step has 3 sequences + # - Second time step has 2 sequences + # - Third time step has 1 sequence + # This means sequence lengths are [3, 2, 1] in sorted order + lengths_list = [] + for i in range(max_batch_size): + # Find the length of the i-th sequence (in sorted order) + # It's the number of time steps where batch_sizes > i + seq_len = sum(1 for bs in batch_sizes if bs > i) + lengths_list.append(seq_len) + lengths = paddle.to_tensor(lengths_list, dtype="int64", place=data.place) + + if unsorted_indices is not None: + output = output[:, unsorted_indices] + lengths = lengths[unsorted_indices] + + if batch_first: + output = output.transpose([1, 0, *range(2, len(output.shape))]) + + return output, lengths def pad_sequence( - sequences: list[Tensor], + sequences: Iterable[Tensor], batch_first: bool = False, padding_value: float = 0.0, padding_side: str = 'right', @@ -66,17 +389,24 @@ def pad_sequence( >>> a = paddle.ones([25, 300]) >>> b = paddle.ones([22, 300]) >>> c = paddle.ones([15, 300]) - >>> padded = paddle.nn.utils.pad_sequence([a, b, c]) + >>> padded = paddle.nn.utils.rnn.pad_sequence([a, b, c]) >>> print(padded.shape) paddle.Size([25, 3, 300]) - >>> padded = paddle.nn.utils.pad_sequence([a, b, c], batch_first=True) + >>> padded = paddle.nn.utils.rnn.pad_sequence([a, b, c], batch_first=True) >>> print(padded.shape) paddle.Size([3, 25, 300]) + """ - if not isinstance(sequences, list): + if not isinstance(sequences, Iterable): raise TypeError( - f"pad_sequence expects a list of Tensors, but got {type(sequences)}" + f"pad_sequence expects an iterable of Tensors, but got {type(sequences)}" ) + sequences = tuple(sequences) + for seq in sequences: + if not isinstance(seq, paddle.Tensor): + raise TypeError( + f"pad_sequence expects an iterable of Tensors, but got element of type {type(seq)}" + ) if padding_side not in ('right', 'left'): raise ValueError( f"padding_side must be 'right' or 'left', but got '{padding_side}'" @@ -136,15 +466,16 @@ def unpad_sequence( >>> b = paddle.ones([22, 300]) >>> c = paddle.ones([15, 300]) >>> sequences = [a, b, c] - >>> padded = paddle.nn.utils.pad_sequence(sequences) + >>> padded = paddle.nn.utils.rnn.pad_sequence(sequences) >>> lengths = paddle.to_tensor([v.shape[0] for v in sequences]) - >>> unpadded = paddle.nn.utils.unpad_sequence(padded, lengths) + >>> unpadded = paddle.nn.utils.rnn.unpad_sequence(padded, lengths) >>> paddle.allclose(sequences[0], unpadded[0]).item() True >>> paddle.allclose(sequences[1], unpadded[1]).item() True >>> paddle.allclose(sequences[2], unpadded[2]).item() True + """ if not batch_first: # Transpose from T x B x * to B x T x * @@ -157,3 +488,62 @@ def unpad_sequence( unpadded.append(seq[:length_val]) return unpadded + + +def pack_sequence( + sequences: list[Tensor], + enforce_sorted: bool = True, +) -> PackedSequence: + r"""Packs a list of variable length Tensors. + + Consecutive call of the next functions: ``pad_sequence``, ``pack_padded_sequence``. + + ``sequences`` should be a list of Tensors of size ``L x *``, where `L` is + the length of a sequence and `*` is any number of trailing dimensions, + including ``0``. + + For unsorted sequences, use `enforce_sorted = False`. If ``enforce_sorted`` + is ``True``, the sequences should be sorted in the order of decreasing length. + ``enforce_sorted = True`` is only necessary for ONNX export. + + Args: + sequences (list[Tensor]): A list of sequences of decreasing length. + enforce_sorted (bool, optional): if ``True``, checks that the input + contains sequences sorted by length in a decreasing order. If + ``False``, this condition is not checked. Default: ``True``. + + Returns: + PackedSequence: a PackedSequence object. + + Examples: + >>> import paddle + + """ + lengths = paddle.to_tensor([v.shape[0] for v in sequences]) + return pack_padded_sequence( + pad_sequence(sequences), lengths, enforce_sorted=enforce_sorted + ) + + +def unpack_sequence(packed_sequences: PackedSequence) -> list[Tensor]: + r"""Unpack PackedSequence into a list of variable length Tensors. + + ``packed_sequences`` should be a PackedSequence object. + + Args: + packed_sequences (PackedSequence): A PackedSequence object. + + Returns: + list[Tensor]: a list of Tensor objects. + + Examples: + >>> import paddle + + """ + padded_sequences, lengths = pad_packed_sequence( + packed_sequences, batch_first=True + ) + unpacked_sequences = unpad_sequence( + padded_sequences, lengths, batch_first=True + ) + return unpacked_sequences diff --git a/python/paddle/nn/utils/weight_norm_hook.py b/python/paddle/nn/utils/weight_norm_hook.py index 635be564f3381e..ef2ef6191f8666 100644 --- a/python/paddle/nn/utils/weight_norm_hook.py +++ b/python/paddle/nn/utils/weight_norm_hook.py @@ -18,6 +18,7 @@ import paddle from paddle import _C_ops +from paddle.utils.decorator_utils import param_one_alias from ...base.data_feeder import check_variable_and_dtype from ...base.layer_helper import LayerHelper @@ -175,6 +176,7 @@ def __call__(self, layer: Layer, inputs: Never) -> None: setattr(layer, self.name, self.compute_weight(layer)) +@param_one_alias(["layer", "module"]) def weight_norm(layer: Layer, name: str = 'weight', dim: int = 0) -> Layer: r""" Applies weight normalization to a parameter according to the @@ -195,6 +197,7 @@ def weight_norm(layer: Layer, name: str = 'weight', dim: int = 0) -> Layer: Parameters: layer(Layer): Layer of paddle, which has weight. + Alias: ``module``. name(str, optional): Name of the weight parameter. Default: 'weight'. dim(int, optional): Dimension over which to compute the norm. Dim is a non-negative number which is less than the rank of weight Tensor. For Example, dim can be chosen from 0, diff --git a/python/paddle/tensor/__init__.py b/python/paddle/tensor/__init__.py index 7c9d5185f15635..b7333eff2492cc 100644 --- a/python/paddle/tensor/__init__.py +++ b/python/paddle/tensor/__init__.py @@ -23,12 +23,10 @@ create_array, ) from .attribute import ( # noqa: F401 - imag, is_complex, is_floating_point, is_integer, rank, - real, shape, ) from .compat_softmax import log_softmax, softmax # noqa: F401 @@ -78,6 +76,7 @@ corrcoef, cov, cross, + det, diagonal, dist, dot, @@ -205,6 +204,7 @@ reshape, reshape_, reshape_as, + resize_as_, roll, rot90, row_stack, @@ -215,6 +215,7 @@ scatter_nd, scatter_nd_add, scatter_reduce, + scatter_reduce_, select_scatter, shard_index, slice, @@ -257,6 +258,8 @@ add, add_, add_n, + addcdiv, + addcdiv_, addmm, addmm_, addmv, @@ -420,6 +423,7 @@ sigmoid, sigmoid_, sign, + sign_, signbit, sin, sin_, @@ -522,11 +526,13 @@ movedim = moveaxis mod = remainder floor_mod = remainder +fmod = remainder fix = trunc fix_ = trunc_ mvlgamma = multigammaln mvlgamma_ = multigammaln_ negative_ = neg_ +pinverse = pinv # this list used in math_op_patch.py for _binary_creator_ tensor_method_func = [ @@ -585,6 +591,7 @@ 'logcumsumexp', 'logit', 'logit_', + 'det', 'exp', 'exp_', 'expm1', @@ -614,6 +621,7 @@ 'scale', 'scale_', 'sign', + 'sign_', 'sin', 'sinc', 'sinh', @@ -664,6 +672,7 @@ 'remainder', 'remainder_', 'mod', + 'fmod', 'floor_mod', 'multiply', 'multiply_', @@ -677,6 +686,8 @@ 'log1p', 'log1p_', 'erf', + 'addcdiv', + 'addcdiv_', 'addmm', 'addmm_', 'addmv', @@ -752,6 +763,7 @@ 'reshape', 'reshape_as', 'reshape_', + 'resize_as_', 'scatter', 'scatter_', 'scatter_nd_add', @@ -875,6 +887,7 @@ 'take_along_axis', 'take_along_dim', 'scatter_reduce', + 'scatter_reduce_', 'put_along_axis', 'scatter_add', 'select_scatter', @@ -920,6 +933,7 @@ 'atan2', 'diagflat', 'multinomial', + 'pinverse', 'pinv', 'renorm', 'renorm_', diff --git a/python/paddle/tensor/attribute.py b/python/paddle/tensor/attribute.py index 51430414c09955..7e7728856f00ac 100644 --- a/python/paddle/tensor/attribute.py +++ b/python/paddle/tensor/attribute.py @@ -20,14 +20,14 @@ import paddle from paddle import _C_ops -from paddle._C_ops import real # noqa: F401 +from paddle._C_ops import imag, real # noqa: F401 from paddle.utils.decorator_utils import param_one_alias from ..base.data_feeder import check_type, check_variable_and_dtype from ..base.framework import in_dynamic_or_pir_mode, use_pir_api from ..common_ops_import import Variable from ..framework import LayerHelper, core -from .creation import _complex_to_real_dtype, assign +from .creation import assign if TYPE_CHECKING: from paddle import Tensor @@ -289,55 +289,3 @@ def is_integer(x: Tensor) -> bool: ) return is_int_dtype - - -def imag(x: Tensor, name: str | None = None) -> Tensor: - """ - Returns a new tensor containing imaginary values of input tensor. - - Args: - x (Tensor): the input tensor, its data type could be complex64 or complex128. - name (str|None, optional): The default value is None. Normally there is no need for - user to set this property. For more information, please refer to :ref:`api_guide_Name` . - - Returns: - Tensor: a tensor containing imaginary values of the input tensor. - - Examples: - .. code-block:: pycon - - >>> import paddle - - >>> x = paddle.to_tensor( - ... [ - ... [1 + 6j, 2 + 5j, 3 + 4j], - ... [4 + 3j, 5 + 2j, 6 + 1j], - ... ] - ... ) - >>> print(x) - Tensor(shape=[2, 3], dtype=complex64, place=Place(cpu), stop_gradient=True, - [[(1.00000000+6.00000000j), (2.00000000+5.00000000j), (3.00000000+4.00000000j)], - [(4.00000000+3.00000000j), (5.00000000+2.00000000j), (6.00000000+1.00000000j)]]) - - >>> imag_res = paddle.imag(x) - >>> print(imag_res) - Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True, - [[6., 5., 4.], - [3., 2., 1.]]) - - >>> imag_t = x.imag() - >>> print(imag_t) - Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True, - [[6., 5., 4.], - [3., 2., 1.]]) - """ - if in_dynamic_or_pir_mode(): - return _C_ops.imag(x) - else: - check_variable_and_dtype(x, 'x', ['complex64', 'complex128'], 'imag') - helper = LayerHelper('imag', **locals()) - out = helper.create_variable_for_type_inference( - dtype=_complex_to_real_dtype(helper.input_dtype()) - ) - helper.append_op(type='imag', inputs={'X': x}, outputs={'Out': out}) - return out diff --git a/python/paddle/tensor/creation.py b/python/paddle/tensor/creation.py index b5fd3c8a9fe832..3ba080e6737c8b 100644 --- a/python/paddle/tensor/creation.py +++ b/python/paddle/tensor/creation.py @@ -32,6 +32,7 @@ ParamAliasDecorator, param_one_alias, param_two_alias, + resize__decorator, size_args_decorator, ) from paddle.utils.inplace_utils import inplace_apis_in_dygraph_only @@ -4230,6 +4231,7 @@ def set_( return _C_ops.set_(x, source, shape, stride, offset) +@resize__decorator() @inplace_apis_in_dygraph_only def resize_( x: paddle.Tensor, @@ -4309,7 +4311,7 @@ def resize_( return x.set_(x, shape) -def dtype_tensor_factory(dtype): +def dtype_tensor_factory(dtype, _name='_DtypeTensorFactory'): class _DtypeTensorFactory: def __new__(cls, *args, **kwargs): if len(args) == 0: @@ -4322,16 +4324,17 @@ def __new__(cls, *args, **kwargs): kwargs.setdefault('dtype', dtype) return paddle.Tensor(*args, **kwargs) + _DtypeTensorFactory.__name__ = _name return _DtypeTensorFactory -FloatTensor = dtype_tensor_factory('float32') -DoubleTensor = dtype_tensor_factory('float64') -HalfTensor = dtype_tensor_factory('float16') -BFloat16Tensor = dtype_tensor_factory('bfloat16') -ByteTensor = dtype_tensor_factory('uint8') -CharTensor = dtype_tensor_factory('int8') -ShortTensor = dtype_tensor_factory('int16') -IntTensor = dtype_tensor_factory('int32') -LongTensor = dtype_tensor_factory('int64') -BoolTensor = dtype_tensor_factory('bool') +FloatTensor = dtype_tensor_factory('float32', 'FloatTensor') +DoubleTensor = dtype_tensor_factory('float64', 'DoubleTensor') +HalfTensor = dtype_tensor_factory('float16', 'HalfTensor') +BFloat16Tensor = dtype_tensor_factory('bfloat16', 'BFloat16Tensor') +ByteTensor = dtype_tensor_factory('uint8', 'ByteTensor') +CharTensor = dtype_tensor_factory('int8', 'CharTensor') +ShortTensor = dtype_tensor_factory('int16', 'ShortTensor') +IntTensor = dtype_tensor_factory('int32', 'IntTensor') +LongTensor = dtype_tensor_factory('int64', 'LongTensor') +BoolTensor = dtype_tensor_factory('bool', 'BoolTensor') diff --git a/python/paddle/tensor/linalg.py b/python/paddle/tensor/linalg.py index 3d979b956fec04..5c3bd0e2f4330a 100644 --- a/python/paddle/tensor/linalg.py +++ b/python/paddle/tensor/linalg.py @@ -36,6 +36,7 @@ from paddle.utils.decorator_utils import ( ParamAliasDecorator, VariableArgsDecorator, + param_one_alias, param_two_alias, transpose_decorator, ) @@ -2302,6 +2303,7 @@ def histogram_bin_edges( return paddle.linspace(min, max, bins + 1, name=name) +@param_one_alias(["x", "input"]) def det(x: Tensor, name: str | None = None) -> Tensor: """ @@ -2310,7 +2312,7 @@ def det(x: Tensor, name: str | None = None) -> Tensor: Args: x (Tensor): the input matrix of size `(n, n)` or the batch of matrices of size `(*, n, n)` where `*` is one or more - batch dimensions. + batch dimensions. Alias: ``input``. name (str|None, optional): Name of the output.It's used to print debug info for developers. Details: :ref:`api_guide_Name`. Default is None. @@ -3598,11 +3600,16 @@ def __check_input(x, UPLO): return out_value, out_vector +@param_one_alias(["x", "input", "A"]) def pinv( x: Tensor, rcond: float | Tensor = 1e-15, hermitian: bool = False, name: str | None = None, + *, + atol: float | Tensor | None = None, + rtol: float | Tensor | None = None, + out: Tensor | None = None, ) -> Tensor: r""" Calculate pseudo inverse via SVD(singular value decomposition) @@ -3625,14 +3632,27 @@ def pinv( arbitrary positive number. The data type of x should be float32 or float64 or complex64 or complex128. When data type is complex64 or complex128, hermitian should be set - True. + True. Alias: ``input``. rcond (Tensor|float, optional): the tolerance value to determine - when is a singular value zero. Default:1e-15. + when is a singular value zero. This is used for backward + compatibility with NumPy. Prefer using ``rtol`` instead. + If ``rtol`` is specified, ``rcond`` is ignored. + Default:1e-15. hermitian (bool, optional): indicates whether x is Hermitian if complex or symmetric if real. Default: False. name (str|None, optional): The default value is None. Normally there is no need for user to set this property. For more information, please refer to :ref:`api_guide_Name`. + Keyword Args: + atol (float|Tensor|None, optional): the absolute tolerance value. + When None, it is considered to be zero. Default: None. + rtol (float|Tensor|None, optional): the relative tolerance value. + When both ``atol`` and ``rtol`` are None, ``rcond`` is used. + If ``rtol`` is specified, ``rcond`` is ignored. + Default: None. + out (Tensor|None, optional): output tensor. If provided, the result + will be assigned to this tensor. Default: None. + Returns: Tensor: The tensor with same data type with x. it represents pseudo inverse of x. Its shape should be (*, n, m). @@ -3662,6 +3682,8 @@ def pinv( # one can verify : x * out * x = x ; # or out * x * out = x ; """ + use_atol_rtol = atol is not None or rtol is not None + if in_dynamic_or_pir_mode(): if not hermitian: # combine svd and matmul op @@ -3670,8 +3692,31 @@ def pinv( max_singular_val = s else: max_singular_val = _C_ops.max(s, [-1], True) - rcond = paddle.to_tensor(rcond, dtype=x.dtype) - cutoff = rcond * max_singular_val + + if use_atol_rtol: + # Compute cutoff using atol and rtol + atol_val = atol if atol is not None else 0.0 + if not isinstance(atol_val, paddle.Tensor): + atol_val = paddle.to_tensor(atol_val, dtype=x.dtype) + elif atol_val.dtype != x.dtype: + atol_val = paddle.cast(atol_val, x.dtype) + + if rtol is not None: + rtol_val = rtol + if not isinstance(rtol_val, paddle.Tensor): + rtol_val = paddle.to_tensor(rtol_val, dtype=x.dtype) + elif rtol_val.dtype != x.dtype: + rtol_val = paddle.cast(rtol_val, x.dtype) + cutoff = paddle.maximum( + atol_val, max_singular_val * rtol_val + ) + else: + cutoff = atol_val + else: + # Use rcond for backward compatibility + rcond = paddle.to_tensor(rcond, dtype=x.dtype) + cutoff = rcond * max_singular_val + y = float('inf') y = paddle.to_tensor(y, dtype=x.dtype) @@ -3684,6 +3729,9 @@ def pinv( out_1 = v * st out_2 = _C_ops.matmul(out_1, u, False, True) + if out is not None: + paddle.assign(out_2, out) + return out return out_2 else: if in_dynamic_mode() and x.size == 0: @@ -3695,8 +3743,31 @@ def pinv( s, u = _C_ops.eigh(x, 'L') s_abs = paddle.abs(s) max_singular_val = _C_ops.max(s_abs, [-1], True) - rcond = paddle.to_tensor(rcond, dtype=s.dtype) - cutoff = rcond * max_singular_val + + if use_atol_rtol: + # Compute cutoff using atol and rtol + atol_val = atol if atol is not None else 0.0 + if not isinstance(atol_val, paddle.Tensor): + atol_val = paddle.to_tensor(atol_val, dtype=s.dtype) + elif atol_val.dtype != s.dtype: + atol_val = paddle.cast(atol_val, s.dtype) + + if rtol is not None: + rtol_val = rtol + if not isinstance(rtol_val, paddle.Tensor): + rtol_val = paddle.to_tensor(rtol_val, dtype=s.dtype) + elif rtol_val.dtype != s.dtype: + rtol_val = paddle.cast(rtol_val, s.dtype) + cutoff = paddle.maximum( + atol_val, max_singular_val * rtol_val + ) + else: + cutoff = atol_val + else: + # Use rcond for backward compatibility + rcond = paddle.to_tensor(rcond, dtype=s.dtype) + cutoff = rcond * max_singular_val + y = float('inf') y = paddle.to_tensor(y, dtype=s.dtype) @@ -3706,6 +3777,9 @@ def pinv( out_1 = u * st u_conj = _C_ops.conj(u) out_2 = _C_ops.matmul(out_1, u_conj, False, True) + if out is not None: + paddle.assign(out_2, out) + return out return out_2 else: if not hermitian: diff --git a/python/paddle/tensor/manipulation.py b/python/paddle/tensor/manipulation.py index 04ed65cb05f390..7b87b5f7678ec6 100644 --- a/python/paddle/tensor/manipulation.py +++ b/python/paddle/tensor/manipulation.py @@ -5822,6 +5822,38 @@ def reshape_(x: Tensor, shape: ShapeLike, name: str | None = None) -> Tensor: return out +@inplace_apis_in_dygraph_only +@param_one_alias(['y', 'the_template']) +def resize_as_( + x: Tensor, + y: Tensor, +) -> Tensor: + """ + Resizes the tensor to be the same shape as the given tensor. + + Note: + This API is ONLY available in Dygraph mode. + + Args: + x (Tensor): The input tensor to be resized. + y (Tensor): The tensor whose shape will be used as the target shape. + + Returns: + Tensor: The resized tensor with the same shape as y. + + Examples: + .. code-block:: pycon + + >>> import paddle + >>> x = paddle.ones([2, 3]) + >>> y = paddle.zeros([4, 5]) + >>> x.resize_as_(y) + >>> print(x.shape) + paddle.Size([4, 5]) + """ + return x.resize_(y.shape) + + @overload def atleast_1d(inputs: Tensor, name: str | None = ...) -> Tensor: ... @@ -7511,6 +7543,28 @@ def scatter_reduce( ) +def scatter_reduce_( + input: Tensor, + dim: int, + index: Tensor, + src: Tensor, + reduce: Literal['sum', 'prod', 'mean', 'amin', 'amax'], + *, + include_self: bool = True, +) -> Tensor: + """ + Inplace version of ``scatter_reduce`` API, the output Tensor will be inplaced with input ``input``. + Please refer to :ref:`api_paddle_scatter_reduce`. + """ + if reduce == 'sum': + reduce = 'add' + if reduce == 'prod': + reduce = 'multiply' + return put_along_axis_( + input, index, src, dim, reduce, include_self, broadcast=False + ) + + def put_along_axis( arr: Tensor, indices: Tensor, diff --git a/python/paddle/tensor/math.py b/python/paddle/tensor/math.py index af3f7ead4ebb4d..a4686cc62500ca 100644 --- a/python/paddle/tensor/math.py +++ b/python/paddle/tensor/math.py @@ -104,6 +104,7 @@ sigmoid, sigmoid_, sign, + sign_, sin, sin_, sinh, @@ -2329,7 +2330,6 @@ def addmv( vec: Tensor, beta: float = 1, alpha: float = 1, - name: str | None = None, *, out: Tensor | None = None, ) -> Tensor: @@ -2378,7 +2378,6 @@ def addmv_( vec: Tensor, beta: float = 1, alpha: float = 1, - name: str | None = None, ) -> Tensor: """ Inplace version of ``addmv`` API. @@ -2393,7 +2392,6 @@ def addr( vec2: Tensor, beta: float = 1, alpha: float = 1, - name: str | None = None, *, out: Tensor | None = None, ) -> Tensor: @@ -2439,7 +2437,6 @@ def addr_( vec2: Tensor, beta: float = 1, alpha: float = 1, - name: str | None = None, ) -> Tensor: """ Inplace version of ``addr`` API. @@ -2448,6 +2445,63 @@ def addr_( return input +def addcdiv( + input: Tensor, + tensor1: Tensor, + tensor2: Tensor, + value: float = 1, + *, + out: Tensor | None = None, +) -> Tensor: + """ + Performs the element-wise division of `tensor1` by `tensor2`, + multiplies the result by the scalar `value` and adds it to `input`. + + The formula is: out = input + value * (tensor1 / tensor2) + + Args: + input (Tensor): The input tensor to be added. + tensor1 (Tensor): The numerator tensor. + tensor2 (Tensor): The denominator tensor. + value (float, optional): Multiplier for tensor1 / tensor2. Default: 1. + name (str|None, optional): Name for the operation. Default: None. + + Keyword Args: + out (Tensor|None, optional): Output tensor. Default: None. + + Returns: + Tensor: The result tensor. + + Examples: + .. code-block:: pycon + + >>> import paddle + >>> input = paddle.randn([3]) + >>> tensor1 = paddle.randn([3]) + >>> tensor2 = paddle.randn([3]) + >>> out = paddle.addcdiv(input, tensor1, tensor2, value=0.5) + """ + result = paddle.add(input, value * paddle.divide(tensor1, tensor2)) + if out is not None: + paddle.assign(result, out) + return out + return result + + +@inplace_apis_in_dygraph_only +def addcdiv_( + input: Tensor, + tensor1: Tensor, + tensor2: Tensor, + value: float = 1, +) -> Tensor: + """ + Inplace version of ``addcdiv`` API. + """ + input.add_(value * paddle.divide(tensor1, tensor2)) + return input + + def histc( input: Tensor, bins: int = 100, diff --git a/python/paddle/tensor/random.py b/python/paddle/tensor/random.py index b461f333e14d5e..5e3c8de6aa05b0 100644 --- a/python/paddle/tensor/random.py +++ b/python/paddle/tensor/random.py @@ -1984,12 +1984,17 @@ def random_( return _C_ops.random_(x, from_, to) +@param_one_alias(["x", "input"]) def randint_like( x: Tensor, low: int = 0, high: int | None = None, dtype: DTypeLike | None = None, name: str | None = None, + *, + device: PlaceLike | None = None, + pin_memory: bool = False, + requires_grad: bool = False, ) -> Tensor: """ Returns a Tensor filled with random integers from a discrete uniform @@ -2014,6 +2019,14 @@ def randint_like( need for user to set this property. For more information, please refer to :ref:`api_guide_Name`. + Keyword Args: + device (PlaceLike|None, optional): The desired device of returned tensor. + Default: if ``None``, defaults to the device of ``x``. + pin_memory (bool, optional): If set, return tensor would be allocated in the pinned memory. + Works only for CPU tensors. Default: False. + requires_grad (bool, optional): If autograd should record operations on the + returned tensor. Default: False. + Returns: Tensor, A Tensor filled with random integers from a discrete uniform distribution in the range [``low``, ``high``), with ``shape`` and ``dtype``. @@ -2152,12 +2165,18 @@ def randint_like( f"high = {high}" ) + place = ( + _get_paddle_place(device) + if device is not None + else _current_expected_place() + ) + if pin_memory and in_dynamic_mode() and device is not None: + place = _to_pinned_place(place) + if in_dynamic_or_pir_mode(): if in_dynamic_mode(): shape = paddle.utils.convert_shape_to_list(shape) - out = _C_ops.randint( - low, high, shape, DataType.INT64, _current_expected_place() - ) + out = _C_ops.randint(low, high, shape, DataType.INT64, place) else: check_type( shape, @@ -2173,10 +2192,12 @@ def randint_like( ) if paddle.utils._contain_var(shape): shape = paddle.utils.get_int_tensor_list(shape) - out = _C_ops.randint( - low, high, shape, DataType.INT64, _current_expected_place() - ) + out = _C_ops.randint(low, high, shape, DataType.INT64, place) out = paddle.cast(out, dtype) + if pin_memory and in_dynamic_mode(): + out = out.pin_memory() + if requires_grad is True: + out.stop_gradient = False return out else: check_shape(shape, 'randint_like') diff --git a/python/paddle/testing/__init__.py b/python/paddle/testing/__init__.py index 5ab072e8cc33e7..cb2b69c384c76d 100644 --- a/python/paddle/testing/__init__.py +++ b/python/paddle/testing/__init__.py @@ -13,9 +13,8 @@ # limitations under the License. from __future__ import annotations -from ._comparison import _assert, assert_close +from ._comparison import _assert, assert_allclose, assert_close # noqa: F401 __all__ = [ - '_assert', 'assert_close', ] diff --git a/python/paddle/testing/_comparison.py b/python/paddle/testing/_comparison.py index d80b77886422a3..7a25511035b952 100644 --- a/python/paddle/testing/_comparison.py +++ b/python/paddle/testing/_comparison.py @@ -1103,3 +1103,73 @@ def _assert(condition, message=""): else: if not condition: raise AssertionError(message) + + +def assert_allclose( + actual: Any, + expected: Any, + rtol: float | None = None, + atol: float | None = None, + equal_nan: bool = True, + msg: str = "", +) -> None: + r""" + Asserts that ``actual`` and ``expected`` are close. + + .. warning:: + This API is deprecated. Please use ``paddle.testing.assert_allclose`` instead. + + If ``actual`` and ``expected`` are real-valued, and finite, they are considered close if + + .. math:: + + \lvert \text{actual} - \text{expected} \rvert \le \texttt{atol} + \texttt{rtol} \cdot \lvert \text{expected} \rvert + + Non-finite values (``-inf`` and ``inf``) are only considered close if and only if they are equal. + ``NaN``'s are only considered equal to each other if ``equal_nan`` is ``True``. + + Args: + actual (Any): The actual value. + expected (Any): The expected value. + rtol (float|None, optional): Relative tolerance. If None, uses default tolerances. + Default: None. + atol (float|None, optional): Absolute tolerance. If None, uses default tolerances. + Default: None. + equal_nan (bool, optional): If True, NaN values are considered equal. Default: True. + msg (str, optional): Custom error message. Default: "". + + Raises: + AssertionError: If ``actual`` and ``expected`` are not close. + + Examples: + .. code-block:: pycon + + >>> import paddle + >>> paddle.testing.assert_allclose(paddle.to_tensor([1.0]), paddle.to_tensor([1.0])) + """ + if not isinstance(actual, paddle.Tensor): + actual = paddle.to_tensor(actual) + if not isinstance(expected, paddle.Tensor): + expected = paddle.to_tensor(expected, dtype=actual.dtype) + + if rtol is None and atol is None: + rtol, atol = default_tolerances( + actual, + expected, + dtype_precisions={ + paddle.float16: (1e-3, 1e-3), + paddle.float32: (1e-4, 1e-5), + paddle.float64: (1e-5, 1e-8), + }, + ) + + assert_close( + actual, + expected, + rtol=rtol, + atol=atol, + equal_nan=equal_nan, + check_device=True, + check_dtype=False, + msg=msg or None, + ) diff --git a/python/paddle/utils/decorator_utils.py b/python/paddle/utils/decorator_utils.py index 87784ad7b2e07f..aceb185cb8a460 100644 --- a/python/paddle/utils/decorator_utils.py +++ b/python/paddle/utils/decorator_utils.py @@ -1391,3 +1391,92 @@ def wrapper(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT: return wrapper return decorator + + +def resize__decorator() -> Callable[ + [Callable[_InputT, _RetT]], Callable[_InputT, _RetT] +]: + """Decorator for resize_ to support PyTorch-style variable args (*sizes).""" + + def decorator(func: Callable[_InputT, _RetT]) -> Callable[_InputT, _RetT]: + @functools.wraps(func) + def wrapper(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT: + # Handle PyTorch-style variable args: x.resize_(2, 3, 4) -> x.resize_([2, 3, 4]) + kwargs.pop('memory_format', None) + if len(args) >= 2: + # args[0] is self (x), args[1:] are the sizes + x = args[0] + sizes = args[1:] + # Check if all sizes are integers (variable args mode) + if all(isinstance(s, int) for s in sizes): + kwargs['shape'] = list(sizes) + args = (x,) + return func(*args, **kwargs) + + return wrapper + + return decorator + + +def gru_decorator() -> Callable[ + [Callable[_InputT, _RetT]], Callable[_InputT, _RetT] +]: + """ + Dispatch decorator for ``GRU.__init__``. + + PyTorch's ``torch.nn.GRU`` places ``bias`` (``bool``) at the 4th + positional argument (index 4 when ``self`` is counted), while Paddle's + native signature places ``direction`` (``str``) at the same position. + When ``args[4]`` is a ``bool`` we interpret the call as the PyTorch + convention and remap positional arguments ``args[4:10]`` to keyword + arguments so the call succeeds against Paddle's signature. + + Usage Example: + PyTorch: torch.nn.GRU(input_size, hidden_size, num_layers=1, bias=True, batch_first=False, + dropout=0, bidirectional=False, device=None, dtype=None) + Paddle: paddle.nn.GRU(input_size, hidden_size, num_layers=1, direction='forward', + time_major=False, dropout=0, ...) + """ + + def decorator(func: Callable[_InputT, _RetT]) -> Callable[_InputT, _RetT]: + @functools.wraps(func) + def wrapper(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT: + # Detect PyTorch-style positional args: 4th param is bool (bias) + if len(args) >= 5 and isinstance(args[4], bool): + torch_names = ( + "bias", + "batch_first", + "dropout", + "bidirectional", + "device", + "dtype", + ) + for i, name in enumerate(torch_names): + pos = 4 + i + if pos >= len(args): + break + if name in kwargs: + raise TypeError( + f"__init__() got multiple values for argument '{name}'" + ) + kwargs[name] = args[pos] + args = args[:4] + + # Handle batch_first vs time_major (opposite meaning) + if "batch_first" in kwargs and "time_major" not in kwargs: + batch_first = kwargs.pop("batch_first") + kwargs["time_major"] = not batch_first + + # Handle bidirectional vs direction + if "bidirectional" in kwargs and "direction" not in kwargs: + bidirectional = kwargs.pop("bidirectional") + kwargs["direction"] = ( + "bidirectional" if bidirectional else "forward" + ) + + return func(*args, **kwargs) + + wrapper.__signature__ = inspect.signature(func) + return wrapper + + return decorator diff --git a/test/legacy_test/CMakeLists.txt b/test/legacy_test/CMakeLists.txt index 375043d569115b..990709f48e00b6 100644 --- a/test/legacy_test/CMakeLists.txt +++ b/test/legacy_test/CMakeLists.txt @@ -93,6 +93,9 @@ endif() if(WITH_XPU) list(REMOVE_ITEM TEST_OPS test_max_pool_dilation) +endif() + +if(WITH_XPU OR WITH_ROCM) list(REMOVE_ITEM TEST_OPS test_api_compatibility_part1) list(REMOVE_ITEM TEST_OPS test_api_compatibility_part2) list(REMOVE_ITEM TEST_OPS test_api_compatibility_part3) diff --git a/test/legacy_test/test_api_compatibility_part1.py b/test/legacy_test/test_api_compatibility_part1.py index 25f31d9115c496..1d0ca0cd2f3a16 100644 --- a/test/legacy_test/test_api_compatibility_part1.py +++ b/test/legacy_test/test_api_compatibility_part1.py @@ -1911,6 +1911,83 @@ def test_dygraph_InplaceCompatibility(self): paddle.enable_static() +# Test imag property compatibility (PyTorch-style property access) +class TestImagPropertyAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + np_x_real = np.random.randn(5, 6).astype('float32') + np_x_imag = np.random.randn(5, 6).astype('float32') + self.np_x = np_x_real + 1j * np_x_imag + + # will support future + def _test_dygraph_Compatibility(self): + """Test imag as property (PyTorch style: x.imag)""" + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # PyTorch style: property access + imag_result = x.imag + self.assertIsInstance(imag_result, paddle.Tensor) + np.testing.assert_allclose(imag_result.numpy(), np.imag(self.np_x)) + + paddle.enable_static() + + # will support future + def _test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=[3], dtype='complex64') + + out = x.imag + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out], + ) + np.testing.assert_allclose(fetches[0], np.imag(self.np_x)) + + +# Test real property compatibility (PyTorch-style property access) +class TestRealPropertyAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + np_x_real = np.random.randn(5, 6).astype('float32') + np_x_imag = np.random.randn(5, 6).astype('float32') + self.np_x = np_x_real + 1j * np_x_imag + + # will support future + def _test_dygraph_Compatibility(self): + """Test real as property (PyTorch style: x.real)""" + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + real_result = x.real + self.assertIsInstance(real_result, paddle.Tensor) + np.testing.assert_allclose(real_result.numpy(), np.real(self.np_x)) + + paddle.enable_static() + + # will support future + def _test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=[3], dtype='complex64') + out = x.real + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out], + ) + np.testing.assert_allclose(fetches[0], np.real(self.np_x)) + + # Test baddbmm API compatibility (paddle.baddbmm and paddle.Tensor.baddbmm) class TestBaddbmmAPI(unittest.TestCase): def setUp(self): diff --git a/test/legacy_test/test_api_compatibility_part3.py b/test/legacy_test/test_api_compatibility_part3.py index 7b79a8098953ae..633c7c4b7fd8ce 100644 --- a/test/legacy_test/test_api_compatibility_part3.py +++ b/test/legacy_test/test_api_compatibility_part3.py @@ -3972,6 +3972,245 @@ def test_dygraph_InplaceInput(self): paddle.enable_static() +class TestInvertPermutationAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + perm = paddle.to_tensor([2, 0, 1]) + + # 1. Paddle positional arguments + out1 = paddle.nn.utils.rnn.invert_permutation(perm) + # 2. Paddle keyword arguments + out2 = paddle.nn.utils.rnn.invert_permutation(permutation=perm) + # 3. None input + out3 = paddle.nn.utils.rnn.invert_permutation(None) + + expected = np.array([1, 2, 0]) + np.testing.assert_array_equal(out1.numpy(), expected) + np.testing.assert_array_equal(out2.numpy(), expected) + self.assertIsNone(out3) + + paddle.enable_static() + + +class TestPackPaddedSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_seq = np.array( + [[4, 5, 6], [1, 2, 0], [3, 0, 0]], dtype=np.float32 + ) + self.lengths = [3, 2, 1] + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq = paddle.to_tensor(self.np_seq) + lengths = paddle.to_tensor(self.lengths) + + # 1. Paddle positional arguments + out1 = paddle.nn.utils.rnn.pack_padded_sequence( + seq, lengths, batch_first=True + ) + # 2. Paddle keyword arguments + out2 = paddle.nn.utils.rnn.pack_padded_sequence( + input=seq, lengths=lengths, batch_first=True + ) + # 3. Mixed arguments + out3 = paddle.nn.utils.rnn.pack_padded_sequence( + seq, lengths, batch_first=True + ) + # 4. enforce_sorted=False + out4 = paddle.nn.utils.rnn.pack_padded_sequence( + seq, lengths, batch_first=True, enforce_sorted=False + ) + + expected_data = np.array( + [4.0, 1.0, 3.0, 5.0, 2.0, 6.0], dtype=np.float32 + ) + expected_batch_sizes = np.array([3, 2, 1], dtype=np.int64) + + for out in [out1, out2, out3]: + np.testing.assert_allclose(out.data.numpy(), expected_data) + np.testing.assert_array_equal( + out.batch_sizes.numpy(), expected_batch_sizes + ) + + paddle.enable_static() + + +class TestPadPackedSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_seq = np.array( + [[4, 5, 6], [1, 2, 0], [3, 0, 0]], dtype=np.float32 + ) + self.lengths = [3, 2, 1] + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq = paddle.to_tensor(self.np_seq) + lengths = paddle.to_tensor(self.lengths) + + packed = paddle.nn.utils.rnn.pack_padded_sequence( + seq, lengths, batch_first=True + ) + + # 1. Paddle positional arguments + out1, lengths1 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, batch_first=True + ) + # 2. Paddle keyword arguments + out2, lengths2 = paddle.nn.utils.rnn.pad_packed_sequence( + sequence=packed, batch_first=True + ) + # 3. Mixed arguments + out3, lengths3 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, batch_first=True, padding_value=0.0 + ) + + for out, lens in [(out1, lengths1), (out2, lengths2), (out3, lengths3)]: + np.testing.assert_allclose(out.numpy(), self.np_seq) + np.testing.assert_array_equal( + lens.numpy(), np.array(self.lengths, dtype=np.int64) + ) + + paddle.enable_static() + + +class TestPadSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.seq1 = np.random.rand(5, 10).astype(np.float32) + self.seq2 = np.random.rand(3, 10).astype(np.float32) + self.seq3 = np.random.rand(2, 10).astype(np.float32) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq1 = paddle.to_tensor(self.seq1) + seq2 = paddle.to_tensor(self.seq2) + seq3 = paddle.to_tensor(self.seq3) + sequences = [seq1, seq2, seq3] + + # 1. Paddle positional arguments + out1 = paddle.nn.utils.rnn.pad_sequence(sequences) + # 2. Paddle keyword arguments + out2 = paddle.nn.utils.rnn.pad_sequence(sequences=sequences) + # 3. batch_first=True + out3 = paddle.nn.utils.rnn.pad_sequence(sequences, batch_first=True) + # 4. padding_value + out4 = paddle.nn.utils.rnn.pad_sequence(sequences, padding_value=-1.0) + # 5. padding_side='left' + out5 = paddle.nn.utils.rnn.pad_sequence(sequences, padding_side='left') + + self.assertEqual(out1.shape, [5, 3, 10]) + self.assertEqual(out3.shape, [3, 5, 10]) + np.testing.assert_allclose(out1.numpy(), out2.numpy()) + + paddle.enable_static() + + +class TestUnpadSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.seq1 = np.random.rand(5, 10).astype(np.float32) + self.seq2 = np.random.rand(3, 10).astype(np.float32) + self.seq3 = np.random.rand(2, 10).astype(np.float32) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq1 = paddle.to_tensor(self.seq1) + seq2 = paddle.to_tensor(self.seq2) + seq3 = paddle.to_tensor(self.seq3) + sequences = [seq1, seq2, seq3] + + padded = paddle.nn.utils.rnn.pad_sequence(sequences) + lengths = paddle.to_tensor([5, 3, 2]) + + # 1. Paddle positional arguments + out1 = paddle.nn.utils.rnn.unpad_sequence(padded, lengths) + # 2. Paddle keyword arguments + out2 = paddle.nn.utils.rnn.unpad_sequence( + padded_sequences=padded, lengths=lengths + ) + # 3. batch_first=True + padded_bf = paddle.nn.utils.rnn.pad_sequence( + sequences, batch_first=True + ) + out3 = paddle.nn.utils.rnn.unpad_sequence( + padded_bf, lengths, batch_first=True + ) + + for i, seq in enumerate(sequences): + np.testing.assert_allclose(out1[i].numpy(), seq.numpy()) + np.testing.assert_allclose(out2[i].numpy(), seq.numpy()) + np.testing.assert_allclose(out3[i].numpy(), seq.numpy()) + + paddle.enable_static() + + +class TestPackSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.seq1 = np.array([1, 2, 3], dtype=np.float32) + self.seq2 = np.array([4, 5], dtype=np.float32) + self.seq3 = np.array([6], dtype=np.float32) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq1 = paddle.to_tensor(self.seq1) + seq2 = paddle.to_tensor(self.seq2) + seq3 = paddle.to_tensor(self.seq3) + sequences = [seq1, seq2, seq3] + + # 1. Paddle positional arguments + out1 = paddle.nn.utils.rnn.pack_sequence(sequences) + # 2. Paddle keyword arguments + out2 = paddle.nn.utils.rnn.pack_sequence(sequences=sequences) + # 3. enforce_sorted=False + out3 = paddle.nn.utils.rnn.pack_sequence( + sequences, enforce_sorted=False + ) + + expected_data = np.array( + [1.0, 4.0, 6.0, 2.0, 5.0, 3.0], dtype=np.float32 + ) + expected_batch_sizes = np.array([3, 2, 1], dtype=np.int64) + + np.testing.assert_allclose(out1.data.numpy(), expected_data) + np.testing.assert_array_equal( + out1.batch_sizes.numpy(), expected_batch_sizes + ) + np.testing.assert_allclose(out2.data.numpy(), expected_data) + + paddle.enable_static() + + +class TestUnpackSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.seq1 = np.array([1, 2, 3], dtype=np.float32) + self.seq2 = np.array([4, 5], dtype=np.float32) + self.seq3 = np.array([6], dtype=np.float32) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq1 = paddle.to_tensor(self.seq1) + seq2 = paddle.to_tensor(self.seq2) + seq3 = paddle.to_tensor(self.seq3) + sequences = [seq1, seq2, seq3] + + packed = paddle.nn.utils.rnn.pack_sequence(sequences) + + # 1. Paddle positional arguments + out1 = paddle.nn.utils.rnn.unpack_sequence(packed) + # 2. Paddle keyword arguments + out2 = paddle.nn.utils.rnn.unpack_sequence(packed_sequences=packed) + + for i, seq in enumerate(sequences): + np.testing.assert_allclose(out1[i].numpy(), seq) + np.testing.assert_allclose(out2[i].numpy(), seq) + + paddle.enable_static() + + class TestSquareInplaceAPI(unittest.TestCase): def setUp(self): self.np_x = np.array([-0.7, -0.2, 0.3, 0.9], dtype="float32") diff --git a/test/legacy_test/test_api_compatibility_part5.py b/test/legacy_test/test_api_compatibility_part5.py index e4295fd976add8..51c730a4202825 100644 --- a/test/legacy_test/test_api_compatibility_part5.py +++ b/test/legacy_test/test_api_compatibility_part5.py @@ -228,16 +228,24 @@ def test_dygraph_Compatibility(self): x = paddle.to_tensor(self.np_x) indices = paddle.to_tensor(self.np_indices) - # 1. Paddle positional arguments + # 1. Paddle Positional arguments out1 = paddle.take(x, indices) + # 2. Paddle keyword arguments out2 = paddle.take(x=x, index=indices) - # 3. Tensor method - out3 = x.take(indices) - # Verify outputs + # 3. PyTorch keyword arguments (alias) + out3 = paddle.take(input=x, index=indices) + + # 4. Mixed arguments + out4 = paddle.take(x, index=indices) + + # 5. Tensor method - args + out5 = x.take(indices) + + # Verify all outputs expected = self.np_x[self.np_indices] - for out in [out1, out2, out3]: + for out in [out1, out2, out3, out4, out5]: np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) paddle.enable_static() @@ -256,8 +264,12 @@ def test_static_Compatibility(self): dtype=str(self.np_indices.dtype), ) + # 1. Paddle Positional arguments out1 = paddle.take(x, indices) + # 2. Paddle keyword arguments out2 = paddle.take(x=x, index=indices) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.take(input=x, index=indices) exe = paddle.static.Executor() fetches = exe.run( @@ -266,7 +278,7 @@ def test_static_Compatibility(self): "x": self.np_x, "indices": self.np_indices, }, - fetch_list=[out1, out2], + fetch_list=[out1, out2, out3], ) expected = self.np_x[self.np_indices] @@ -386,5 +398,2604 @@ def test_dygraph_Compatibility(self): paddle.enable_static() +# Test ParameterList compatibility (values -> parameters alias) +class TestParameterListAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Paddle keyword arguments (parameters) + params1 = [ + paddle.create_parameter(shape=[2, 3], dtype='float32') + for _ in range(3) + ] + pl1 = paddle.nn.ParameterList(parameters=params1) + + # 2. PyTorch keyword arguments (values alias) + params2 = [ + paddle.create_parameter(shape=[2, 3], dtype='float32') + for _ in range(3) + ] + pl2 = paddle.nn.ParameterList(values=params2) + + # 3. PyTorch positional arguments + params3 = [ + paddle.create_parameter(shape=[2, 3], dtype='float32') + for _ in range(3) + ] + pl3 = paddle.nn.ParameterList(params3) + + # 4. Mixed arguments + params4 = [ + paddle.create_parameter(shape=[2, 3], dtype='float32') + for _ in range(3) + ] + pl4 = paddle.nn.ParameterList(params4) + + # 5. Test append with value alias + pl5 = paddle.nn.ParameterList() + param = paddle.create_parameter(shape=[2, 3], dtype='float32') + pl5.append(value=param) + + # 6. Test extend with parameters alias + pl6 = paddle.nn.ParameterList() + params6 = [ + paddle.create_parameter(shape=[2, 3], dtype='float32') + for _ in range(2) + ] + pl6.extend(parameters=params6) + + # Verify lengths + self.assertEqual(len(pl1), 3) + self.assertEqual(len(pl2), 3) + self.assertEqual(len(pl3), 3) + self.assertEqual(len(pl4), 3) + self.assertEqual(len(pl5), 1) + self.assertEqual(len(pl6), 2) + + paddle.enable_static() + + +# Test scatter_reduce_ compatibility (inplace) +class TestScatterReduce_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([[10, 20, 30], [40, 50, 60]]).astype("float32") + self.np_index = np.zeros((2, 3)).astype("int64") + self.np_src = np.array([[1, 2, 3], [4, 5, 6]]).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Paddle scatter_reduce_ positional + x1 = paddle.to_tensor(self.np_x.copy()) + index = paddle.to_tensor(self.np_index) + src = paddle.to_tensor(self.np_src) + out1 = x1.scatter_reduce_(0, index, src, "sum", include_self=True) + + # 2. Paddle keyword arguments + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = x2.scatter_reduce_( + dim=0, index=index, src=src, reduce="sum", include_self=True + ) + + # 3. PyTorch keyword arguments (alias) + # Note: src is alias for src in Paddle too, but let's check + x3 = paddle.to_tensor(self.np_x.copy()) + out3 = x3.scatter_reduce_(dim=0, index=index, src=src, reduce="sum") + + # 4. Mixed arguments + x4 = paddle.to_tensor(self.np_x.copy()) + out4 = x4.scatter_reduce_(0, index, src=src, reduce="sum") + + # Verify inplace operation returns self + self.assertIs(out1, x1) + self.assertIs(out2, x2) + self.assertIs(out3, x3) + self.assertIs(out4, x4) + + # Verify results + self.assertEqual(out1.shape, [2, 3]) + np.testing.assert_allclose(out1.numpy(), out2.numpy()) + + paddle.enable_static() + + +# Test xavier_uniform compatibility (alias for xavier_uniform_) +class TestXavierUniformAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. paddle.nn.init.xavier_uniform_ (with underscore) + x1 = paddle.empty([3, 4], dtype='float32') + paddle.nn.init.xavier_uniform_(x1, gain=1.0) + + # 2. paddle.nn.init.xavier_uniform (without underscore, PyTorch deprecated alias) + x2 = paddle.empty([3, 4], dtype='float32') + paddle.nn.init.xavier_uniform(x2, gain=1.0) + + # 3. PyTorch keyword arguments (tensor alias for x) + x3 = paddle.empty([3, 4], dtype='float32') + paddle.nn.init.xavier_uniform(tensor=x3, gain=1.0) + + # 4. Mixed arguments + x4 = paddle.empty([3, 4], dtype='float32') + paddle.nn.init.xavier_uniform(x4, gain=1.0) + + # Both should work the same + self.assertEqual(x1.shape, x2.shape) + self.assertEqual(x1.shape, x3.shape) + self.assertEqual(x1.shape, x4.shape) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x1 = paddle.static.data(name="x1", shape=[3, 4], dtype="float32") + x2 = paddle.static.data(name="x2", shape=[3, 4], dtype="float32") + + # 1. paddle.nn.init.xavier_uniform_ (with underscore) + paddle.nn.init.xavier_uniform_(x1) + + # 2. paddle.nn.init.xavier_uniform (without underscore, PyTorch deprecated alias) + paddle.nn.init.xavier_uniform(x2) + + # Just verify it doesn't crash + self.assertIsNotNone(x1) + self.assertIsNotNone(x2) + + paddle.disable_static() + + +# Test sign_ compatibility (inplace) +class TestSign_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([-2.0, -1.0, 0.0, 1.0, 2.0]).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Tensor method - args + x1 = paddle.to_tensor(self.np_x.copy()) + out1 = x1.sign_() + + # 2. Paddle function - positional + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = paddle.sign_(x2) + + # 3. Paddle function - keyword + x3 = paddle.to_tensor(self.np_x.copy()) + out3 = paddle.sign_(x=x3) + + # 4. PyTorch function - keyword (input alias) + x4 = paddle.to_tensor(self.np_x.copy()) + out4 = paddle.sign_(input=x4) + + # Verify all outputs + expected = np.sign(self.np_x) + for out in [out1, out2, out3, out4]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + paddle.enable_static() + + def test_static_pir_infer_symbolic_shape(self): + from paddle.base.libpaddle import pir + + with paddle.pir_utils.IrGuard(): + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=[5], dtype="float32") + out = paddle.sign_(x) + + pm = pir.PassManager() + pir.infer_symbolic_shape_pass(pm, main) + pm.run(main) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x.copy()}, + fetch_list=[out], + ) + out_ref = np.sign(self.np_x) + np.testing.assert_allclose(fetches[0], out_ref, rtol=1e-5) + + +# Test linalg.pinv compatibility (atol, rtol, out parameters) +class TestLinalgPinvAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(3, 5).astype("float32") + self.shape = [3, 5] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. Paddle positional arguments (using rcond) + out1 = paddle.linalg.pinv(x, rcond=1e-15) + + # 2. Paddle keyword arguments + out2 = paddle.linalg.pinv(x=x, rcond=1e-15, hermitian=False) + + # 3. PyTorch keyword arguments (input alias) + out3 = paddle.linalg.pinv(input=x) + + # 4. PyTorch keyword arguments (rtol alias) + out4 = paddle.linalg.pinv(x, rtol=1e-15) + + # 5. Mixed arguments (atol, rtol) + out5 = paddle.linalg.pinv(x, atol=1e-10, rtol=1e-10) + + # 6. out parameter test + out6 = paddle.empty([5, 3], dtype='float32') + paddle.linalg.pinv(x, out=out6) + + # 7. Tensor method - args + out7 = x.pinverse() + + # 8. Alias paddle.pinverse + out8 = paddle.pinverse(x) + + # 9. hermitian=True with atol (need square matrix for hermitian) + x_sq = paddle.to_tensor(np.random.rand(3, 3).astype("float32")) + x_sym = x_sq @ x_sq.T + paddle.eye(3) * 0.5 # full rank + out9 = paddle.linalg.pinv(x_sym, hermitian=True, atol=1e-4) + + # 10. hermitian=True with rtol + out10 = paddle.linalg.pinv(x_sym, hermitian=True, rtol=1e-4) + + # 11. hermitian=True with both atol and rtol + out11 = paddle.linalg.pinv(x_sym, hermitian=True, atol=1e-4, rtol=1e-4) + + # Verify all outputs + expected = np.linalg.pinv(self.np_x) + for out in [out1, out2, out3, out4, out5, out6, out7, out8]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + expected_sym = np.linalg.pinv(x_sym.numpy(), hermitian=True) + for out in [out9, out10, out11]: + np.testing.assert_allclose(out.numpy(), expected_sym, rtol=1e-5) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.linalg.pinv(x) + # 2. Paddle keyword arguments + out2 = paddle.linalg.pinv(x=x) + # 3. PyTorch keyword arguments (input alias) + out3 = paddle.linalg.pinv(input=x) + # 4. Tensor method - args + out4 = x.pinverse() + # 5. Alias paddle.pinverse + out5 = paddle.pinverse(x) + + # 6. atol parameter test (keyword-only) + out6 = paddle.linalg.pinv(x, atol=1e-10) + + # 7. rtol parameter test (keyword-only) + out7 = paddle.linalg.pinv(x, rtol=1e-10) + + # 8. atol and rtol combined (keyword-only) + out8 = paddle.linalg.pinv(x, atol=1e-10, rtol=1e-10) + + # 9. out parameter test + out9 = paddle.static.data( + name="out9", shape=[5, 3], dtype="float32" + ) + paddle.linalg.pinv(x, out=out9) + + # 10. hermitian=True with atol (square matrix) + x_sym = paddle.static.data( + name="x_sym", shape=[3, 3], dtype="float32" + ) + out10 = paddle.linalg.pinv(x_sym, hermitian=True, atol=1e-4) + + # 11. hermitian=True with rtol + out11 = paddle.linalg.pinv(x_sym, hermitian=True, rtol=1e-4) + + # 12. hermitian=True with both atol and rtol + out12 = paddle.linalg.pinv( + x_sym, hermitian=True, atol=1e-4, rtol=1e-4 + ) + + exe = paddle.static.Executor() + np_x_sym = ( + self.np_x @ self.np_x.T + + np.eye(3, dtype=self.dtype) * 0.5 # full rank + ) + fetches = exe.run( + main, + feed={ + "x": self.np_x, + "out9": np.empty([5, 3], dtype="float32"), + "x_sym": np_x_sym, + }, + fetch_list=[ + out1, + out2, + out3, + out4, + out5, + out6, + out7, + out8, + out9, + out10, + out11, + out12, + ], + ) + + expected = np.linalg.pinv(self.np_x) + for out in fetches[:9]: + np.testing.assert_allclose(out, expected, rtol=1e-5) + + expected_sym = np.linalg.pinv(np_x_sym, hermitian=True) + for out in fetches[9:]: + np.testing.assert_allclose(out, expected_sym, rtol=1e-5) + + paddle.disable_static() + + +# Test nll_loss compatibility (target -> label alias) +class TestNllLossAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_input = np.random.rand(5, 3).astype("float32") + self.np_label = np.array([0, 2, 1, 1, 0], dtype="int64") + self.shape_input = [5, 3] + self.shape_label = [5] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + log_softmax = paddle.nn.LogSoftmax(axis=1) + input = log_softmax(paddle.to_tensor(self.np_input)) + label = paddle.to_tensor(self.np_label) + + # 1. Paddle positional arguments + out1 = paddle.nn.functional.nll_loss(input, label) + + # 2. Paddle keyword arguments + out2 = paddle.nn.functional.nll_loss(input=input, label=label) + + # 3. PyTorch keyword arguments (target alias) + out3 = paddle.nn.functional.nll_loss(input=input, target=label) + + # 4. Mixed arguments + out4 = paddle.nn.functional.nll_loss( + input, target=label, reduction='mean' + ) + + # Verify all outputs + for out in [out1, out2, out3, out4]: + np.testing.assert_allclose(out.numpy(), out1.numpy(), rtol=1e-5) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + input = paddle.static.data( + name="input", shape=self.shape_input, dtype=self.dtype + ) + label = paddle.static.data( + name="label", shape=self.shape_label, dtype="int64" + ) + + # 1. Paddle positional arguments + out1 = paddle.nn.functional.nll_loss(input, label) + # 2. Paddle keyword arguments + out2 = paddle.nn.functional.nll_loss(input=input, label=label) + # 3. PyTorch keyword arguments (target alias) + out3 = paddle.nn.functional.nll_loss(input=input, target=label) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"input": self.np_input, "label": self.np_label}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + self.assertEqual(out.shape, ()) + + paddle.disable_static() + + +# Test bernoulli_ compatibility (inplace) +class TestBernoulli_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(3, 4).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Tensor method - args + x1 = paddle.to_tensor(self.np_x.copy()) + out1 = x1.bernoulli_() + + # 2. Tensor method - kwargs + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = x2.bernoulli_(p=0.3) + + # 3. Paddle function - positional + x3 = paddle.to_tensor(self.np_x.copy()) + out3 = paddle.bernoulli_(x3, p=0.5) + + # 4. Paddle function - keyword + x4 = paddle.to_tensor(self.np_x.copy()) + out4 = paddle.bernoulli_(x=x4, p=0.5) + + # Verify inplace operation returns self + self.assertIs(out1, x1) + self.assertIs(out2, x2) + self.assertIs(out3, x3) + self.assertIs(out4, x4) + + # Verify output contains only 0s and 1s + for out in [out1, out2, out3, out4]: + self.assertTrue(paddle.all((out == 0) | (out == 1)).item()) + + paddle.enable_static() + + +# Test kl_div compatibility (target -> label alias) +class TestKlDivAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_input = np.random.rand(5, 10).astype("float32") + self.np_target = np.random.rand(5, 10).astype("float32") + self.shape_input = [5, 10] + self.shape_target = [5, 10] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + input = paddle.to_tensor(self.np_input) + target = paddle.to_tensor(self.np_target) + + # 1. Paddle positional arguments + out1 = paddle.nn.functional.kl_div(input, target) + + # 2. Paddle keyword arguments + out2 = paddle.nn.functional.kl_div(input=input, label=target) + + # 3. PyTorch keyword arguments (target alias) + out3 = paddle.nn.functional.kl_div(input=input, target=target) + + # 4. Mixed arguments + out4 = paddle.nn.functional.kl_div( + input, target=target, reduction='mean' + ) + + # Verify all outputs + for out in [out1, out2, out3, out4]: + np.testing.assert_allclose(out.numpy(), out1.numpy(), rtol=1e-5) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + input = paddle.static.data( + name="input", shape=self.shape_input, dtype=self.dtype + ) + target = paddle.static.data( + name="target", shape=self.shape_target, dtype=self.dtype + ) + + # 1. Paddle positional arguments + out1 = paddle.nn.functional.kl_div(input, target) + # 2. Paddle keyword arguments + out2 = paddle.nn.functional.kl_div(input=input, label=target) + # 3. PyTorch keyword arguments (target alias) + out3 = paddle.nn.functional.kl_div(input=input, target=target) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"input": self.np_input, "target": self.np_target}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + self.assertEqual(out.shape, ()) + + paddle.disable_static() + + +# Test hann_window compatibility +class TestHannWindowAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Paddle Positional arguments + out1 = paddle.hann_window(512) + + # 2. Paddle keyword arguments + out2 = paddle.hann_window(window_length=512, periodic=True) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.hann_window(window_length=512, periodic=False) + + # 4. Mixed arguments + out4 = paddle.hann_window(512, periodic=True) + + # Verify all outputs + for out in [out1, out2, out3, out4]: + self.assertEqual(out.shape, [512]) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + # 1. Paddle Positional arguments + out1 = paddle.hann_window(512) + # 2. Paddle keyword arguments + out2 = paddle.hann_window(window_length=512) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.hann_window(window_length=512, periodic=True) + + exe = paddle.static.Executor() + fetches = exe.run(main, feed={}, fetch_list=[out1, out2, out3]) + + for out in fetches: + self.assertEqual(out.shape, (512,)) + + paddle.disable_static() + + +# Test paddle.float compatibility (dtype alias) +class TestFloatDtypeAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. paddle.float should be float32 + self.assertEqual(paddle.float, paddle.float32) + + # 2. Create tensor with paddle.float dtype + x = paddle.to_tensor([1.0, 2.0], dtype=paddle.float) + self.assertEqual(x.dtype, paddle.float32) + + # 3. Use in create_parameter + param = paddle.create_parameter(shape=[2, 3], dtype=paddle.float) + self.assertEqual(param.dtype, paddle.float32) + + paddle.enable_static() + + +# Test fmod_ compatibility (inplace) +class TestFmod_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([5.0, 7.0, 9.0]).astype("float32") + self.np_y = np.array([2.0, 3.0, 4.0]).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + y = paddle.to_tensor(self.np_y) + + # 1. Tensor method - positional + x1 = paddle.to_tensor(self.np_x.copy()) + out1 = x1.fmod_(y) + + # 2. Tensor method - keyword + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = x2.fmod_(other=y) + + # 3. paddle function - positional + x3 = paddle.to_tensor(self.np_x.copy()) + out3 = paddle.fmod_(x3, y) + + # 4. paddle function - keyword (input alias) + x4 = paddle.to_tensor(self.np_x.copy()) + out4 = paddle.fmod_(input=x4, other=y) + + # 5. Mixed arguments + x5 = paddle.to_tensor(self.np_x.copy()) + out5 = paddle.fmod_(x5, other=y) + + # Verify inplace operation returns self + self.assertIs(out1, x1) + self.assertIs(out2, x2) + self.assertIs(out3, x3) + self.assertIs(out4, x4) + self.assertIs(out5, x5) + + # Verify result + expected = np.fmod(self.np_x, self.np_y) + for out in [out1, out2, out3, out4, out5]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + paddle.enable_static() + + +# Test fill_diagonal_ compatibility (inplace) +class TestFillDiagonal_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.ones((4, 3)).astype("float32") * 2 + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Tensor method - positional + x1 = paddle.to_tensor(self.np_x.copy()) + out1 = x1.fill_diagonal_(1.0) + + # 2. Tensor method - keyword + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = x2.fill_diagonal_(fill_value=1.0) + + # 3. Mixed arguments + x3 = paddle.to_tensor(self.np_x.copy()) + out3 = x3.fill_diagonal_(1.0, wrap=False) + + # Verify inplace operation returns self + self.assertIs(out1, x1) + self.assertIs(out2, x2) + self.assertIs(out3, x3) + + # Verify all outputs + for out in [out1, out2, out3]: + self.assertEqual(out[0, 0].item(), 1.0) + self.assertEqual(out[1, 1].item(), 1.0) + self.assertEqual(out[2, 2].item(), 1.0) + + paddle.enable_static() + + +# Test weight_norm compatibility (module -> layer alias) +class TestWeightNormAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # Create a simple layer + conv = paddle.nn.Conv2D(3, 5, 3) + + # 1. Paddle keyword arguments + wn1 = paddle.nn.utils.weight_norm(layer=conv) + + # 2. PyTorch keyword arguments (module alias) + conv2 = paddle.nn.Conv2D(3, 5, 3) + wn2 = paddle.nn.utils.weight_norm(module=conv2) + + # 3. Paddle Positional arguments + conv3 = paddle.nn.Conv2D(3, 5, 3) + wn3 = paddle.nn.utils.weight_norm(conv3) + + # Verify all work correctly + self.assertIsNotNone(wn1.weight_g) + self.assertIsNotNone(wn2.weight_g) + self.assertIsNotNone(wn3.weight_g) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + # 1. Paddle Positional arguments + conv1 = paddle.nn.Conv2D(3, 5, 3) + wn1 = paddle.nn.utils.weight_norm(conv1) + # 2. PyTorch keyword arguments (module alias) + conv2 = paddle.nn.Conv2D(3, 5, 3) + wn2 = paddle.nn.utils.weight_norm(module=conv2) + + # Just verify it doesn't crash in static graph definition + self.assertIsNotNone(wn1) + self.assertIsNotNone(wn2) + + paddle.disable_static() + + +# Test resize_ compatibility (variable args support) +class TestResize_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x.copy()) + + # 1. Paddle list/tuple argument + out1 = x.resize_([2, 3]) + + # 2. PyTorch variable args + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = x2.resize_(2, 3) + + # Verify both produce same shape + self.assertEqual(out1.shape, [2, 3]) + self.assertEqual(out2.shape, [2, 3]) + + paddle.enable_static() + + +# Test Flatten compatibility (start_dim/end_dim -> start_axis/stop_axis) +class TestFlattenAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(2, 3, 4, 5).astype("float32") + self.shape = (2, 3, 4, 5) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. Paddle keyword arguments + layer1 = paddle.nn.Flatten(start_axis=1, stop_axis=-1) + out1 = layer1(x) + + # 2. PyTorch keyword arguments (dim aliases) + layer2 = paddle.nn.Flatten(start_dim=1, end_dim=-1) + out2 = layer2(x) + + # 3. PyTorch positional arguments + layer3 = paddle.nn.Flatten(1, -1) + out3 = layer3(x) + + # 4. Mixed arguments + layer4 = paddle.nn.Flatten(start_dim=1, stop_axis=-1) + out4 = layer4(x) + + # Verify all outputs + for out in [out1, out2, out3, out4]: + self.assertEqual(out.shape, [2, 60]) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype="float32") + + # 1. Paddle keyword arguments + layer1 = paddle.nn.Flatten(start_axis=1, stop_axis=-1) + out1 = layer1(x) + + # 2. PyTorch keyword arguments (dim aliases) + layer2 = paddle.nn.Flatten(start_dim=1, end_dim=-1) + out2 = layer2(x) + + # 3. PyTorch positional arguments + layer3 = paddle.nn.Flatten(1, -1) + out3 = layer3(x) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + self.assertEqual(out.shape, (2, 60)) + + paddle.disable_static() + + +# Test L1Loss compatibility (size_average/reduce parameters) +class TestL1LossAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_input = np.random.rand(3, 5).astype("float32") + self.np_label = np.random.rand(3, 5).astype("float32") + self.shape = (3, 5) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + input = paddle.to_tensor(self.np_input) + label = paddle.to_tensor(self.np_label) + + # 1. Paddle keyword arguments + loss1 = paddle.nn.L1Loss(reduction='mean') + out1 = loss1(input, label) + + # 2. PyTorch keyword arguments (size_average, reduce) + loss2 = paddle.nn.L1Loss(size_average=True, reduce=True) + out2 = loss2(input, label) + + # 3. PyTorch size_average=False + loss3 = paddle.nn.L1Loss(size_average=False, reduce=True) + out3 = loss3(input, label) + + # 4. PyTorch reduce=False + loss4 = paddle.nn.L1Loss(reduce=False) + out4 = loss4(input, label) + + # 5. Mixed arguments + loss5 = paddle.nn.L1Loss(size_average=True, reduction='mean') + out5 = loss5(input, label) + + # Verify all outputs + self.assertEqual(out1.shape, []) + self.assertEqual(out2.shape, []) + self.assertEqual(out3.shape, []) + self.assertEqual(out4.shape, [3, 5]) + self.assertEqual(out5.shape, []) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + input = paddle.static.data( + name="input", shape=self.shape, dtype="float32" + ) + label = paddle.static.data( + name="label", shape=self.shape, dtype="float32" + ) + + # 1. Paddle keyword arguments + loss1 = paddle.nn.L1Loss(reduction='mean') + out1 = loss1(input, label) + + # 2. PyTorch keyword arguments (size_average, reduce) + loss2 = paddle.nn.L1Loss(size_average=True, reduce=True) + out2 = loss2(input, label) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"input": self.np_input, "label": self.np_label}, + fetch_list=[out1, out2], + ) + + for out in fetches: + self.assertEqual(out.shape, ()) + + paddle.disable_static() + + +# Test linalg.inv compatibility (A -> x alias) +class TestLinalgInvAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(2, 2).astype("float32") + # Make it invertible + self.np_x = (np.eye(2) + 0.1 * self.np_x).astype("float32") + self.shape = [2, 2] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. Paddle Positional arguments + out1 = paddle.linalg.inv(x) + + # 2. Paddle keyword arguments + out2 = paddle.linalg.inv(x=x) + + # 3. PyTorch keyword arguments (A alias) + out3 = paddle.linalg.inv(A=x) + + # 4. Mixed arguments + out4 = paddle.linalg.inv(x, name=None) + + # 5. out parameter test + out5 = paddle.empty_like(x) + paddle.linalg.inv(x, out=out5) + + # 6. Tensor method - args + out6 = x.inverse() + + # 7. Alias paddle.inverse + out7 = paddle.inverse(A=x) + + # Verify all outputs + expected = np.linalg.inv(self.np_x) + for out in [out1, out2, out3, out4, out5, out6, out7]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.linalg.inv(x) + # 2. Paddle keyword arguments + out2 = paddle.linalg.inv(x=x) + # 3. PyTorch keyword arguments (A alias) + out3 = paddle.linalg.inv(A=x) + # 4. Tensor method - args + out4 = x.inverse() + # 5. Alias paddle.inverse + out5 = paddle.inverse(A=x) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out1, out2, out3, out4, out5], + ) + + expected = np.linalg.inv(self.np_x) + for out in fetches: + np.testing.assert_allclose(out, expected, rtol=1e-5) + + paddle.disable_static() + + +# Test det compatibility (paddle.det alias) +class TestDetAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(2, 2).astype("float32") + self.shape = [2, 2] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. Paddle Positional arguments + out1 = paddle.linalg.det(x) + + # 2. Paddle keyword arguments + out2 = paddle.linalg.det(x=x) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.linalg.det(input=x) + + # 4. Mixed arguments + out4 = paddle.linalg.det(x, name=None) + + # 5. Tensor method - args + out5 = x.det() + + # Verify all outputs + expected = np.linalg.det(self.np_x) + for out in [out1, out2, out3, out4, out5]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.linalg.det(x) + # 2. Paddle keyword arguments + out2 = paddle.linalg.det(x=x) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.linalg.det(input=x) + # 4. Tensor method - args + out4 = x.det() + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out1, out2, out3, out4], + ) + + expected = np.linalg.det(self.np_x) + for out in fetches: + np.testing.assert_allclose(out, expected, rtol=1e-5) + + paddle.disable_static() + + +# Test pinverse compatibility (paddle.pinverse alias) +class TestPinverseAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(3, 2).astype("float32") + self.shape = [3, 2] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. paddle.pinverse positional + out1 = paddle.pinverse(x) + + # 2. paddle.pinverse keyword + out2 = paddle.pinverse(x=x) + + # 3. paddle.pinverse with input alias + out3 = paddle.pinverse(input=x) + + # 4. out parameter test + out4 = paddle.empty([2, 3], dtype='float32') + paddle.pinverse(x, out=out4) + + # 5. atol parameter test (keyword-only) + out5 = paddle.pinverse(x, atol=1e-10) + + # 6. rtol parameter test (keyword-only) + out6 = paddle.pinverse(x, rtol=1e-10) + + # 7. atol and rtol combined (keyword-only) + out7 = paddle.pinverse(x, atol=1e-10, rtol=1e-10) + + # 8. Tensor method - args + out8 = x.pinverse() + + # 9. hermitian=True with atol (need square matrix for hermitian) + x_sq = paddle.to_tensor(np.random.rand(3, 3).astype("float32")) + x_sym = x_sq @ x_sq.T + paddle.eye(3) * 0.5 # full rank + out9 = paddle.pinverse(x_sym, hermitian=True, atol=1e-4) + + # 10. hermitian=True with rtol + out10 = paddle.pinverse(x_sym, hermitian=True, rtol=1e-4) + + # 11. hermitian=True with both atol and rtol + out11 = paddle.pinverse(x_sym, hermitian=True, atol=1e-4, rtol=1e-4) + + # Verify all outputs + expected = np.linalg.pinv(self.np_x) + for out in [out1, out2, out3, out4, out5, out6, out7, out8]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + expected_sym = np.linalg.pinv(x_sym.numpy(), hermitian=True) + for out in [out9, out10, out11]: + np.testing.assert_allclose(out.numpy(), expected_sym, rtol=1e-5) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. paddle.pinverse positional + out1 = paddle.pinverse(x) + # 2. paddle.pinverse keyword + out2 = paddle.pinverse(x=x) + # 3. paddle.pinverse with input alias + out3 = paddle.pinverse(input=x) + # 4. Tensor method + out4 = x.pinverse() + # 5. atol parameter test (keyword-only) + out5 = paddle.pinverse(x, atol=1e-10) + # 6. rtol parameter test (keyword-only) + out6 = paddle.pinverse(x, rtol=1e-10) + # 7. atol and rtol combined (keyword-only) + out7 = paddle.pinverse(x, atol=1e-10, rtol=1e-10) + # 8. out parameter test + out8 = paddle.static.data( + name="out8", shape=[2, 3], dtype="float32" + ) + paddle.pinverse(x, out=out8) + + # 9. hermitian=True with atol (square matrix) + x_sym = paddle.static.data( + name="x_sym", shape=[3, 3], dtype="float32" + ) + out9 = paddle.pinverse(x_sym, hermitian=True, atol=1e-4) + + # 10. hermitian=True with rtol + out10 = paddle.pinverse(x_sym, hermitian=True, rtol=1e-4) + + # 11. hermitian=True with both atol and rtol + out11 = paddle.pinverse(x_sym, hermitian=True, atol=1e-4, rtol=1e-4) + + exe = paddle.static.Executor() + np_x_sym = ( + self.np_x @ self.np_x.T + + np.eye(3, dtype=self.dtype) * 0.5 # full rank + ) + fetches = exe.run( + main, + feed={ + "x": self.np_x, + "out8": np.empty([2, 3], dtype="float32"), + "x_sym": np_x_sym, + }, + fetch_list=[ + out1, + out2, + out3, + out4, + out5, + out6, + out7, + out8, + out9, + out10, + out11, + ], + ) + + expected = np.linalg.pinv(self.np_x) + for out in fetches[:8]: + np.testing.assert_allclose(out, expected, rtol=1e-5) + + expected_sym = np.linalg.pinv(np_x_sym, hermitian=True) + for out in fetches[8:]: + np.testing.assert_allclose(out, expected_sym, rtol=1e-5) + + paddle.disable_static() + + +# Test addcdiv_ compatibility +class TestAddcdiv_InplaceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([1.0, 2.0, 3.0]).astype("float32") + self.np_t1 = np.array([4.0, 5.0, 6.0]).astype("float32") + self.np_t2 = np.array([2.0, 2.0, 2.0]).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + t1 = paddle.to_tensor(self.np_t1) + t2 = paddle.to_tensor(self.np_t2) + + # 1. Paddle Positional arguments + x1 = paddle.to_tensor(self.np_x.copy()) + out1 = paddle.addcdiv(x1, t1, t2, 1.0) + + # 2. Paddle keyword arguments + x2 = paddle.to_tensor(self.np_x.copy()) + out2 = paddle.addcdiv(input=x2, tensor1=t1, tensor2=t2, value=1.0) + + # 3. PyTorch keyword arguments (alias) + x3 = paddle.to_tensor(self.np_x.copy()) + out3 = paddle.addcdiv(input=x3, tensor1=t1, tensor2=t2, value=1.0) + + # 4. Mixed arguments + x4 = paddle.to_tensor(self.np_x.copy()) + out4 = paddle.addcdiv(x4, t1, tensor2=t2, value=1.0) + + # 5. out parameter test + x5 = paddle.to_tensor(self.np_x.copy()) + out5 = paddle.empty_like(x5) + paddle.addcdiv(x5, t1, t2, value=1.0, out=out5) + + # 6. Tensor method - args + x6 = paddle.to_tensor(self.np_x.copy()) + out6 = x6.addcdiv_(t1, t2, value=1.0) + + # 7. Tensor method - kwargs + x7 = paddle.to_tensor(self.np_x.copy()) + out7 = x7.addcdiv_(tensor1=t1, tensor2=t2, value=1.0) + + # Verify all outputs + expected = self.np_x + 1.0 * (self.np_t1 / self.np_t2) + for out in [out1, out2, out3, out4, out5, out6, out7]: + np.testing.assert_allclose(out.numpy(), expected, rtol=1e-5) + + paddle.enable_static() + + +# Test imag compatibility (compat function) +class TestImagAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array( + [[1 + 6j, 2 + 5j], [3 + 4j, 5 + 2j]], dtype='complex64' + ) + self.shape = [2, 2] + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. paddle.imag positional + out1 = paddle.imag(x) + + # 2. paddle.imag keyword + out2 = paddle.imag(x=x) + + # 3. paddle.imag with input alias + out3 = paddle.imag(input=x) + + # Verify outputs + for out in [out1, out2, out3]: + self.assertEqual(out.dtype, paddle.float32) + np.testing.assert_allclose(out.numpy(), self.np_x.imag) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data( + name="x", shape=self.shape, dtype="complex64" + ) + + # 1. paddle.imag positional + out1 = paddle.imag(x) + # 2. paddle.imag keyword + out2 = paddle.imag(x=x) + # 3. paddle.imag with input alias + out3 = paddle.imag(input=x) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + np.testing.assert_allclose(out, self.np_x.imag) + + paddle.disable_static() + + +# Test real compatibility (compat function) +class TestRealAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array( + [[1 + 6j, 2 + 5j], [3 + 4j, 5 + 2j]], dtype='complex64' + ) + self.shape = [2, 2] + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. paddle.real positional + out1 = paddle.real(x) + + # 2. paddle.real keyword + out2 = paddle.real(x=x) + + # 3. paddle.real with input alias + out3 = paddle.real(input=x) + # Verify outputs + for out in [out1, out2, out3]: + np.testing.assert_allclose(out.numpy(), self.np_x.real) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data( + name="x", shape=self.shape, dtype="complex64" + ) + + # 1. paddle.real positional + out1 = paddle.real(x) + # 2. paddle.real keyword + out2 = paddle.real(x=x) + # 3. paddle.real with input alias + out3 = paddle.real(input=x) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + np.testing.assert_allclose(out, self.np_x.real) + + paddle.disable_static() + + +# Test nan_to_num compatibility (PyTorch parameter alias) +class TestNanToNumAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([np.nan, 0.3, np.inf, -np.inf], dtype="float32") + self.shape = [4] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. Paddle Positional arguments + out1 = paddle.nan_to_num(x, 0.0) + + # 2. Paddle keyword arguments + out2 = paddle.nan_to_num(x=x, nan=0.0) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.nan_to_num(input=x, nan=0.0) + + # 4. Mixed arguments + out4 = paddle.nan_to_num(x, nan=0.0, posinf=None) + + # 5. out parameter test + out5 = paddle.empty_like(out1) + paddle.nan_to_num(x, nan=0.0, out=out5) + + # 6. Tensor method - args + out6 = x.nan_to_num(0.0) + + # 7. Tensor method - kwargs + out7 = x.nan_to_num(nan=0.0) + + # Verify all outputs + for out in [out1, out2, out3, out4, out5, out6, out7]: + np.testing.assert_allclose(out.numpy(), out1.numpy()) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.nan_to_num(x, 0.0) + # 2. Paddle keyword arguments + out2 = paddle.nan_to_num(x=x, nan=0.0) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.nan_to_num(input=x, nan=0.0) + # 4. Tensor method - args + out4 = x.nan_to_num(0.0) + # 5. Tensor method - kwargs + out5 = x.nan_to_num(nan=0.0) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x}, + fetch_list=[out1, out2, out3, out4, out5], + ) + + for out in fetches: + np.testing.assert_allclose(out, fetches[0]) + + paddle.disable_static() + + +# Test randint_like compatibility (PyTorch parameter alias and new params) +class TestRandintLikeAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.shape = [2, 3] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.zeros(self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.randint_like(x, 0, 10) + + # 2. Paddle keyword arguments + out2 = paddle.randint_like(x=x, low=0, high=10) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.randint_like(input=x, low=0, high=10) + + # 4. Mixed arguments + out4 = paddle.randint_like(x, high=10) + + # 5. pin_memory parameter (keyword-only) + out5 = paddle.randint_like(x, 0, 10, pin_memory=False) + + # 6. requires_grad parameter (keyword-only) + out6 = paddle.randint_like(x, 0, 10, requires_grad=False) + + # 7. Both pin_memory and requires_grad (keyword-only) + out7 = paddle.randint_like( + x, 0, 10, pin_memory=False, requires_grad=False + ) + + # Verify all outputs + for out in [out1, out2, out3, out4, out5, out6, out7]: + self.assertEqual(out.shape, x.shape) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.randint_like(x, 0, 10) + # 2. Paddle keyword arguments + out2 = paddle.randint_like(x=x, low=0, high=10) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.randint_like(input=x, low=0, high=10) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": np.zeros(self.shape, dtype=self.dtype)}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + self.assertEqual(out.shape, tuple(self.shape)) + + paddle.disable_static() + + +# Test resize_as_ compatibility +class TestResizeAsAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.random.rand(2, 3).astype("float32") + self.np_y = np.random.rand(4, 5).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + y = paddle.to_tensor(self.np_y) + + # 1. Paddle Positional arguments + x1 = paddle.to_tensor(self.np_x) + out1 = paddle.resize_as_(x1, y) + + # 2. Paddle keyword arguments + x2 = paddle.to_tensor(self.np_x) + out2 = paddle.resize_as_(x=x2, y=y) + + # 3. Mixed arguments + x3 = paddle.to_tensor(self.np_x) + out3 = paddle.resize_as_(x3, y=y) + + # 4. Tensor method - args + x4 = paddle.to_tensor(self.np_x) + out4 = x4.resize_as_(y) + + # Verify all outputs + for out in [out1, out2, out3, out4]: + self.assertEqual(out.shape, y.shape) + + paddle.enable_static() + + +# Test huber_loss compatibility (alias for smooth_l1_loss) +class TestHuberLossAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.input = np.random.rand(3, 5).astype("float32") + self.target = np.random.rand(3, 5).astype("float32") + self.shape = (3, 5) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + input = paddle.to_tensor(self.input) + target = paddle.to_tensor(self.target) + + # 1. Paddle Positional arguments + out1 = paddle.nn.functional.huber_loss(input, target) + + # 2. Paddle keyword arguments + out2 = paddle.nn.functional.huber_loss(input=input, label=target) + + # 3. PyTorch keyword arguments (target alias) + out3 = paddle.nn.functional.huber_loss(input=input, target=target) + + # 4. Mixed arguments + out4 = paddle.nn.functional.huber_loss(input, target=target, delta=1.0) + + # 5. smooth_l1_loss should be equivalent + out5 = paddle.nn.functional.smooth_l1_loss(input, target) + + # Verify outputs are equivalent + np.testing.assert_allclose(out1.numpy(), out2.numpy()) + np.testing.assert_allclose(out1.numpy(), out3.numpy()) + np.testing.assert_allclose(out1.numpy(), out4.numpy()) + np.testing.assert_allclose(out1.numpy(), out5.numpy()) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + input = paddle.static.data( + name="input", shape=self.shape, dtype="float32" + ) + target = paddle.static.data( + name="target", shape=self.shape, dtype="float32" + ) + + # 1. Paddle positional arguments + out1 = paddle.nn.functional.huber_loss( + input, target, reduction='none' + ) + # 2. Paddle keyword arguments + out2 = paddle.nn.functional.huber_loss( + input=input, label=target, reduction='none' + ) + # 3. PyTorch keyword arguments (target alias) + out3 = paddle.nn.functional.huber_loss( + input=input, target=target, reduction='none' + ) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"input": self.input, "target": self.target}, + fetch_list=[out1, out2, out3], + ) + + for out in fetches: + self.assertEqual(out.shape, (3, 5)) + + paddle.disable_static() + + +# Test fmod compatibility (alias for remainder/mod) +class TestFmodAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([5.0, 7.0, 9.0], dtype="float32") + self.np_y = np.array([2.0, 3.0, 4.0], dtype="float32") + self.shape = [3] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + y = paddle.to_tensor(self.np_y) + + # 1. Paddle Positional arguments + out1 = paddle.fmod(x, y) + + # 2. Paddle keyword arguments + out2 = paddle.fmod(x=x, y=y) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.fmod(input=x, other=y) + + # 4. Mixed arguments + out4 = paddle.fmod(x, other=y) + + # 5. out parameter test + out5 = paddle.empty_like(x) + paddle.fmod(x, y, out=out5) + + # 6. Tensor method - args + out6 = x.fmod(y) + + # 7. Tensor method - kwargs + out7 = x.fmod(other=y) + + # Verify all outputs + for out in [out1, out2, out3, out4, out5, out6, out7]: + np.testing.assert_allclose(out.numpy(), out1.numpy()) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + y = paddle.static.data(name="y", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.fmod(x, y) + # 2. Paddle keyword arguments + out2 = paddle.fmod(x=x, y=y) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.fmod(input=x, other=y) + # 4. Tensor method - args + out4 = x.fmod(y) + # 5. Tensor method - kwargs + out5 = x.fmod(other=y) + + exe = paddle.static.Executor() + fetches = exe.run( + main, + feed={"x": self.np_x, "y": self.np_y}, + fetch_list=[out1, out2, out3, out4, out5], + ) + + for out in fetches: + np.testing.assert_allclose(out, fetches[0]) + + paddle.disable_static() + + +# Test absolute compatibility (alias for abs) +class TestAbsoluteAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_x = np.array([-1.0, 2.0, -3.0], dtype="float32") + self.shape = [3] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor(self.np_x) + + # 1. Paddle Positional arguments + out1 = paddle.absolute(x) + + # 2. Paddle keyword arguments + out2 = paddle.absolute(x=x) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.absolute(input=x) + + # 4. Mixed arguments + out4 = paddle.absolute(x, name=None) + + # 5. out parameter test + out5 = paddle.empty_like(x) + paddle.absolute(x, out=out5) + + # 6. Tensor method - args + out6 = x.absolute() + + # 7. Alias paddle.abs + out7 = paddle.abs(x) + + # Verify all outputs + expected = np.abs(self.np_x) + for out in [out1, out2, out3, out4, out5, out6, out7]: + np.testing.assert_allclose(out.numpy(), expected) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # 1. Paddle Positional arguments + out1 = paddle.absolute(x) + # 2. Paddle keyword arguments + out2 = paddle.absolute(x=x) + # 3. PyTorch keyword arguments (alias) + out3 = paddle.absolute(input=x) + # 4. Alias paddle.abs + out4 = paddle.abs(x) + + exe = paddle.static.Executor() + fetches = exe.run( + main, feed={"x": self.np_x}, fetch_list=[out1, out2, out3, out4] + ) + + expected = np.abs(self.np_x) + for out in fetches: + np.testing.assert_allclose(out, expected) + + paddle.disable_static() + + +# Test assert_allclose compatibility +class TestAssertAllcloseAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + x = paddle.to_tensor([1.0, 2.0, 3.0]) + y = paddle.to_tensor([1.0, 2.0, 3.0]) + + # 1. Should not raise + paddle.testing.assert_allclose(x, y) + + # 2. With tolerances + paddle.testing.assert_allclose(x, y, rtol=1e-5, atol=1e-8) + + # 4. Non-Tensor inputs (isinstance branches) + paddle.testing.assert_allclose( + paddle.to_tensor([1.0, 2.0, 3.0]).numpy(), # actual is ndarray + paddle.to_tensor([1.0, 2.0, 3.0]).numpy(), # expected is ndarray + ) + + # 5. Non-Tensor actual with list + paddle.testing.assert_allclose( + [1.0, 2.0, 3.0], paddle.to_tensor([1.0, 2.0, 3.0]) + ) + + # 6. Non-Tensor expected with list + paddle.testing.assert_allclose( + paddle.to_tensor([1.0, 2.0, 3.0]), [1.0, 2.0, 3.0] + ) + + # 3. Should raise on mismatch + z = paddle.to_tensor([1.0, 2.0, 4.0]) + with self.assertRaises(AssertionError): + paddle.testing.assert_allclose(x, z) + + paddle.enable_static() + + +# Test GRU compatibility +class TestGRUAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.input_size = 16 + self.hidden_size = 32 + self.num_layers = 2 + self.batch_size = 4 + self.seq_len = 23 + self.shape_x = [self.batch_size, self.seq_len, self.input_size] + self.shape_h = [self.num_layers, self.batch_size, self.hidden_size] + self.dtype = "float32" + + def test_dygraph_Compatibility(self): + paddle.disable_static() + np.random.seed(2025) + np_x = np.random.randn(*self.shape_x).astype(self.dtype) + np_h = np.random.randn(*self.shape_h).astype(self.dtype) + x = paddle.to_tensor(np_x) + prev_h = paddle.to_tensor(np_h) + + # --- Forward, with bias (num_layers=self.num_layers) --- + ref = paddle.nn.GRU(self.input_size, self.hidden_size, self.num_layers) + ref_sd = ref.state_dict() + ref_y, ref_h = ref(x, prev_h) + + # 1. Paddle positional arguments + rnn1 = paddle.nn.GRU(self.input_size, self.hidden_size, self.num_layers) + rnn1.set_state_dict(ref_sd) + y1, h1 = rnn1(x, prev_h) + np.testing.assert_allclose(y1.numpy(), ref_y.numpy(), rtol=1e-5) + np.testing.assert_allclose(h1.numpy(), ref_h.numpy(), rtol=1e-5) + + # 2. Paddle keyword arguments + rnn2 = paddle.nn.GRU( + input_size=self.input_size, + hidden_size=self.hidden_size, + num_layers=self.num_layers, + ) + rnn2.set_state_dict(ref_sd) + y2, h2 = rnn2(x, prev_h) + np.testing.assert_allclose(y2.numpy(), ref_y.numpy(), rtol=1e-5) + np.testing.assert_allclose(h2.numpy(), ref_h.numpy(), rtol=1e-5) + + # 3. PyTorch positional arguments (bias=True at 4th position) + rnn3 = paddle.nn.GRU( + self.input_size, self.hidden_size, self.num_layers, True + ) + rnn3.set_state_dict(ref_sd) + y3, h3 = rnn3(x, prev_h) + np.testing.assert_allclose(y3.numpy(), ref_y.numpy(), rtol=1e-5) + np.testing.assert_allclose(h3.numpy(), ref_h.numpy(), rtol=1e-5) + + # 4. PyTorch keyword arguments (bias, batch_first, bidirectional) + rnn4 = paddle.nn.GRU( + self.input_size, + self.hidden_size, + num_layers=self.num_layers, + bias=True, + batch_first=True, + bidirectional=False, + ) + rnn4.set_state_dict(ref_sd) + y4, h4 = rnn4(x, prev_h) + np.testing.assert_allclose(y4.numpy(), ref_y.numpy(), rtol=1e-5) + np.testing.assert_allclose(h4.numpy(), ref_h.numpy(), rtol=1e-5) + + # --- bias=False --- + ref_nb = paddle.nn.GRU( + self.input_size, + self.hidden_size, + self.num_layers, + bias_ih_attr=False, + bias_hh_attr=False, + ) + ref_nb_sd = ref_nb.state_dict() + ref_y_nb, ref_h_nb = ref_nb(x, prev_h) + + # 5. bias parameter test (bias=False) + rnn5 = paddle.nn.GRU( + self.input_size, self.hidden_size, self.num_layers, bias=False + ) + rnn5.set_state_dict(ref_nb_sd) + y5, h5 = rnn5(x, prev_h) + np.testing.assert_allclose(y5.numpy(), ref_y_nb.numpy(), rtol=1e-5) + np.testing.assert_allclose(h5.numpy(), ref_h_nb.numpy(), rtol=1e-5) + + # 6. device parameter test (constructor only) + paddle.nn.GRU(self.input_size, self.hidden_size, device="cpu") + + # 7. dtype parameter test (constructor only) + paddle.nn.GRU(self.input_size, self.hidden_size, dtype="float32") + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + x = paddle.static.data( + name="x", + shape=self.shape_x, + dtype=self.dtype, + ) + prev_h = paddle.static.data( + name="prev_h", + shape=self.shape_h, + dtype=self.dtype, + ) + + # 1. Paddle positional arguments + rnn1 = paddle.nn.GRU( + self.input_size, self.hidden_size, self.num_layers + ) + y1, h1 = rnn1(x, prev_h) + + # 2. PyTorch keyword arguments (bias, batch_first, bidirectional) + rnn2 = paddle.nn.GRU( + self.input_size, + self.hidden_size, + num_layers=self.num_layers, + bias=True, + batch_first=True, + bidirectional=False, + ) + y2, h2 = rnn2(x, prev_h) + + exe = paddle.static.Executor() + exe.run(startup) + # Just verify it runs in static mode + fetches = exe.run( + main, + feed={ + "x": np.random.randn(*self.shape_x).astype(self.dtype), + "prev_h": np.random.randn(*self.shape_h).astype(self.dtype), + }, + fetch_list=[y1, h1, y2, h2], + ) + + self.assertEqual( + fetches[0].shape, + (self.batch_size, self.seq_len, self.hidden_size), + ) + + paddle.disable_static() + + +# Test set_default_tensor_type compatibility +class TestSetDefaultTensorTypeAPI(unittest.TestCase): + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # Save original dtype + original_dtype = paddle.get_default_dtype() + + # ========== float32 tests ========== + paddle.set_default_tensor_type(paddle.FloatTensor) + self.assertEqual(paddle.get_default_dtype(), "float32") + paddle.set_default_tensor_type(paddle.cuda.FloatTensor) + self.assertEqual(paddle.get_default_dtype(), "float32") + + paddle.set_default_tensor_type("paddle.FloatTensor") + self.assertEqual(paddle.get_default_dtype(), "float32") + paddle.set_default_tensor_type("paddle.cuda.FloatTensor") + self.assertEqual(paddle.get_default_dtype(), "float32") + + paddle.set_default_tensor_type("torch.FloatTensor") + self.assertEqual(paddle.get_default_dtype(), "float32") + paddle.set_default_tensor_type("torch.cuda.FloatTensor") + self.assertEqual(paddle.get_default_dtype(), "float32") + + # ========== float64 tests ========== + paddle.set_default_tensor_type(paddle.DoubleTensor) + self.assertEqual(paddle.get_default_dtype(), "float64") + paddle.set_default_tensor_type(paddle.cuda.DoubleTensor) + self.assertEqual(paddle.get_default_dtype(), "float64") + + paddle.set_default_tensor_type("paddle.DoubleTensor") + self.assertEqual(paddle.get_default_dtype(), "float64") + paddle.set_default_tensor_type("paddle.cuda.DoubleTensor") + self.assertEqual(paddle.get_default_dtype(), "float64") + + paddle.set_default_tensor_type("torch.DoubleTensor") + self.assertEqual(paddle.get_default_dtype(), "float64") + paddle.set_default_tensor_type("torch.cuda.DoubleTensor") + self.assertEqual(paddle.get_default_dtype(), "float64") + + # ========== float16 tests (10 formats) ========== + paddle.set_default_tensor_type(paddle.HalfTensor) + self.assertEqual(paddle.get_default_dtype(), "float16") + paddle.set_default_tensor_type(paddle.cuda.HalfTensor) + self.assertEqual(paddle.get_default_dtype(), "float16") + + paddle.set_default_tensor_type("paddle.HalfTensor") + self.assertEqual(paddle.get_default_dtype(), "float16") + paddle.set_default_tensor_type("paddle.cuda.HalfTensor") + self.assertEqual(paddle.get_default_dtype(), "float16") + + paddle.set_default_tensor_type("torch.HalfTensor") + self.assertEqual(paddle.get_default_dtype(), "float16") + paddle.set_default_tensor_type("torch.cuda.HalfTensor") + self.assertEqual(paddle.get_default_dtype(), "float16") + + # ========== bfloat16 tests (10 formats) ========== + paddle.set_default_tensor_type(paddle.BFloat16Tensor) + self.assertEqual(paddle.get_default_dtype(), "bfloat16") + paddle.set_default_tensor_type(paddle.cuda.BFloat16Tensor) + self.assertEqual(paddle.get_default_dtype(), "bfloat16") + + paddle.set_default_tensor_type("paddle.BFloat16Tensor") + self.assertEqual(paddle.get_default_dtype(), "bfloat16") + paddle.set_default_tensor_type("paddle.cuda.BFloat16Tensor") + self.assertEqual(paddle.get_default_dtype(), "bfloat16") + + paddle.set_default_tensor_type("torch.BFloat16Tensor") + self.assertEqual(paddle.get_default_dtype(), "bfloat16") + paddle.set_default_tensor_type("torch.cuda.BFloat16Tensor") + self.assertEqual(paddle.get_default_dtype(), "bfloat16") + + # ========== TypeError branches ========== + # Invalid tensor type name (not in dtype_map) + with self.assertRaises(TypeError): + paddle.set_default_tensor_type("torch.IntTensor") + + # Passing dtype instead of tensor type + with self.assertRaises(TypeError): + paddle.set_default_tensor_type(paddle.float32) + + # Restore original dtype + paddle.set_default_dtype(original_dtype) + + paddle.enable_static() + + +# Test PackedSequence compatibility +class TestPackedSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.data = np.random.rand(10, 5).astype("float32") + self.batch_sizes = np.array([3, 3, 2, 1, 1], dtype="int64") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Create PackedSequence with positional arguments + data_tensor = paddle.to_tensor(self.data) + batch_sizes_tensor = paddle.to_tensor(self.batch_sizes) + packed1 = paddle.nn.utils.rnn.PackedSequence( + data_tensor, batch_sizes_tensor + ) + + # 2. Create PackedSequence with keyword arguments + packed2 = paddle.nn.utils.rnn.PackedSequence( + data=data_tensor, batch_sizes=batch_sizes_tensor + ) + + # 3. Create PackedSequence with sorted_indices + sorted_indices = paddle.to_tensor([2, 0, 1], dtype="int64") + packed3 = paddle.nn.utils.rnn.PackedSequence( + data_tensor, batch_sizes_tensor, sorted_indices=sorted_indices + ) + + # 4. Create PackedSequence with all parameters + unsorted_indices = paddle.to_tensor([1, 2, 0], dtype="int64") + packed4 = paddle.nn.utils.rnn.PackedSequence( + data=data_tensor, + batch_sizes=batch_sizes_tensor, + sorted_indices=sorted_indices, + unsorted_indices=unsorted_indices, + ) + + # Verify all instances + for packed in [packed1, packed2, packed3, packed4]: + self.assertEqual(packed.data.shape, [10, 5]) + self.assertEqual(packed.batch_sizes.shape, [5]) + np.testing.assert_allclose(packed.data.numpy(), self.data) + np.testing.assert_array_equal( + packed.batch_sizes.numpy(), self.batch_sizes + ) + + # Verify sorted_indices and unsorted_indices + np.testing.assert_array_equal(packed3.sorted_indices.numpy(), [2, 0, 1]) + np.testing.assert_array_equal(packed4.sorted_indices.numpy(), [2, 0, 1]) + np.testing.assert_array_equal( + packed4.unsorted_indices.numpy(), [1, 2, 0] + ) + + # 5. Test properties + # Note: is_cuda returns True if data is on GPU, False on CPU + # Since the test may run on GPU or CPU, we just check it's a boolean + self.assertIsInstance(packed1.is_cuda, bool) + self.assertIsInstance(packed1.is_pinned, bool) + + # 6. Test to() method + # When called with dtype change, data dtype changes but indices stay int64 + packed_dtype = packed4.to(dtype=paddle.float64) + self.assertEqual(packed_dtype.data.dtype, paddle.float64) + self.assertEqual(packed_dtype.sorted_indices.dtype, paddle.int64) + self.assertEqual(packed_dtype.unsorted_indices.dtype, paddle.int64) + self.assertEqual(packed_dtype.unsorted_indices.dtype, paddle.int64) + + # 7. Test dtype conversion methods + packed_double = packed1.double() + self.assertEqual(packed_double.data.dtype, paddle.float64) + + packed_float = packed1.float() + self.assertEqual(packed_float.data.dtype, paddle.float32) + + packed_half = packed1.half() + self.assertEqual(packed_half.data.dtype, paddle.float16) + + packed_long = packed1.long() + self.assertEqual(packed_long.data.dtype, paddle.int64) + + packed_int = packed1.int() + self.assertEqual(packed_int.data.dtype, paddle.int32) + + packed_short = packed1.short() + self.assertEqual(packed_short.data.dtype, paddle.int16) + + packed_char = packed1.char() + self.assertEqual(packed_char.data.dtype, paddle.int8) + + packed_byte = packed1.byte() + self.assertEqual(packed_byte.data.dtype, paddle.uint8) + + # 8. Test pin_memory + if paddle.is_compiled_with_cuda(): + packed_pinned = packed1.pin_memory() + self.assertIsInstance( + packed_pinned, paddle.nn.utils.rnn.PackedSequence + ) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + data = paddle.static.data( + name="data", shape=[10, 5], dtype="float32" + ) + batch_sizes = paddle.static.data( + name="batch_sizes", shape=[5], dtype="int64" + ) + + # 1. Create PackedSequence with positional arguments + packed1 = paddle.nn.utils.rnn.PackedSequence(data, batch_sizes) + + # 2. Create PackedSequence with keyword arguments + packed2 = paddle.nn.utils.rnn.PackedSequence( + data=data, batch_sizes=batch_sizes + ) + + # Verify Variables are preserved + self.assertEqual(packed1.data.name, data.name) + self.assertEqual(packed1.batch_sizes.name, batch_sizes.name) + self.assertEqual(packed2.data.name, data.name) + self.assertEqual(packed2.batch_sizes.name, batch_sizes.name) + + paddle.disable_static() + + +# Test invert_permutation compatibility +class TestInvertPermutationAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Basic test with positional argument + perm = paddle.to_tensor([2, 0, 1], dtype="int64") + inv_perm1 = paddle.nn.utils.rnn.invert_permutation(perm) + np.testing.assert_array_equal(inv_perm1.numpy(), [1, 2, 0]) + + # 2. Test with keyword argument + inv_perm2 = paddle.nn.utils.rnn.invert_permutation(permutation=perm) + np.testing.assert_array_equal(inv_perm2.numpy(), [1, 2, 0]) + + # 3. Test with None input + result = paddle.nn.utils.rnn.invert_permutation(None) + self.assertIsNone(result) + + # 4. Test with different permutation + perm2 = paddle.to_tensor([0, 1, 2, 3], dtype="int64") + inv_perm3 = paddle.nn.utils.rnn.invert_permutation(perm2) + np.testing.assert_array_equal(inv_perm3.numpy(), [0, 1, 2, 3]) + + paddle.enable_static() + + def test_static_Compatibility(self): + paddle.enable_static() + main = paddle.static.Program() + startup = paddle.static.Program() + with paddle.static.program_guard(main, startup): + perm = paddle.static.data(name="perm", shape=[3], dtype="int64") + inv_perm1 = paddle.nn.utils.rnn.invert_permutation(perm) + inv_perm2 = paddle.nn.utils.rnn.invert_permutation(permutation=perm) + + exe = paddle.static.Executor() + exe.run(startup) + fetches = exe.run( + main, + feed={"perm": np.array([2, 0, 1], dtype="int64")}, + fetch_list=[inv_perm1, inv_perm2], + ) + + for out in fetches: + np.testing.assert_array_equal(out, [1, 2, 0]) + + paddle.disable_static() + + +# Test pack_padded_sequence compatibility +class TestPackPaddedSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_seq = np.random.rand(5, 3, 10).astype("float32") + self.lengths = [5, 3, 2] + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq = paddle.to_tensor(self.np_seq) + + # 1. Paddle Positional arguments + packed1 = paddle.nn.utils.rnn.pack_padded_sequence( + seq, paddle.to_tensor(self.lengths) + ) + + # 2. Paddle keyword arguments + packed2 = paddle.nn.utils.rnn.pack_padded_sequence( + input=seq, lengths=paddle.to_tensor(self.lengths) + ) + + # 3. PyTorch keyword arguments (batch_first=False) + packed3 = paddle.nn.utils.rnn.pack_padded_sequence( + input=seq, lengths=self.lengths, batch_first=False + ) + + # 4. With batch_first=True + seq_batch_first = seq.transpose([1, 0, 2]) + packed4 = paddle.nn.utils.rnn.pack_padded_sequence( + seq_batch_first, self.lengths, batch_first=True + ) + + # 5. With enforce_sorted=False + packed5 = paddle.nn.utils.rnn.pack_padded_sequence( + seq, self.lengths, enforce_sorted=False + ) + + # 6. With enforce_sorted=False (complex case) + seq_unsorted = paddle.to_tensor( + [[1, 2, 0], [3, 0, 0], [4, 5, 6]], dtype='float32' + ) + packed6 = paddle.nn.utils.rnn.pack_padded_sequence( + seq_unsorted, [2, 1, 3], batch_first=True, enforce_sorted=False + ) + + # 7. Mixed arguments (positional + keyword) + packed7 = paddle.nn.utils.rnn.pack_padded_sequence( + seq, enforce_sorted=True, lengths=paddle.to_tensor(self.lengths) + ) + + # Verify all outputs + for packed in [packed1, packed2, packed3, packed5, packed7]: + self.assertIsInstance(packed, paddle.nn.utils.rnn.PackedSequence) + self.assertTrue(hasattr(packed, 'data')) + self.assertTrue(hasattr(packed, 'batch_sizes')) + # All these should be identical + np.testing.assert_allclose( + packed.data.numpy(), packed1.data.numpy() + ) + np.testing.assert_array_equal( + packed.batch_sizes.numpy(), packed1.batch_sizes.numpy() + ) + + # packed4 (batch_first=True) should be same as packed1 + np.testing.assert_allclose(packed4.data.numpy(), packed1.data.numpy()) + np.testing.assert_array_equal( + packed4.batch_sizes.numpy(), packed1.batch_sizes.numpy() + ) + + # Verify unsorted case + self.assertIsNotNone(packed6.sorted_indices) + self.assertIsNotNone(packed6.unsorted_indices) + # Verify packed6 data (manual check for small unsorted case) + # seq_unsorted = [[1, 2, 0], [3, 0, 0], [4, 5, 6]], lengths = [2, 1, 3] + # sorted by lengths: [4, 5, 6] (3), [1, 2, 0] (2), [3, 0, 0] (1) + # packed: [4, 1, 3, 5, 2, 6] + expected_data = [4, 1, 3, 5, 2, 6] + expected_batch_sizes = [3, 2, 1] + np.testing.assert_array_equal( + packed6.data.numpy().flatten(), expected_data + ) + np.testing.assert_array_equal( + packed6.batch_sizes.numpy(), expected_batch_sizes + ) + + paddle.enable_static() + + +# Test pad_packed_sequence compatibility +class TestPadPackedSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.np_seq = np.random.rand(5, 3, 10).astype("float32") + self.lengths = [5, 3, 2] + + def test_dygraph_Compatibility(self): + paddle.disable_static() + seq = paddle.to_tensor(self.np_seq) + lengths_tensor = paddle.to_tensor(self.lengths) + + # Create packed sequence + packed = paddle.nn.utils.rnn.pack_padded_sequence(seq, lengths_tensor) + + # 1. Paddle Positional arguments + padded1, lengths1 = paddle.nn.utils.rnn.pad_packed_sequence(packed) + + # 2. Paddle keyword arguments + padded2, lengths2 = paddle.nn.utils.rnn.pad_packed_sequence( + sequence=packed + ) + + # 3. With batch_first=True + packed_bf = paddle.nn.utils.rnn.pack_padded_sequence( + seq.transpose([1, 0, 2]), lengths_tensor, batch_first=True + ) + padded3, lengths3 = paddle.nn.utils.rnn.pad_packed_sequence( + packed_bf, batch_first=True + ) + + # 4. With padding_value + padded4, lengths4 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, padding_value=1.0 + ) + + # 5. With total_length + padded5, lengths5 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, total_length=10 + ) + + # 6. Mixed arguments (positional + keyword) + padded6, lengths6 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, batch_first=False + ) + + # 7. Another padding_value test + padded7, lengths7 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, padding_value=-1.0 + ) + + # 8. batch_first=True on packed (which was batch_first=False) + padded8, lengths8 = paddle.nn.utils.rnn.pad_packed_sequence( + packed, batch_first=True + ) + + # 9. TypeError when sequence is not PackedSequence + with self.assertRaises(TypeError): + paddle.nn.utils.rnn.pad_packed_sequence("not_a_packed_sequence") + + # Verify outputs numerical correctness + # For default padding (0.0) + expected_padded = self.np_seq.copy() + # For batch 0, length is 5. No padding. + # For batch 1, length is 3. Elements at time 3, 4 should be 0. + expected_padded[3:, 1, :] = 0.0 + # For batch 2, length is 2. Elements at time 2, 3, 4 should be 0. + expected_padded[2:, 2, :] = 0.0 + + for padded, lengths in [ + (padded1, lengths1), + (padded2, lengths2), + (padded6, lengths6), + ]: + self.assertEqual(padded.shape, [5, 3, 10]) + np.testing.assert_array_equal(lengths.numpy(), self.lengths) + np.testing.assert_allclose(padded.numpy(), expected_padded) + + # For padding_value=1.0 (padded4) + expected_padded_1 = self.np_seq.copy() + expected_padded_1[3:, 1, :] = 1.0 + expected_padded_1[2:, 2, :] = 1.0 + np.testing.assert_allclose(padded4.numpy(), expected_padded_1) + + # For padding_value=-1.0 (padded7) + expected_padded_neg1 = self.np_seq.copy() + expected_padded_neg1[3:, 1, :] = -1.0 + expected_padded_neg1[2:, 2, :] = -1.0 + np.testing.assert_allclose(padded7.numpy(), expected_padded_neg1) + + # For batch_first=True (padded3) + expected_padded_bf = self.np_seq.transpose([1, 0, 2]).copy() + expected_padded_bf[1, 3:, :] = 0.0 + expected_padded_bf[2, 2:, :] = 0.0 + self.assertEqual(padded3.shape, [3, 5, 10]) + np.testing.assert_allclose(padded3.numpy(), expected_padded_bf) + + # For total_length=10 (padded5) + self.assertEqual(padded5.shape, [10, 3, 10]) + np.testing.assert_allclose(padded5.numpy()[:5], expected_padded) + np.testing.assert_allclose(padded5.numpy()[5:], 0.0) + + # For padded8 (batch_first=True on packed which was batch_first=False) + self.assertEqual(padded8.shape, [3, 5, 10]) + np.testing.assert_allclose(padded8.numpy(), expected_padded_bf) + + paddle.enable_static() + + +# Test pad_sequence compatibility +class TestPadSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.a = np.random.rand(25, 300).astype("float32") + self.b = np.random.rand(22, 300).astype("float32") + self.c = np.random.rand(15, 300).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + a = paddle.to_tensor(self.a) + b = paddle.to_tensor(self.b) + c = paddle.to_tensor(self.c) + sequences = [a, b, c] + + # 1. Paddle Positional arguments + padded1 = paddle.nn.utils.rnn.pad_sequence(sequences) + + # 2. Paddle keyword arguments + padded2 = paddle.nn.utils.rnn.pad_sequence(sequences=sequences) + + # 3. PyTorch keyword arguments (batch_first=True) + padded3 = paddle.nn.utils.rnn.pad_sequence(sequences, batch_first=True) + + # 4. With padding_value + padded4 = paddle.nn.utils.rnn.pad_sequence(sequences, padding_value=1.0) + + # 5. With padding_side='left' + padded5 = paddle.nn.utils.rnn.pad_sequence( + sequences, padding_side='left' + ) + + # 6. Mixed arguments (positional + keyword) + padded6 = paddle.nn.utils.rnn.pad_sequence( + sequences, batch_first=True, padding_value=0.0 + ) + + # 7. TypeError when sequences is a string (not a valid iterable of Tensors) + with self.assertRaises(TypeError): + paddle.nn.utils.rnn.pad_sequence("not_a_valid_input") + + # 8. ValueError for invalid padding_side + with self.assertRaises(ValueError): + paddle.nn.utils.rnn.pad_sequence(sequences, padding_side='invalid') + + # 9. PyTorch-style tuple input (should work the same as list) + padded_tuple = paddle.nn.utils.rnn.pad_sequence( + (a, b, c), batch_first=True + ) + self.assertEqual(padded_tuple.shape, [3, 25, 300]) + np.testing.assert_allclose(padded_tuple.numpy(), padded3.numpy()) + + # Verify outputs + self.assertEqual(padded1.shape, [25, 3, 300]) + self.assertEqual(padded2.shape, [25, 3, 300]) + self.assertEqual(padded3.shape, [3, 25, 300]) + self.assertEqual(padded4.shape, [25, 3, 300]) + self.assertEqual(padded5.shape, [25, 3, 300]) + self.assertEqual(padded6.shape, [3, 25, 300]) + + # Numerical checks + np.testing.assert_allclose(padded1.numpy()[:, 0, :], self.a) + np.testing.assert_allclose(padded1.numpy()[:22, 1, :], self.b) + np.testing.assert_allclose(padded1.numpy()[22:, 1, :], 0.0) + np.testing.assert_allclose(padded1.numpy()[:15, 2, :], self.c) + np.testing.assert_allclose(padded1.numpy()[15:, 2, :], 0.0) + + # padding_value=1.0 + np.testing.assert_allclose(padded4.numpy()[22:, 1, :], 1.0) + + # batch_first=True + np.testing.assert_allclose(padded3.numpy()[0, :, :], self.a) + + # padding_side='left' + # padded5 shape [25, 3, 300] + # a: [25, 300] -> no padding + # b: [22, 300] -> 3 elements padded at top + # c: [15, 300] -> 10 elements padded at top + np.testing.assert_allclose(padded5.numpy()[:, 0, :], self.a) + np.testing.assert_allclose(padded5.numpy()[3:, 1, :], self.b) + np.testing.assert_allclose(padded5.numpy()[:3, 1, :], 0.0) + np.testing.assert_allclose(padded5.numpy()[10:, 2, :], self.c) + np.testing.assert_allclose(padded5.numpy()[:10, 2, :], 0.0) + + paddle.enable_static() + + +# Test unpad_sequence compatibility +class TestUnpadSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + self.a = np.random.rand(25, 300).astype("float32") + self.b = np.random.rand(22, 300).astype("float32") + self.c = np.random.rand(15, 300).astype("float32") + + def test_dygraph_Compatibility(self): + paddle.disable_static() + a = paddle.to_tensor(self.a) + b = paddle.to_tensor(self.b) + c = paddle.to_tensor(self.c) + sequences = [a, b, c] + + # Pad then unpad + padded = paddle.nn.utils.rnn.pad_sequence(sequences) + lengths = paddle.to_tensor([v.shape[0] for v in sequences]) + + # 1. Paddle Positional arguments + unpadded1 = paddle.nn.utils.rnn.unpad_sequence(padded, lengths) + + # 2. Paddle keyword arguments + unpadded2 = paddle.nn.utils.rnn.unpad_sequence( + padded_sequences=padded, lengths=lengths + ) + + # 3. PyTorch keyword arguments (batch_first=True) + padded_bf = paddle.nn.utils.rnn.pad_sequence( + sequences, batch_first=True + ) + unpadded3 = paddle.nn.utils.rnn.unpad_sequence( + padded_bf, lengths, batch_first=True + ) + + # 4. Mixed arguments (positional + keyword) + unpadded4 = paddle.nn.utils.rnn.unpad_sequence( + padded, batch_first=False, lengths=lengths + ) + + # Verify outputs + for i, (original, unpadded) in enumerate(zip(sequences, unpadded1)): + np.testing.assert_allclose(original.numpy(), unpadded.numpy()) + + for i, (original, unpadded) in enumerate(zip(sequences, unpadded2)): + np.testing.assert_allclose(original.numpy(), unpadded.numpy()) + + for i, (original, unpadded) in enumerate(zip(sequences, unpadded3)): + np.testing.assert_allclose(original.numpy(), unpadded.numpy()) + + for i, (original, unpadded) in enumerate(zip(sequences, unpadded4)): + np.testing.assert_allclose(original.numpy(), unpadded.numpy()) + + paddle.enable_static() + + +# Test pack_sequence compatibility +class TestPackSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # 1. Paddle Positional arguments (sorted) + a = paddle.to_tensor([1, 2, 3]) + b = paddle.to_tensor([4, 5]) + c = paddle.to_tensor([6]) + packed1 = paddle.nn.utils.rnn.pack_sequence([a, b, c]) + + # 2. Paddle keyword arguments + packed2 = paddle.nn.utils.rnn.pack_sequence(sequences=[a, b, c]) + + # 3. PyTorch keyword arguments (enforce_sorted=False) + d = paddle.to_tensor([1, 2, 3]) + e = paddle.to_tensor([4]) + f = paddle.to_tensor([5, 6]) + packed3 = paddle.nn.utils.rnn.pack_sequence( + [d, e, f], enforce_sorted=False + ) + + # 4. Mixed arguments (positional + keyword) + g = paddle.to_tensor([7, 8, 9]) + h = paddle.to_tensor([10, 11]) + packed4 = paddle.nn.utils.rnn.pack_sequence([g, h], enforce_sorted=True) + + # Verify outputs + for packed in [packed1, packed2]: + self.assertIsInstance(packed, paddle.nn.utils.rnn.PackedSequence) + np.testing.assert_array_equal( + packed.data.numpy().flatten(), [1, 4, 6, 2, 5, 3] + ) + np.testing.assert_array_equal(packed.batch_sizes.numpy(), [3, 2, 1]) + + # packed3: d=[1,2,3], e=[4], f=[5,6]. enforce_sorted=False + # sorted by len: [1,2,3] (3), [5,6] (2), [4] (1) + # data: [1, 5, 4, 2, 6, 3] + # batch_sizes: [3, 2, 1] + self.assertIsNotNone(packed3.sorted_indices) + self.assertIsNotNone(packed3.unsorted_indices) + np.testing.assert_array_equal( + packed3.data.numpy().flatten(), [1, 5, 4, 2, 6, 3] + ) + np.testing.assert_array_equal(packed3.batch_sizes.numpy(), [3, 2, 1]) + + # packed4: g=[7,8,9], h=[10,11]. enforce_sorted=True + # data: [7, 10, 8, 11, 9] + # batch_sizes: [2, 2, 1] + self.assertIsInstance(packed4, paddle.nn.utils.rnn.PackedSequence) + np.testing.assert_array_equal( + packed4.data.numpy().flatten(), [7, 10, 8, 11, 9] + ) + np.testing.assert_array_equal(packed4.batch_sizes.numpy(), [2, 2, 1]) + + paddle.enable_static() + + +# Test unpack_sequence compatibility +class TestUnpackSequenceAPI(unittest.TestCase): + def setUp(self): + np.random.seed(2025) + + def test_dygraph_Compatibility(self): + paddle.disable_static() + + # Create sequences + a = paddle.to_tensor([1, 2, 3]) + b = paddle.to_tensor([4, 5]) + c = paddle.to_tensor([6]) + sequences = [a, b, c] + + # Pack and unpack + packed = paddle.nn.utils.rnn.pack_sequence(sequences) + + # 1. Paddle Positional arguments + unpacked1 = paddle.nn.utils.rnn.unpack_sequence(packed) + + # 2. Paddle keyword arguments + unpacked2 = paddle.nn.utils.rnn.unpack_sequence(packed_sequences=packed) + + # 3. Mixed arguments (positional + keyword) + unpacked3 = paddle.nn.utils.rnn.unpack_sequence(packed_sequences=packed) + + # Verify outputs + for i, (original, unpacked) in enumerate(zip(sequences, unpacked1)): + np.testing.assert_array_equal(original.numpy(), unpacked.numpy()) + + for i, (original, unpacked) in enumerate(zip(sequences, unpacked2)): + np.testing.assert_array_equal(original.numpy(), unpacked.numpy()) + + for i, (original, unpacked) in enumerate(zip(sequences, unpacked3)): + np.testing.assert_array_equal(original.numpy(), unpacked.numpy()) + + paddle.enable_static() + + if __name__ == "__main__": unittest.main() diff --git a/test/legacy_test/test_nn_init_function.py b/test/legacy_test/test_nn_init_function.py index e8163437da85c1..0d038a42203881 100644 --- a/test/legacy_test/test_nn_init_function.py +++ b/test/legacy_test/test_nn_init_function.py @@ -1256,6 +1256,18 @@ def test_dygraph(self): self.assertIs(output, input_tensor) self.check(input_tensor, sparsity, std=0.01) + def test_alias(self): + if paddle.is_compiled_with_xpu(): + self.skipTest("sparsity is not supported on XPU") + with dygraph_guard(): + for sparsity in [0.1, 0.5, 0.9]: + input_tensor = paddle.randn([100, 50]) + output = paddle.nn.init.sparse( + input_tensor, sparsity=sparsity, std=0.01 + ) + self.assertIs(output, input_tensor) + self.check(input_tensor, sparsity, std=0.01) + def test_static_graph_case(self): self.place = get_devices() with static_guard():