diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/README.md new file mode 100644 index 000000000..7b8145b14 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/README.md @@ -0,0 +1,44 @@ +# PaddlePaddle__Paddle-74491 + +This directory converts Paddle PR #74491 into a SWE-Paddle community task candidate. + +## Source + +| Field | Value | +| --- | --- | +| Repo | `PaddlePaddle/Paddle` | +| PR | [#74491](https://github.com/PaddlePaddle/Paddle/pull/74491) | +| PR title | [API compatibility] add new API `paddle.Tensor.requires_grad` | +| Base commit | `01666a6667e744874d7f7c379b2649d8bae67f09` | +| Merged at | `2025-08-13` | +| Task type | `feature_enhancement` | +| Resource | CPU | + +## Summary + +Add a `requires_grad` compatibility property for Paddle Tensor-like values across dynamic, static, and PIR execution modes while keeping existing gradient-control behavior intact. + +## Why This Is A Good SWE-Paddle Candidate + +- The task comes from a merged Paddle API-compatibility PR with a narrow, well-defined behavior contract. +- The observable behavior can be verified deterministically without a source build, GPU, network access, or distributed runtime. +- The change spans three execution modes, so the task checks consistency rather than a single isolated code path. +- Existing Tensor metadata behavior can be protected independently with a P2P test. + +## Files + +- `proposal.md`: candidate proposal for maintainer triage. +- `instruction.md`: self-contained problem statement for the coding agent. +- `solution/code.patch`: gold patch from the merged PR, limited to production files. +- `tests/test.patch`: test patch exposing the target behavior. +- `tests/test.sh`: minimal target test command. +- `environment/README.md`: environment notes for reproduction. +- `README.md`: task overview and verification entrypoint. + +## Verification + +```bash +bash tests/test.sh +``` + +Expected behavior: applying `tests/test.patch` to `base_commit` should fail on the new `requires_grad` behavior while the P2P remains valid; applying both `tests/test.patch` and `solution/code.patch` should pass all target tests. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/environment/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/environment/README.md new file mode 100644 index 000000000..a3eced8d7 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/environment/README.md @@ -0,0 +1,27 @@ +# Environment Notes + +This candidate is part of the SWE-Paddle community task set. + +## Expected Environment + +- Repository: `PaddlePaddle/Paddle` +- Base commit: `01666a6667e744874d7f7c379b2649d8bae67f09` +- Resource: CPU +- GPU required: no +- Build path: Python-only AST overlay; no Paddle source build is required. + +## Run Order + +1. Check out `PaddlePaddle/Paddle` at the base commit. +2. Apply `tests/test.patch`. +3. Run `bash tests/test.sh`; the target behavior should fail before the fix. +4. Apply `solution/code.patch`. +5. Run `bash tests/test.sh` again; the target behavior should pass after the gold patch. + +## Minimal Test Command + +```bash +bash tests/test.sh +``` + +The verifier is responsible for deriving stable F2P and P2P node IDs from repeated runs. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/instruction.md b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/instruction.md new file mode 100644 index 000000000..253f3adb8 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/instruction.md @@ -0,0 +1,26 @@ +# 为 Tensor 增加 `requires_grad` 属性 + +## 详细描述 + +Paddle 目前通过 `stop_gradient` 控制 Tensor 是否参与梯度计算。为了提升 API 兼容性和用户体验,还需要提供语义对应的 `requires_grad` 属性。 + +需要为动态图 Tensor、传统静态图 Variable,以及 PIR 静态图中的 `paddle.pir.Value` 增加 `requires_grad`。其中,`paddle.pir.Value` 是 PIR 计算图中表示算子输入和输出数据的对象,可以理解为 PIR 静态图中的 Tensor-like 符号对象。 + +- `requires_grad=True` 时,`stop_gradient=False` +- `requires_grad=False` 时,`stop_gradient=True` + +`requires_grad` 应支持读取和设置,并且只能赋布尔值。传入其他类型时应抛出 `TypeError`。 + +## 验收说明 + +- 动态图 Tensor、静态图 Variable 和 PIR Value 均支持 `requires_grad` +- `requires_grad` 的值始终与 `stop_gradient` 相反 +- 设置 `requires_grad` 后,`stop_gradient` 应同步更新 +- 为 `requires_grad` 赋非布尔值时应抛出 `TypeError` +- 现有 `stop_gradient` 的行为保持不变 + +## 技术要求 + +- 熟悉 Python +- 了解 Paddle 动态图、静态图和 PIR +- 了解 `stop_gradient` 与梯度计算的关系 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/proposal.md b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/proposal.md new file mode 100644 index 000000000..8eaa5f1e5 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/proposal.md @@ -0,0 +1,53 @@ +# Task Proposal: PaddlePaddle__Paddle-74491 + +## 1. 来源信息 + +* Instance ID:`PaddlePaddle__Paddle-74491` +* PR 链接:https://github.com/PaddlePaddle/Paddle/pull/74491 +* PR 标题:`[API compatibility] add new API paddle.Tensor.requires_grad` +* `base_commit`:`01666a6667e744874d7f7c379b2649d8bae67f09` +* merged 时间:`2025-08-13` +* 你的身份:熟悉该模块的 contributor +* 后续联系人:TBD + +## 2. 问题一句话 + +为动态图、静态图和 PIR 模式下的 Tensor 增加统一的 `requires_grad` 属性,使调用方能够通过兼容接口读取和设置 Tensor 是否参与梯度计算。 + +## 3. 为什么适合作为 SWE-Paddle 样本 + +* **真实性**:该任务来自已合入 Paddle `develop` 分支的 API compatibility PR,不是人工构造的需求。 +* **代表性**:该任务涉及常用的 Tensor 梯度控制接口,并要求动态图、静态图和 PIR 模式保持一致的属性行为。 +* **边界清楚**:目标行为集中在 `requires_grad` 的读取、赋值、类型校验,以及它与现有 `stop_gradient` 状态之间的对应关系。 +* **非平凡性**:任务需要在三类 Tensor 接口中提供一致的属性语义,同时保证现有梯度控制和 Tensor 元数据行为不受影响。 + +## 4. 任务类型和标签 + +* 任务类型:`feature_enhancement` +* 执行后端:`cpu` +* 设备范围:`cpu_only` +* 模块标签:`[tensor, autograd, api_compatibility, dynamic_graph, static_graph, pir]` + +## 5. 验证思路 + +* 目标测试命令:`bash tests/test.sh` +* 目标测试文件:`test/swe_paddle/test_pr74491_requires_grad.py` +* 修复前预期:现有 Tensor 元数据和 `stop_gradient` 相关回归测试应通过;动态图、静态图和 PIR 模式下的 `requires_grad` 属性测试应失败。 +* 修复后预期:继续应用 `solution/code.patch` 后,三种模式下的 Tensor 均应满足 `requires_grad == (not stop_gradient)`;布尔赋值应正确更新梯度状态,非布尔赋值应抛出 `TypeError`,全部目标测试应通过。 +* P2P 候选:三种 Tensor 路径中现有的 `stop_gradient` 状态和 `dim` 等元数据行为保持不变。 + +## 6. 环境与资源 + +* 资源需求:CPU +* Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit` +* 是否能提供 Docker:暂无 +* patch 类型:Python-only +* 最小测试命令:`bash tests/test.sh` +* 是否有 oracle 日志:由 SWE-Paddle verifier 结果另行维护 + +## 7. 风险自查 + +* 泄露风险:`instruction.md` 只描述 `requires_grad` 的公开属性行为、类型要求和兼容性约束,不透露属性的注册位置、具体函数名称或实现步骤。 +* 环境风险:通过 AST overlay 隔离 source checkout 与当前运行环境之间的版本差异,避免依赖历史 Paddle native runtime。 +* flaky 风险:测试只验证确定性的布尔属性、异常类型和对象状态变化,不依赖随机数、GPU、并发或外部资源。 +* 拆分风险:动态图、静态图和 PIR 三条路径共同实现同一个跨执行模式 Tensor API,适合作为一个完整任务。 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/solution/code.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/solution/code.patch new file mode 100644 index 000000000..4a320e152 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/solution/code.patch @@ -0,0 +1,159 @@ +diff --git a/python/paddle/base/dygraph/math_op_patch.py b/python/paddle/base/dygraph/math_op_patch.py +index 6aa14dc8013470b2695374e8ab31302e8aa8c0a1..1571b7e0de27e65dbb51e81748d6aabc61deeaac 100644 +--- a/python/paddle/base/dygraph/math_op_patch.py ++++ b/python/paddle/base/dygraph/math_op_patch.py +@@ -286,6 +286,40 @@ def monkey_patch_math_tensor(): + out = _C_ops.transpose(var, perm) + return out + ++ @property ++ def requires_grad(self: Tensor) -> bool: ++ """ ++ Whether this Tensor requires gradient computation. ++ ++ This is a convenience property that returns the opposite of stop_gradient. ++ Setting requires_grad=True is equivalent to setting stop_gradient=False. ++ ++ Examples: ++ .. code-block:: python ++ ++ >>> import paddle ++ >>> x = paddle.randn([2, 3]) ++ >>> print(x.requires_grad) # False by default ++ >>> ++ >>> x.requires_grad = False ++ >>> print(x.stop_gradient) # True ++ """ ++ return not self.stop_gradient ++ ++ @requires_grad.setter ++ def requires_grad(self: Tensor, value: bool) -> None: ++ """ ++ Set whether this Tensor requires gradient computation. ++ ++ Args: ++ value (bool): True to enable gradient computation, False to disable. ++ """ ++ if not isinstance(value, bool): ++ raise TypeError( ++ f"requires_grad must be bool, but got {type(value)}" ++ ) ++ self.stop_gradient = not value ++ + eager_methods = [ + ('__neg__', _neg_), + ('__abs__', _abs_), +@@ -305,6 +339,7 @@ def monkey_patch_math_tensor(): + ('size', _size_), + ('T', _T_), + ('mT', _mT_), ++ ("requires_grad", requires_grad), + # for logical compare + ('__array_ufunc__', None), + ] +diff --git a/python/paddle/base/layers/math_op_patch.py b/python/paddle/base/layers/math_op_patch.py +index 5e22912256b0f2f50acc36b68085077f7fec4a1a..8239d53c535e7770f98f074085df886e6bb46a44 100644 +--- a/python/paddle/base/layers/math_op_patch.py ++++ b/python/paddle/base/layers/math_op_patch.py +@@ -564,6 +564,40 @@ def monkey_patch_variable(): + """ + return len(self.shape) + ++ @property ++ def requires_grad(self) -> bool: ++ """ ++ Whether this Tensor requires gradient computation. ++ ++ This is a convenience property that returns the opposite of stop_gradient. ++ Setting requires_grad=True is equivalent to setting stop_gradient=False. ++ ++ Examples: ++ .. code-block:: python ++ ++ >>> import paddle ++ >>> x = paddle.randn([2, 3]) ++ >>> print(x.requires_grad) # False by default ++ >>> ++ >>> x.requires_grad = False ++ >>> print(x.stop_gradient) # True ++ """ ++ return not self.stop_gradient ++ ++ @requires_grad.setter ++ def requires_grad(self, value: bool) -> None: ++ """ ++ Set whether this Tensor requires gradient computation. ++ ++ Args: ++ value (bool): True to enable gradient computation, False to disable. ++ """ ++ if not isinstance(value, bool): ++ raise TypeError( ++ f"requires_grad must be bool, but got {type(value)}" ++ ) ++ self.stop_gradient = not value ++ + def _scalar_add_(var, value): + return _scalar_op_(var, 1.0, value) + +@@ -814,6 +848,7 @@ def monkey_patch_variable(): + ('dim', dim), + ('ndimension', ndimension), + ('ndim', _ndim), ++ ("requires_grad", requires_grad), + ( + '__add__', + _binary_creator_('__add__', 'elementwise_add', False, _scalar_add_), +diff --git a/python/paddle/pir/math_op_patch.py b/python/paddle/pir/math_op_patch.py +index f51cfd916428a134004a3a6f0abc6cb65124afe3..b114c100ff284f4e6f9078aeda3b4a8abc3c9c15 100644 +--- a/python/paddle/pir/math_op_patch.py ++++ b/python/paddle/pir/math_op_patch.py +@@ -633,6 +633,40 @@ def monkey_patch_value(): + + return _C_ops.transpose(self, perm) + ++ @property ++ def requires_grad(self) -> bool: ++ """ ++ Whether this Tensor requires gradient computation. ++ ++ This is a convenience property that returns the opposite of stop_gradient. ++ Setting requires_grad=True is equivalent to setting stop_gradient=False. ++ ++ Examples: ++ .. code-block:: python ++ ++ >>> import paddle ++ >>> x = paddle.randn([2, 3]) ++ >>> print(x.requires_grad) # False by default ++ >>> ++ >>> x.requires_grad = False ++ >>> print(x.stop_gradient) # True ++ """ ++ return not self.stop_gradient ++ ++ @requires_grad.setter ++ def requires_grad(self, value: bool) -> None: ++ """ ++ Set whether this Tensor requires gradient computation. ++ ++ Args: ++ value (bool): True to enable gradient computation, False to disable. ++ """ ++ if not isinstance(value, bool): ++ raise TypeError( ++ f"requires_grad must be bool, but got {type(value)}" ++ ) ++ self.stop_gradient = not value ++ + def _int_(self): + error_msg = """\ + int(Tensor) is not supported in static graph mode. Because it's value is not available during the static mode. +@@ -1182,6 +1216,7 @@ def monkey_patch_value(): + ('size', _size_), + ('T', _T_), + ('mT', _mT_), ++ ("requires_grad", requires_grad), + ('clone', clone), + ('clear_gradient', clear_gradient), + ('append', append), diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/tests/test.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/tests/test.patch new file mode 100644 index 000000000..a9bf2a0c3 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/tests/test.patch @@ -0,0 +1,133 @@ +diff --git a/test/swe_paddle/test_pr74491_requires_grad.py b/test/swe_paddle/test_pr74491_requires_grad.py +new file mode 100644 +index 0000000..25b888c +--- /dev/null ++++ b/test/swe_paddle/test_pr74491_requires_grad.py +@@ -0,0 +1,127 @@ ++import ast ++import copy ++from pathlib import Path ++ ++import pytest ++ ++ ++MODE_FILES = { ++ "dynamic": Path("python/paddle/base/dygraph/math_op_patch.py"), ++ "static": Path("python/paddle/base/layers/math_op_patch.py"), ++ "pir": Path("python/paddle/pir/math_op_patch.py"), ++} ++ ++ ++def _walk_functions(tree, name): ++ return [ ++ node ++ for node in ast.walk(tree) ++ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) ++ and node.name == name ++ ] ++ ++ ++def _compile_function(path, name): ++ tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) ++ matches = _walk_functions(tree, name) ++ assert matches, f"{path} must provide {name}" ++ node = copy.deepcopy(matches[0]) ++ node.decorator_list = [] ++ node.name = f"_swe_{name}" ++ module = ast.Module(body=[node], type_ignores=[]) ++ ast.fix_missing_locations(module) ++ namespace = {"Tensor": object} ++ exec(compile(module, str(path), "exec"), namespace) ++ return namespace[node.name] ++ ++ ++def _requires_grad_property(path): ++ tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) ++ candidates = _walk_functions(tree, "requires_grad") ++ ++ getter = None ++ setter = None ++ for node in candidates: ++ for decorator in node.decorator_list: ++ if isinstance(decorator, ast.Name) and decorator.id == "property": ++ getter = node ++ if ( ++ isinstance(decorator, ast.Attribute) ++ and decorator.attr == "setter" ++ and isinstance(decorator.value, ast.Name) ++ and decorator.value.id == "requires_grad" ++ ): ++ setter = node ++ ++ assert getter is not None and setter is not None, ( ++ f"{path} must expose a readable and writable requires_grad property" ++ ) ++ ++ getter = copy.deepcopy(getter) ++ setter = copy.deepcopy(setter) ++ getter.name = "_swe_requires_grad_getter" ++ setter.name = "_swe_requires_grad_setter" ++ getter.decorator_list = [] ++ setter.decorator_list = [] ++ ++ module = ast.Module(body=[getter, setter], type_ignores=[]) ++ ast.fix_missing_locations(module) ++ namespace = {"Tensor": object} ++ exec(compile(module, str(path), "exec"), namespace) ++ return property( ++ namespace[getter.name], ++ namespace[setter.name], ++ ) ++ ++ ++def test_existing_dim_metadata_behavior_remains_compatible(): ++ class FakeTensor: ++ shape = [2, 3, 4, 5] ++ ++ for path in MODE_FILES.values(): ++ dim = _compile_function(path, "dim") ++ assert dim(FakeTensor()) == 4 ++ ++ ++def test_requires_grad_tracks_stop_gradient_across_tensor_modes(): ++ for mode, path in MODE_FILES.items(): ++ descriptor = _requires_grad_property(path) ++ ++ class FakeTensor: ++ requires_grad = descriptor ++ ++ def __init__(self, stop_gradient): ++ self.stop_gradient = stop_gradient ++ ++ tensor = FakeTensor(True) ++ assert tensor.requires_grad is False, mode ++ ++ tensor.requires_grad = True ++ assert tensor.requires_grad is True, mode ++ assert tensor.stop_gradient is False, mode ++ ++ tensor.requires_grad = False ++ assert tensor.requires_grad is False, mode ++ assert tensor.stop_gradient is True, mode ++ ++ tensor.stop_gradient = False ++ assert tensor.requires_grad is True, mode ++ ++ ++def test_requires_grad_rejects_non_boolean_assignments(): ++ invalid_values = ["true", 1, 0, None, [], {}] ++ ++ for mode, path in MODE_FILES.items(): ++ descriptor = _requires_grad_property(path) ++ ++ class FakeTensor: ++ requires_grad = descriptor ++ ++ def __init__(self): ++ self.stop_gradient = True ++ ++ tensor = FakeTensor() ++ for value in invalid_values: ++ with pytest.raises(TypeError, match="requires_grad must be bool"): ++ tensor.requires_grad = value ++ assert tensor.stop_gradient is True, mode diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-74491/tests/test.sh b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/tests/test.sh new file mode 100644 index 000000000..ed5a9b576 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-74491/tests/test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +python -m pytest test/swe_paddle/test_pr74491_requires_grad.py -q