diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/README.md new file mode 100644 index 000000000..b1d9ea37f --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/README.md @@ -0,0 +1,49 @@ +# PaddlePaddle__Paddle-78301 + +This directory converts Paddle PR #78301 into a SWE-Paddle community task candidate. + +## Source + +| Field | Value | +| --- | --- | +| Repository | `PaddlePaddle/Paddle` | +| PR | [78301](https://github.com/PaddlePaddle/Paddle/pull/78301) | +| PR title | `[API Compatibility] align paddle.nn.Layer.to/paddle.Tensor.to -part` | +| Base commit | `fd041ffe2d941d7219090cb12f6ffb10860dc851` | +| Squash commit | `d6e41b70154ba52525884afdc15c2a9d763a2cae` | +| Merged at | `2026-04-03T07:08:36Z` | +| Track | `C` (feature_or_api) | +| Task type | `feature_enhancement` | +| Resource | CPU | + +## Summary + +Align the public conversion call forms of `paddle.nn.Layer.to` and `paddle.Tensor.to`. The task covers positional and keyword dtype/device/reference-Tensor arguments, blocking options, `copy`, no-argument calls, invalid-input errors, and recursive in-place layer conversion. + +## Why This Task + +- It comes from a merged API-compatibility fix with a clear user-visible contract. +- It requires consistent overload parsing across two public APIs rather than a signature-only change. +- It exercises success paths, argument conflicts, invalid input, layer identity, and recursive sublayer conversion. +- Its Python-only scope is suitable for CPU verification while remaining non-trivial. + +Only the behavior merged in PR #78301 is in scope. Follow-up PRs referenced by `proposal.md` are intentionally excluded from the gold patch and acceptance criteria. + +## Files + +- `proposal.md`: approved candidate proposal and scope. +- `instruction.md`: self-contained problem statement for the coding agent. +- `solution/code.patch`: gold patch for non-test source files. +- `tests/test.patch`: target behavior tests, including a same-dtype positional `copy=True` case that requires a distinct result. +- `tests/test.sh`: minimal F2P/P2P test command. +- `environment/README.md`: base revision, run order, and verification risks. + +## Verification + +From a Paddle source checkout at the base commit, apply `tests/test.patch` and run: + +```bash +bash tests/test.sh +``` + +The target tests should fail before `solution/code.patch` is applied and pass afterward. The existing `TestLayerTo::test_main` node is included as a P2P regression guard. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/environment/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/environment/README.md new file mode 100644 index 000000000..3d946f88a --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/environment/README.md @@ -0,0 +1,37 @@ +# Environment Notes + +## Expected Environment + +- Repository: `PaddlePaddle/Paddle` +- Base commit: `fd041ffe2d941d7219090cb12f6ffb10860dc851` +- Resource: CPU +- GPU required: no +- Build path: Paddle source checkout at the base commit with a base-compatible Paddle Python installation. The task changes Python sources only, so the verifier may use a Python overlay on an existing compatible build; otherwise build and install Paddle from the base checkout. + +## Run Order + +1. Check out `PaddlePaddle/Paddle` at the base commit. +2. Apply `tests/test.patch`. +3. Run `bash tests/test.sh`; at least the target behavior should fail before the fix. +4. Apply `solution/code.patch`. +5. Run `bash tests/test.sh` again; the target behavior and P2P guard should pass. + +## Minimal Test Command + +```bash +bash tests/test.sh +``` + +## Verification Scope + +- F2P targets: `TestLayerAndTensorToAPI` and `TestTensorToCopyCompatibility::test_copy_as_positional_argument` in `test/legacy_test/test_api_compatibility_part3.py`. +- P2P guard: `TestLayerTo::test_main` in `test/legacy_test/test_base_layer.py`. +- The standalone copy case passes `copy=True` as the third positional value to a same-dtype conversion and requires a distinct result. The base parser ignores that positional value, so this case is expected to fail before the fix and pass afterward. + +## Risks + +- A compatible Paddle build is required even though the patch itself is Python-only. +- The source test class changes Paddle's global dynamic/static mode in `tearDown`. Run the target class and the P2P guard in separate pytest processes so their global modes cannot leak into one another. +- The legacy P2P guard uses eager-only gradient internals and must run with `FLAGS_enable_pir_api=0`; otherwise it fails before exercising `Layer.to`. +- Later follow-up fixes to these APIs are outside this task. Verification must use the stated base commit and only the supplied patches. +- Verification was completed with a base-compatible local Paddle build and Python-source overlays. Maintainers should reproduce the Run/Test/Fix sequence in the designated verifier environment. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/instruction.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/instruction.md new file mode 100644 index 000000000..ea40ade9b --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/instruction.md @@ -0,0 +1,23 @@ +# 对齐 Tensor 与 Layer 的转换调用行为 + +Paddle 的 Tensor 和神经网络 Layer 转换接口目前无法一致地支持常见调用形式。请调整它们的可观察行为,使下列用法能够可靠工作。 + +## 行为要求 + +- 接受 dtype、device 或参考 Tensor 作为第一个位置参数。 +- 在适用场景中,支持同时以位置参数传入 device 和 dtype。 +- 支持以关键字参数传入 `device` 和 `dtype`。 +- 支持 `blocking`、`non_blocking` 和 `copy` 的合法位置参数或关键字参数形式。 +- 允许不传参数的转换调用。 +- 同时指定 `blocking` 和 `non_blocking` 时抛出 `TypeError`。 +- 位置参数过多或存在未知关键字参数时抛出 `TypeError`。 +- 第一个位置参数无法识别时抛出 `ValueError`。 +- Layer 转换应原地执行、返回同一个 Layer 对象,并递归应用于子层。 +- 传入参考 Tensor 时,目标对象应采用其转换属性。 + +## 验收标准 + +- Tensor 转换支持 dtype、device、参考 Tensor、阻塞选项和复制语义。 +- Layer 转换支持对应的 dtype、device、参考 Tensor 和阻塞选项调用,并保持对象身份不变。 +- 嵌套 Layer 和已注册 buffer 均得到一致转换;按照本任务目标行为,dtype 转换也适用于整数 buffer。 +- 现有 Layer 转换回归测试继续通过。 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/proposal.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/proposal.md index 52e0cdbe0..7b043f3cb 100644 --- a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/proposal.md +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/proposal.md @@ -37,24 +37,31 @@ - 目标测试文件: - `test/legacy_test/test_api_compatibility_part3.py` - `test/legacy_test/test_base_layer.py` -- 目标测试类:`TestLayerAndTensorToAPI`;P2P 护栏为 `TestLayerTo::test_main` +- 目标测试类:`TestLayerAndTensorToAPI`、`TestTensorToCopyCompatibility`;P2P 护栏为 `TestLayerTo::test_main` - 目标测试命令: ```bash + export PYTHONPATH="$(pwd)/test/legacy_test:$(pwd)/test/dygraph_to_static${PYTHONPATH:+:$PYTHONPATH}" + export FLAGS_enable_pir_api=0 python -m pytest \ test/legacy_test/test_api_compatibility_part3.py::TestLayerAndTensorToAPI \ + -q + python -m pytest \ + test/legacy_test/test_api_compatibility_part3.py::TestTensorToCopyCompatibility::test_copy_as_positional_argument \ + -q + python -m pytest \ test/legacy_test/test_base_layer.py::TestLayerTo::test_main \ -q ``` - 关键 F2P 场景: - `Layer.to` 和 `Tensor.to` 接受 dtype、device、参考 Tensor 的位置参数,以及对应的关键字参数组合。 - - 支持 `blocking`、`non_blocking` 和 `copy` 的合法调用形式;测试补丁应至少增加一个 `Tensor.to(dtype='float64', copy=True)` 或等价的 `copy` smoke case。新增 benchmark 测试可以补充原 PR 未覆盖的边界,但不能引入 #78593 之后的语义。 + - 支持 `blocking`、`non_blocking` 和 `copy` 的合法调用形式;测试补丁增加同 dtype Tensor 的位置参数用例 `tensor.to(paddle.float64, True, True)`,并验证 `copy=True` 返回不同对象。该用例不得引入 #78593 之后的语义。 - 同时设置互相冲突的阻塞参数时抛出 `TypeError`;无参数调用保持合法;参数过多或未知关键字抛出 `TypeError`;无法识别的首个位置参数抛出 `ValueError`。 - `Layer.to` 返回原对象,并对子层执行一致的转换。 - 按 #78301 合入时的行为,使用 dtype 转换 Layer 时,测试中的整数 buffer 也会转换到目标 dtype。后续 #78839 对这一行为的修正不纳入本任务。 - 修复前预期:在 `base_commit` 上应用围绕 #78301 目标行为构造的 `tests/test.patch` 后,新增兼容性用例应出现失败或错误。例如 dtype 作为首个位置参数时会被错误解释为 device,部分位置参数组合、无参数调用或阻塞参数冲突无法按目标行为执行,且新增的 Layer/Tensor 统一调用场景不能全部通过。 -- 修复后预期:继续应用仅来自 #78301 非测试文件的 `solution/code.patch` 后,`TestLayerAndTensorToAPI` 及指定的既有 Layer 回归测试全部通过,并满足 #78301 合入时的参数解析和转换行为。 +- 修复后预期:继续应用仅来自 #78301 非测试文件的 `solution/code.patch` 后,`TestLayerAndTensorToAPI`、`TestTensorToCopyCompatibility::test_copy_as_positional_argument` 及指定的既有 Layer 回归测试全部通过,并满足 #78301 合入时的参数解析和转换行为。 - P2P 候选:`test/legacy_test/test_base_layer.py::TestLayerTo::test_main` 在 #78301 中未被修改,可作为 Layer.to 既有行为回归护栏;完整任务包阶段还应从同模块中挑选未被 `test.patch` 修改、且在 base 和修复后都通过的存量用例。 ## 6. 环境与资源 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/solution/code.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/solution/code.patch new file mode 100644 index 000000000..0bf2e92bf --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/solution/code.patch @@ -0,0 +1,508 @@ +diff --git a/python/paddle/base/dygraph/tensor_patch_methods.py b/python/paddle/base/dygraph/tensor_patch_methods.py +--- a/python/paddle/base/dygraph/tensor_patch_methods.py ++++ b/python/paddle/base/dygraph/tensor_patch_methods.py +@@ -723,34 +723,74 @@ def to( + self: Tensor, + device: PlaceLike, + dtype: DTypeLike | None = ..., +- blocking: bool | None = ..., ++ blocking: bool = ..., ++ copy: bool = ..., ++ *, ++ non_blocking: bool = ..., + ) -> Tensor: ... + + @overload + def to( +- self: Tensor, dtype: DTypeLike, blocking: bool | None = ... ++ self: Tensor, ++ dtype: DTypeLike, ++ blocking: bool = ..., ++ copy: bool = ..., ++ *, ++ non_blocking: bool = ..., + ) -> Tensor: ... + + @overload + def to( +- self: Tensor, other: Tensor, blocking: bool | None = ... ++ self: Tensor, ++ other: Tensor, ++ blocking: bool = ..., ++ copy: bool = ..., ++ *, ++ non_blocking: bool = ..., + ) -> Tensor: ... + + @framework.dygraph_only + def to(self: Tensor, *args, **kwargs): + """ + Performs Tensor dtype and/or device conversion. A paddle.dtype and place +- are inferred from the arguments of ``self.to(*args, **kwargs)``.There are +- three ways to call `to`: ++ are inferred from the arguments of ``self.to(*args, **kwargs)``. + +- 1. to(dtype, blocking=True) +- 2. to(device, dtype=None, blocking=True) +- 3. to(other, blocking=True) ++ This API has three calling conventions: + +- **Notes**: +- **If the self Tensor already has the correct dtype and device, +- then self is returned. Otherwise, the returned tensor is a copy of self with +- the desired dtype and device.** ++ 1. ``to(device=None, dtype=None, blocking=True, copy=False, *, non_blocking=False)``: ++ Moves and/or casts the Tensor. ++ ++ 2. ``to(dtype, blocking=True, copy=False, *, non_blocking=False)``: ++ Equivalent to ``self.to(device=None, dtype=dtype, ...)``. ++ ++ 3. ``to(other, blocking=True, copy=False, *, non_blocking=False)``: ++ Equivalent to ``self.to(device=other.place, dtype=other.dtype, ...)``. ++ ++ .. note:: ++ If the self Tensor already has the correct dtype and device, ++ then self is returned. Otherwise, the returned tensor is a copy of ++ self with the desired dtype and device. ++ ++ Args: ++ device (str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional): ++ The device to move to. Default: ``None``. ++ dtype (str|numpy.dtype|paddle.dtype|None, optional): ++ The desired data type. Default: ``None``. ++ blocking (bool, optional): ++ If ``False`` and the source is in pinned memory, the copy will be ++ asynchronous with respect to the host. Default: ``True``. ++ copy (bool, optional): ++ If ``True``, a new Tensor is created even when the Tensor ++ already matches the desired conversion. Default: ``False``. ++ other (Tensor, optional): ++ Tensor whose dtype and device are the desired dtype and device. ++ ++ Keyword args: ++ non_blocking (bool, optional): ++ If ``True`` and the source is in pinned memory, the copy will be ++ asynchronous with respect to the host. Default: ``False``. ++ ``non_blocking`` and ``blocking`` are mutually exclusive ++ and cannot both be set at the same time. + + Returns: + Tensor: self +@@ -786,95 +826,9 @@ def to(self: Tensor, *args, **kwargs): + Tensor(shape=[3], dtype=int16, place=Place(gpu:0), stop_gradient=True, + [4, 5, 6]) + """ +- device = None +- dtype = None +- blocking = None +- +- if "non_blocking" in kwargs: +- non_blocking = kwargs.pop("non_blocking") +- else: +- non_blocking = False +- +- if "copy" in kwargs: +- copy_tensor = kwargs.pop("copy") +- else: +- copy_tensor = False ++ from paddle.nn.layer.layers import _parse_to_args + +- size_args = len(args) +- size_kwargs = len(kwargs) +- +- def get_device_dtype_from_tensor(other): +- if other is not None: +- device = str(other.place)[6:-1] +- dtype = other.dtype +- return device, dtype +- else: +- return None, None +- +- if size_args + size_kwargs > 3 or size_args + size_kwargs == 0: +- raise TypeError( +- "to() received too many arguments - expected one of:\n \ +- * (Union[str, paddle.CPUPlace(), paddle.CUDAPlace(), paddle.CUDAPinnedPlace(), paddle.XPUPlace(), paddle.CustomPlace()] \ +- device, Union[str, paddle.dtype, numpy.dtype] dtype, bool blocking)\n \ +- * (Union[str, paddle.dtype, numpy.dtype] dtype, bool blocking)\n \ +- * (paddle.Tensor other, bool blocking) " +- ) +- valid_keys = {"device", "dtype", "blocking", "other"} +- valid_dtypes = [ +- "bfloat16", +- "float16", +- "float32", +- "float64", +- "int8", +- "int16", +- "int32", +- "int64", +- "uint8", +- "complex64", +- "complex128", +- "bool", +- ] +- invalid_keys = set(kwargs.keys()) - valid_keys +- if len(invalid_keys) != 0: +- raise TypeError( +- "to() got an unexpected keyword argument " +- + next(iter(invalid_keys)) +- ) +- if size_args > 0: +- if isinstance(args[0], paddle.Tensor): +- device, dtype = get_device_dtype_from_tensor(args[0]) +- if size_args == 2: +- blocking = args[1] +- else: +- blocking = kwargs.get("blocking", None) +- elif ( +- isinstance(args[0], (paddle.dtype, np.dtype)) +- or isinstance(args[0], str) +- and args[0].lower() in valid_dtypes +- ): +- dtype = args[0] +- if size_args == 2: +- blocking = args[1] +- else: +- blocking = kwargs.get("blocking", None) +- else: +- device = args[0] +- if size_args == 2: +- dtype = args[1] +- elif size_args == 3: +- dtype, blocking = args[1], args[2] +- else: +- dtype = kwargs.get("dtype", None) +- blocking = kwargs.get("blocking", None) +- else: +- device = kwargs.get("device", None) +- dtype = kwargs.get("dtype", None) +- blocking = kwargs.get("blocking", None) +- if device is None and dtype is None: +- device, dtype = get_device_dtype_from_tensor( +- kwargs.get("other", None) +- ) +- blocking = False if not blocking or non_blocking else True ++ device, dtype, blocking, copy_tensor = _parse_to_args(*args, **kwargs) + return self._to(device, dtype, blocking, copy_tensor) + + def clear_grad(self: Tensor) -> None: +diff --git a/python/paddle/nn/layer/layers.py b/python/paddle/nn/layer/layers.py +--- a/python/paddle/nn/layer/layers.py ++++ b/python/paddle/nn/layer/layers.py +@@ -155,6 +155,142 @@ def _addindent(string, indent): + return s1[0] + '\n' + '\n'.join(s2) + + ++def _parse_to_args(*args, **kwargs): ++ """Parse arguments for .to(), shared by Tensor.to and Layer.to. ++ ++ Calling conventions:: ++ ++ to(device=None, dtype=None, blocking=True, copy=False, *, non_blocking=False) ++ to(dtype, blocking=True, copy=False, *, non_blocking=False) ++ to(tensor, blocking=True, copy=False, *, non_blocking=False) ++ ++ Returns: ++ tuple: (device, dtype, blocking, copy) ++ """ ++ valid_dtypes = { ++ 'bfloat16', ++ 'float16', ++ 'float32', ++ 'float64', ++ 'int8', ++ 'int16', ++ 'int32', ++ 'int64', ++ 'uint8', ++ 'complex64', ++ 'complex128', ++ 'bool', ++ } ++ ++ # Extract keyword-only parameter ++ non_blocking = kwargs.pop('non_blocking', None) ++ copy = kwargs.pop('copy', False) ++ ++ device = None ++ dtype = None ++ blocking = None ++ ++ size_args = len(args) ++ size_kwargs = len(kwargs) ++ ++ if size_args + size_kwargs > 4: ++ raise TypeError( ++ "to() received too many arguments - expected one of:\n" ++ " to(device=None, dtype=None, blocking=True, *, non_blocking=False)\n" ++ " to(dtype, blocking=True, *, non_blocking=False)\n" ++ " to(tensor, blocking=True, copy=False, *, non_blocking=False)" ++ ) ++ ++ valid_keys = {'device', 'dtype', 'blocking', 'other', 'tensor'} ++ invalid_keys = set(kwargs.keys()) - valid_keys ++ if invalid_keys: ++ raise TypeError( ++ "to() got an unexpected keyword argument '" ++ + next(iter(invalid_keys)) ++ + "'" ++ ) ++ ++ if size_args > 0: ++ first = args[0] ++ if isinstance(first, paddle.Tensor): ++ # to(tensor, blocking=True, copy=False) ++ device = first.place ++ dtype = first.dtype ++ if size_args == 2: ++ blocking = args[1] ++ elif size_args == 3: ++ blocking, copy = args[1], args[2] ++ else: ++ blocking = kwargs.get('blocking') ++ elif isinstance(first, (core.DataType, VarDesc.VarType, np.dtype)) or ( ++ isinstance(first, str) and first.lower() in valid_dtypes ++ ): ++ # to(dtype, blocking=True, copy=False) ++ dtype = first ++ if size_args >= 2: ++ blocking = args[1] ++ else: ++ blocking = kwargs.get('blocking') ++ if size_args >= 3: ++ copy = args[2] ++ elif first is None or isinstance(first, (str, core.Place)): ++ # to(device, dtype=None, blocking=True, copy=False) ++ device = first ++ if size_args >= 2: ++ dtype = args[1] ++ if size_args >= 3: ++ blocking = args[2] ++ if size_args >= 4: ++ copy = args[3] ++ if size_args < 2: ++ dtype = kwargs.get('dtype') ++ if size_args < 3: ++ blocking = kwargs.get('blocking') ++ else: ++ raise ValueError( ++ f"device should be type of str, paddle.CPUPlace, paddle.CUDAPlace, " ++ f"paddle.CUDAPinnedPlace, paddle.XPUPlace, or paddle.base.libpaddle.Place, " ++ f"but got {type(first).__name__}" ++ ) ++ else: ++ tensor_arg = kwargs.get('other') ++ if tensor_arg is None: ++ tensor_arg = kwargs.get('tensor') ++ if tensor_arg is not None: ++ device = tensor_arg.place ++ dtype = tensor_arg.dtype ++ blocking = kwargs.get('blocking') ++ else: ++ device = kwargs.get('device') ++ dtype = kwargs.get('dtype') ++ blocking = kwargs.get('blocking') ++ ++ # Validate blocking / non_blocking types ++ if blocking is not None: ++ assert isinstance(blocking, bool), ( ++ "blocking value error, must be the True, False or None" ++ ) ++ if non_blocking is not None: ++ assert isinstance(non_blocking, bool), ( ++ "non_blocking value error, must be the True or False" ++ ) ++ ++ # blocking and non_blocking cannot both be explicitly set ++ if blocking is not None and non_blocking is not None: ++ raise TypeError( ++ "to() received both 'blocking' and 'non_blocking' arguments. " ++ "These are mutually exclusive, please use only one of them." ++ ) ++ ++ # Resolve blocking ++ if non_blocking is not None: ++ blocking = not non_blocking ++ elif blocking is None: ++ blocking = True ++ ++ return device, dtype, blocking, copy ++ ++ + def _layer_trans_dtype(layer, dtype, excluded_layers): + if type(layer) in excluded_layers: + return +@@ -2817,28 +2953,75 @@ def load_state_dict( + ) + return _IncompatibleKeys(missing_keys, unexpected_keys) + ++ @overload + def to( + self, +- device: PlaceLike | None = None, +- dtype: DTypeLike | None = None, +- blocking: bool | None = None, +- non_blocking: bool | None = None, +- ) -> Self: ++ device: PlaceLike | None = ..., ++ dtype: DTypeLike | None = ..., ++ blocking: bool = ..., ++ *, ++ non_blocking: bool = ..., ++ ) -> Self: ... ++ ++ @overload ++ def to( ++ self, ++ dtype: DTypeLike, ++ blocking: bool = ..., ++ *, ++ non_blocking: bool = ..., ++ ) -> Self: ... ++ ++ @overload ++ def to( ++ self, ++ tensor: Tensor, ++ blocking: bool = ..., ++ *, ++ non_blocking: bool = ..., ++ ) -> Self: ... ++ ++ def to(self, *args, **kwargs) -> Self: + ''' +- Cast the parameters and buffers of Layer by the give device, dtype and blocking. ++ Move and/or cast the parameters and buffers. + +- Parameters: +- device(str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional): The device of the Layer which want to be stored. +- If None, the device is the same with the original Tensor. If device is string, it can be ``cpu``, ``gpu:x`` and ``xpu:x``, where ``x`` is the +- index of the GPUs or XPUs. Default: None. ++ This API has three calling conventions: ++ ++ 1. ``to(device=None, dtype=None, blocking=True, *, non_blocking=False)``: ++ Moves and/or casts the parameters and buffers. + +- dtype(str|numpy.dtype|paddle.dtype|None, optional): The type of the data. If None, the dtype is the same with the original Tensor. Default: None. ++ 2. ``to(dtype, blocking=True, *, non_blocking=False)``: ++ Equivalent to ``self.to(device=None, dtype=dtype, ...)``. + +- blocking(bool|None, optional): If False and the source is in pinned memory, the copy will be +- asynchronous with respect to the host. Otherwise, the argument has no effect. If None, the blocking is set True. Default: None. ++ 3. ``to(tensor, blocking=True, *, non_blocking=False)``: ++ Equivalent to ``self.to(device=tensor.place, dtype=tensor.dtype, ...)``. ++ ++ .. note:: ++ This method modifies the layer in-place. + +- non_blocking(bool|None, optional): If True and the source is in pinned memory, the copy will be +- asynchronous with respect to the host. Otherwise, the argument has no effect. If None, the non_blocking is set False. Default: None. ++ Args: ++ device (str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional): ++ The device of the Layer which want to be stored. ++ If None, the device is the same with the original Tensor. ++ If device is string, it can be ``cpu``, ``gpu:x`` and ``xpu:x``, ++ where ``x`` is the index of the GPUs or XPUs. Default: ``None``. ++ dtype (str|numpy.dtype|paddle.dtype|None, optional): ++ The type of the data. If None, the dtype is the same with the ++ original Tensor. Default: ``None``. ++ blocking (bool, optional): ++ If ``False`` and the source is in pinned memory, the copy will be ++ asynchronous with respect to the host. Otherwise, the argument ++ has no effect. Default: ``True``. ++ tensor (Tensor, optional): ++ Tensor whose dtype and device are the desired dtype and device ++ for all parameters and buffers in this layer. ++ ++ Keyword args: ++ non_blocking (bool, optional): ++ If ``True`` and the source is in pinned memory, the copy will be ++ asynchronous with respect to the host. Default: ``False``. ++ ``non_blocking`` and ``blocking`` are mutually exclusive ++ and cannot both be set at the same time. + + Returns: + self +@@ -2883,11 +3066,11 @@ def to( + [-0.58883440, 0.99266374]]) + + ''' ++ device, dtype, blocking, _ = _parse_to_args(*args, **kwargs) + return self._to_impl( + device=device, + dtype=dtype, + blocking=blocking, +- non_blocking=non_blocking, + include_sublayers=True, + floating_only=False, + ) +@@ -2994,41 +3177,32 @@ def _to_impl( + device: PlaceLike | None = None, + dtype: DTypeLike | None = None, + blocking: bool | None = None, +- non_blocking: bool | None = None, + include_sublayers: bool = True, + floating_only: bool = False, + ): + ''' + Cast the parameters and buffers of Layer by the give device, dtype and blocking. + + Parameters: +- device(str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional): The device of the Layer which want to be stored. +- If None, the device is the same with the original Tensor. If device is string, it can be ``cpu``, ``gpu:x`` and ``xpu:x``, where ``x`` is the +- index of the GPUs or XPUs. Default: None. +- +- dtype(str|numpy.dtype|paddle.dtype|None, optional): The type of the data. If None, the dtype is the same with the original Tensor. Default: None. +- +- blocking(bool|None, optional): If False and the source is in pinned memory, the copy will be +- asynchronous with respect to the host. Otherwise, the argument has no effect. If None, the blocking is set True. Default: None. +- +- non_blocking(bool|None, optional): If True and the source is in pinned memory, the copy will be +- asynchronous with respect to the host. Otherwise, the argument has no effect. If None, the non_blocking is set False. Default: None. +- +- include_sublayers(bool, optional): If True, deal with self and all sublayers parameters and buffers, if not only deal with self parameters and buffers. Default: True. +- +- floating_only(bool, optional): If True, only cast all floating point parameters and buffers of Layer by the give device, dtype and blocking. ++ device(str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional): ++ The device of the Layer which want to be stored. Default: None. ++ dtype(str|numpy.dtype|paddle.dtype|None, optional): ++ The type of the data. Default: None. ++ blocking(bool|None, optional): ++ If False and the source is in pinned memory, the copy will be ++ asynchronous with respect to the host. Default: None. ++ include_sublayers(bool, optional): ++ If True, deal with self and all sublayers parameters and ++ buffers. Default: True. ++ floating_only(bool, optional): ++ If True, only cast floating point parameters and buffers. + + Returns: + self + + ''' + +- if ( +- device is None +- and dtype is None +- and blocking is None +- and non_blocking is None +- ): ++ if device is None and dtype is None and blocking is None: + return self + + if device is not None: +@@ -3051,14 +3225,6 @@ def _to_impl( + "blocking value error, must be the True, False or None" + ) + +- if non_blocking is None: +- non_blocking = False +- else: +- assert isinstance(non_blocking, bool), ( +- "non_blocking value error, must be the True, False or None" +- ) +- blocking = False if not blocking or non_blocking else True +- + def transform(t, device, dtype, blocking): + if floating_only and (not paddle.is_floating_point(t)): + return t diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/tests/test.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/tests/test.patch new file mode 100644 index 000000000..f65e20646 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/tests/test.patch @@ -0,0 +1,274 @@ +diff --git a/test/legacy_test/test_api_compatibility_part3.py b/test/legacy_test/test_api_compatibility_part3.py +--- a/test/legacy_test/test_api_compatibility_part3.py ++++ b/test/legacy_test/test_api_compatibility_part3.py +@@ -1156,6 +1156,270 @@ def test_static_Compatibility(self): + np.testing.assert_array_equal(ref_out, out) + + ++class TestLayerAndTensorToAPI(unittest.TestCase): ++ """Test paddle.nn.Layer.to and paddle.Tensor.to alignment with PyTorch.""" ++ ++ def setUp(self): ++ paddle.disable_static() ++ ++ def tearDown(self): ++ paddle.enable_static() ++ ++ def _make_model(self): ++ """Create a model with float params and an int buffer.""" ++ ++ class Model(paddle.nn.Layer): ++ def __init__(self): ++ super().__init__() ++ self.linear = paddle.nn.Linear(3, 2) ++ self.register_buffer( ++ 'int_buf', paddle.to_tensor([1, 2, 3], dtype='int32') ++ ) ++ ++ def forward(self, x): ++ return self.linear(x) ++ ++ return Model() ++ ++ # ---- Layer.to: Positional dtype ---- ++ ++ def test_layer_positional_paddle_dtype(self): ++ """Layer.to(paddle.float64)""" ++ linear = paddle.nn.Linear(2, 2) ++ ret = linear.to(paddle.float64) ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ self.assertEqual(linear.bias.dtype, paddle.float64) ++ self.assertIs(ret, linear) ++ ++ def test_layer_positional_dtype_string(self): ++ """Layer.to('float64')""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to('float64') ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ ++ def test_layer_positional_dtype_float16(self): ++ """Layer.to(paddle.float16)""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(paddle.float16) ++ self.assertEqual(linear.weight.dtype, paddle.float16) ++ ++ # ---- Layer.to: Positional tensor ---- ++ ++ def test_layer_positional_tensor(self): ++ """Layer.to(tensor) -- match tensor's dtype and device""" ++ linear = paddle.nn.Linear(2, 2) ++ ref = paddle.to_tensor([1.0], dtype='float64') ++ linear.to(ref) ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ ++ # ---- Layer.to: Positional device ---- ++ ++ def test_layer_positional_device_string(self): ++ """Layer.to('cpu')""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to('cpu') ++ self.assertTrue(linear.weight.place.is_cpu_place()) ++ ++ def test_layer_positional_device_and_dtype(self): ++ """Layer.to('cpu', 'float64')""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to('cpu', 'float64') ++ self.assertTrue(linear.weight.place.is_cpu_place()) ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ ++ # ---- Layer.to: Keyword args ---- ++ ++ def test_layer_keyword_device(self): ++ """Layer.to(device='cpu')""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(device='cpu') ++ self.assertTrue(linear.weight.place.is_cpu_place()) ++ ++ def test_layer_keyword_dtype(self): ++ """Layer.to(dtype='float64')""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(dtype='float64') ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ ++ def test_layer_keyword_device_and_dtype(self): ++ """Layer.to(device='cpu', dtype='float64')""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(device='cpu', dtype='float64') ++ self.assertTrue(linear.weight.place.is_cpu_place()) ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ ++ def test_layer_keyword_non_blocking(self): ++ """Layer.to(dtype='float64', non_blocking=False)""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(dtype='float64', non_blocking=False) ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ ++ def test_layer_keyword_blocking(self): ++ """Layer.to(device='cpu', blocking=True)""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(device='cpu', blocking=True) ++ self.assertTrue(linear.weight.place.is_cpu_place()) ++ ++ # ---- Layer.to: No args ---- ++ ++ def test_layer_no_args(self): ++ """Layer.to() -- returns self unchanged""" ++ linear = paddle.nn.Linear(2, 2) ++ original_dtype = linear.weight.dtype ++ ret = linear.to() ++ self.assertIs(ret, linear) ++ self.assertEqual(linear.weight.dtype, original_dtype) ++ ++ # ---- Layer.to: all-dtype casting ---- ++ ++ def test_layer_cast_all_with_positional_dtype(self): ++ """Layer.to(dtype) casts ALL params and buffers, including int buf.""" ++ model = self._make_model() ++ self.assertEqual(model.int_buf.dtype, paddle.int32) ++ model.to(paddle.float64) ++ self.assertEqual(model.linear.weight.dtype, paddle.float64) ++ self.assertEqual(model.int_buf.dtype, paddle.float64) ++ ++ def test_layer_cast_all_with_keyword_dtype(self): ++ """Layer.to(dtype='float64') casts ALL params and buffers.""" ++ model = self._make_model() ++ model.to(dtype='float64') ++ self.assertEqual(model.linear.weight.dtype, paddle.float64) ++ self.assertEqual(model.int_buf.dtype, paddle.float64) ++ ++ def test_layer_cast_all_with_tensor(self): ++ """Layer.to(tensor) casts ALL params and buffers.""" ++ model = self._make_model() ++ ref = paddle.to_tensor([1.0], dtype='float64') ++ model.to(ref) ++ self.assertEqual(model.linear.weight.dtype, paddle.float64) ++ self.assertEqual(model.int_buf.dtype, paddle.float64) ++ ++ # ---- Layer.to: sublayers and chaining ---- ++ ++ def test_layer_sublayers_cast(self): ++ """Layer.to() should recurse into sublayers.""" ++ model = paddle.nn.Sequential( ++ paddle.nn.Linear(3, 4), paddle.nn.Linear(4, 2) ++ ) ++ model.to(paddle.float64) ++ for sub in model.sublayers(): ++ if hasattr(sub, 'weight'): ++ self.assertEqual(sub.weight.dtype, paddle.float64) ++ ++ def test_layer_returns_self(self): ++ """Layer.to() should return self for chaining.""" ++ linear = paddle.nn.Linear(2, 2) ++ self.assertIs(linear.to(paddle.float64), linear) ++ ++ def test_layer_sequential_to_calls(self): ++ """Multiple Layer.to() calls should work correctly.""" ++ linear = paddle.nn.Linear(2, 2) ++ linear.to(paddle.float64) ++ self.assertEqual(linear.weight.dtype, paddle.float64) ++ linear.to('float32') ++ self.assertEqual(linear.weight.dtype, paddle.float32) ++ ++ # ---- Tensor.to ---- ++ ++ def test_tensor_positional_dtype(self): ++ """Tensor.to(paddle.float64)""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to(paddle.float64) ++ self.assertEqual(out.dtype, paddle.float64) ++ ++ def test_tensor_positional_dtype_string(self): ++ """Tensor.to('float64')""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to('float64') ++ self.assertEqual(out.dtype, paddle.float64) ++ ++ def test_tensor_positional_device(self): ++ """Tensor.to('cpu')""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to('cpu') ++ self.assertTrue(out.place.is_cpu_place()) ++ ++ def test_tensor_positional_device_and_dtype(self): ++ """Tensor.to('cpu', 'float64')""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to('cpu', 'float64') ++ self.assertTrue(out.place.is_cpu_place()) ++ self.assertEqual(out.dtype, paddle.float64) ++ ++ def test_tensor_positional_other(self): ++ """Tensor.to(other_tensor)""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ ref = paddle.to_tensor([1], dtype='int32') ++ out = t.to(ref) ++ self.assertEqual(out.dtype, paddle.int32) ++ ++ def test_tensor_keyword_dtype(self): ++ """Tensor.to(dtype='float64')""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to(dtype='float64') ++ self.assertEqual(out.dtype, paddle.float64) ++ ++ def test_tensor_keyword_device(self): ++ """Tensor.to(device='cpu')""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to(device='cpu') ++ self.assertTrue(out.place.is_cpu_place()) ++ ++ def test_tensor_keyword_non_blocking(self): ++ """Tensor.to(dtype='float64', non_blocking=False)""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to(dtype='float64', non_blocking=False) ++ self.assertEqual(out.dtype, paddle.float64) ++ ++ def test_tensor_no_args(self): ++ """Tensor.to() -- returns self""" ++ t = paddle.to_tensor([1.0, 2.0]) ++ out = t.to() ++ self.assertEqual(out.dtype, t.dtype) ++ ++ # ---- blocking / non_blocking conflict ---- ++ ++ def test_blocking_non_blocking_conflict_raises(self): ++ """Setting both blocking and non_blocking raises TypeError.""" ++ linear = paddle.nn.Linear(2, 2) ++ with self.assertRaises(TypeError): ++ linear.to(dtype='float64', blocking=True, non_blocking=False) ++ ++ def test_tensor_blocking_non_blocking_conflict_raises(self): ++ """Tensor: setting both blocking and non_blocking raises TypeError.""" ++ t = paddle.to_tensor([1.0]) ++ with self.assertRaises(TypeError): ++ t.to(dtype='float64', blocking=True, non_blocking=False) ++ ++ # ---- Error handling ---- ++ ++ def test_too_many_args(self): ++ """to() with too many arguments raises TypeError.""" ++ linear = paddle.nn.Linear(2, 2) ++ with self.assertRaises(TypeError): ++ linear.to('cpu', 'float64', True, False, 'extra') ++ ++ def test_unexpected_keyword(self): ++ """to() with unexpected keyword raises TypeError.""" ++ linear = paddle.nn.Linear(2, 2) ++ with self.assertRaises(TypeError): ++ linear.to(foo='bar') ++ ++ def test_invalid_first_arg(self): ++ """to() with invalid first arg raises ValueError.""" ++ linear = paddle.nn.Linear(2, 2) ++ with self.assertRaises(ValueError): ++ linear.to(123) ++ ++ ++class TestTensorToCopyCompatibility(unittest.TestCase): ++ def test_copy_as_positional_argument(self): ++ tensor = paddle.to_tensor([1.0, 2.0], dtype=paddle.float64) ++ result = tensor.to(paddle.float64, True, True) ++ self.assertIsNot(result, tensor) ++ ++ + # Test select_scatter compatibility + class TestSelectScatterAPICompatibility(unittest.TestCase): + def setUp(self): diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78301/tests/test.sh b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/tests/test.sh new file mode 100755 index 000000000..f06f30492 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78301/tests/test.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +export PYTHONPATH="$(pwd)/test/legacy_test:$(pwd)/test/dygraph_to_static${PYTHONPATH:+:$PYTHONPATH}" +export FLAGS_enable_pir_api=0 + +PYTHON_BIN="${PYTHON_BIN:-python}" + +"$PYTHON_BIN" -m pytest \ + test/legacy_test/test_api_compatibility_part3.py::TestLayerAndTensorToAPI \ + -q + +"$PYTHON_BIN" -m pytest \ + test/legacy_test/test_api_compatibility_part3.py::TestTensorToCopyCompatibility::test_copy_as_positional_argument \ + -q + +"$PYTHON_BIN" -m pytest \ + test/legacy_test/test_base_layer.py::TestLayerTo::test_main \ + -q