diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/README.md new file mode 100644 index 000000000..31d773d29 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/README.md @@ -0,0 +1,51 @@ +# PaddlePaddle__Paddle-73387 + +This directory converts Paddle PR #73387 into a SWE-Paddle community task candidate. + +## Source + +| Field | Value | +| --- | --- | +| Repo | `PaddlePaddle/Paddle` | +| PR | [73387](https://github.com/PaddlePaddle/Paddle/pull/73387) | +| PR title | `[0-size Tensor Job2 No.58] Add 0-size Tensor support for gather_tree` | +| Base commit | `57d91535621b3b793c2bd1e6d5dcc2801ed893fd` | +| Gold commit | `71179f5ae909c4577479beb77321f87b9b0b00ae` | +| Merged at | `2025-06-19` | +| Task type | `bug_fix` | +| Resource | CPU | +| Scope | C++ Operator Kernel | + +## Summary + +Fix `gather_tree` operator to correctly handle 0-size tensors by adding early-return logic in CPU/GPU kernels and skipping shape equality checks in InferMeta when input is 0-size. + +## Why This Is A Good SWE-Paddle Candidate + +- It is derived from a merged Paddle bug-fix PR rather than a synthetic issue. +- The target behavior is isolated to the C++ operator kernel level and requires rebuilding Paddle from source. +- The failure is deterministic: the base revision fails when processing 0-size tensors due to shape mismatch checks and kernel execution on empty data. +- The task has clear regression coverage for existing non-zero-size behavior. +- The task runs on CPU and does not require distributed execution, external services, or additional datasets. + +## Files + +- `proposal.md`: candidate proposal for maintainer triage. +- `instruction.md`: self-contained problem statement for the coding agent. +- `solution/code.patch`: gold implementation patch (C++ kernel and InferMeta changes). +- `tests/test.patch`: tests exposing the target behavior. +- `tests/test.sh`: minimal target test command. +- `environment/README.md`: environment and reproduction notes. + +## Verification + +```bash +bash tests/test.sh +``` + +Expected behavior: + +| Revision state | Existing behavior (P2P) | gather_tree F2P | +| --- | ---: | ---: | +| Base + `tests/test.patch` | PASS | FAIL | +| Base + `tests/test.patch` + `solution/code.patch` | PASS | PASS | diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/environment/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/environment/README.md new file mode 100644 index 000000000..7bc588c55 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/environment/README.md @@ -0,0 +1,52 @@ +# Environment Notes + +## Expected Environment + +- Repository: `PaddlePaddle/Paddle` +- Base commit: `57d91535621b3b793c2bd1e6d5dcc2801ed893fd` +- Gold commit: `71179f5ae909c4577479beb77321f87b9b0b00ae` +- Resource: CPU +- GPU required: no +- Patch type: C++ kernel (CPU/GPU backends) + InferMeta + Symbolic Shape +- Python dependencies: PaddlePaddle (source build), NumPy + +The verifier should execute against the Paddle source revision represented by the selected patch state. A source build is required since the patch modifies C++ kernel code, InferMeta, and symbolic shape inference. + +## Build Instructions + +1. Check out `PaddlePaddle/Paddle` at the base commit. +2. Apply `tests/test.patch`. +3. Build Paddle from source (CPU-only build is sufficient): + ```bash + mkdir build && cd build + cmake .. -DWITH_GPU=OFF -DWITH_TESTING=ON -DCMAKE_BUILD_TYPE=Release + make -j$(nproc) + ``` +4. Install the built Paddle package. + +## Run Order + +1. Check out `PaddlePaddle/Paddle` at the base commit. +2. Build and install Paddle from source. +3. Apply `tests/test.patch`. +4. Run the P2P tests; existing non-zero-size behavior should pass. +5. Run the 0-size tensor tests; the target case should fail before the fix. +6. Apply `solution/code.patch`. +7. Rebuild Paddle from source. +8. Reinstall Paddle package. +9. Run `bash tests/test.sh`; all target tests should pass. + +## Minimal Test Command + +```bash +bash tests/test.sh +``` + +## Expected Matrix + +| Revision state | P2P | gather_tree F2P | +| --- | ---: | ---: | +| Base + test patch | PASS | FAIL | +| Base + test patch + solution patch | PASS | PASS | + +No GPU, distributed runtime, external service, or additional dataset is required. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/instruction.md b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/instruction.md new file mode 100644 index 000000000..9b0101029 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/instruction.md @@ -0,0 +1,57 @@ +# 修复 `gather_tree` 对 0-size Tensor 的处理 + +## 详细描述 + +当 `gather_tree(ids, parents)` 的输入 `ids` 和 `parents` 为 0-size Tensor(即 `out.numel() == 0`)时,当前实现存在以下问题: + +1. **InferMeta 层**:`GatherTreeMeta` 函数会强制检查 `ids_dims == parents_dims`,但当输入为 0-size 时,这个检查可能不必要或导致错误。 +2. **符号推导层**:`GatherTreeOpInferSymbolicShape` 在遍历维度时,会对 0-size 维度添加等式约束,这在 0-size 情况下是不合适的。 +3. **Kernel 层**:CPU 和 GPU kernel 在分配输出内存后,直接进入计算逻辑,对 0-size 输入会访问无效内存或执行无意义的计算。 + +典型表现包括: + +- InferMeta 阶段抛出 shape 不匹配的异常 +- Kernel 执行时出现段错误或未定义行为 +- 0-size Tensor 输入无法通过 `gather_tree` 算子 + +例如: + +```python +import numpy as np +import paddle + +paddle.disable_static() + +# 0-size tensor 输入 +ids = np.random.randint(0, high=10, size=(0, 2, 2)).astype('int64') +parents = np.random.randint(0, high=2, size=(0, 2, 2)).astype('int64') + +ids_tensor = paddle.to_tensor(ids) +parents_tensor = paddle.to_tensor(parents) + +out = paddle.nn.functional.gather_tree(ids_tensor, parents_tensor) +# 期望返回 shape 为 (0, 2, 2) 的空 Tensor +``` + +上述调用中 `ids` 和 `parents` 的 shape 为 `[0, 2, 2]`,`out.numel() == 0`。按照 API semantics,当输入为 0-size 时,该调用应正常完成并返回正确 shape 的空 Tensor。 + +需要在以下位置进行修改: +- InferMeta 层:当 `ids` 的 numel 为 0 时,跳过 shape 相等性检查 +- 符号推导层:在遍历维度时,跳过 0-size 维度的等式约束添加 +- Kernel 层:在分配输出内存后,检查 `out->numel() == 0` 并直接返回 + +## 验收说明 + +- 当输入为 0-size Tensor 时,`gather_tree` 应正常完成,返回正确 shape 的空 Tensor +- 输出的 shape 应与输入 `ids` 一致 +- 非 0-size Tensor 输入下的 `gather_tree` 行为不得退化 +- 梯度计算也应正常工作(0-size Tensor 的梯度也为空 Tensor) + +## 技术要求 + +- 熟悉 C++ 和 Paddle PHI kernel 开发 +- 了解 Tensor shape、0-size Tensor 和 kernel 执行路径 +- 了解 `gather_tree` 算子的输入输出语义 +- 了解 Paddle CPU/GPU kernel 的多 backend 实现模式 +- 了解 InferMeta 和符号推导机制 +- 需要从源码编译 Paddle 以验证修改 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/proposal.md b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/proposal.md new file mode 100644 index 000000000..3d0e558ef --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/proposal.md @@ -0,0 +1,62 @@ +# SWE-Paddle Task Proposal: PaddlePaddle__Paddle-73387 + +## 1. 来源信息 + +- Instance ID: `PaddlePaddle__Paddle-73387` +- PR 链接: https://github.com/PaddlePaddle/Paddle/pull/73387 +- PR 标题: `[0-size Tensor Job2 No.58] Add 0-size Tensor support for gather_tree` +- Base commit: `57d91535621b3b793c2bd1e6d5dcc2801ed893fd` +- Gold commit: `71179f5ae909c4577479beb77321f87b9b0b00ae` +- Merged at: 2025-06-19 +- 你的身份: contributor + +## 2. 问题一句话 + +`gather_tree` 在输入为 0-size Tensor 时,InferMeta 的 shape 检查、符号推导的维度约束和 kernel 的计算逻辑均未处理 0-size 边界情况,需要在三个层面分别添加 0-size 早期返回或跳过逻辑。 + +## 3. 为什么适合作为 SWE-Paddle 样本 + +- **真实性**: 来自 Paddle「0-size Tensor 机制建设」系列任务,是真实研发需求。 +- **代表性**: 覆盖 C++ kernel 层面的 0-size Tensor 边界处理,涉及 CPU/GPU 双端 kernel、InferMeta 和符号推导三个层面的修改。 +- **边界清楚**: 目标仅限输入为 0-size 时的三个层面的早期返回/跳过逻辑;正向非零尺寸输入不应受影响。 +- **非平凡性**: 修复需要在三个层面分别处理: + - InferMeta: 当 `common::product(ids_dims) != 0` 时才进行 shape 检查 + - 符号推导: 当 `ids_shape[i] == 0` 时跳过等式约束添加 + - Kernel: 当 `out->numel() == 0` 时直接返回 + 涉及对 kernel 执行流程、InferMeta 和符号推导机制的理解。 +- **回归护栏明确**: 目标 F2P 可覆盖 0-size Tensor 输入的 `gather_tree` 算子测试;同文件中已有的 `TestGatherTreeOp` 等标准测试用例可作为 P2P 护栏。 + +## 4. 任务类型和标签 + +- 任务类型: `bug_fix` +- 执行后端: `cpu` +- 设备范围: `cpu_only` +- 模块标签: `[operator_kernel, gather_tree, 0-size_tensor, cpu_kernel, gpu_kernel, infermeta, symbolic_shape]` + +## 5. 验证思路 + +- 目标测试命令: `bash tests/test.sh` +- 目标测试文件: + - `test/legacy_test/test_gather_tree_op.py`(`TestGatherTreeOp_ZeroSize`、`TestGatherTreeOp_ZeroSize2`) +- P2P 候选: 同文件中已有的 `TestGatherTreeOp`、`TestGatherTreeOp_batch_size_1` 等标准 gather_tree 算子测试用例。 +- 修复前预期: `base_commit` + `tests/test.patch` 后,0-size Tensor 输入的 `gather_tree` 算子测试失败(InferMeta shape 检查或 kernel 执行报错)。 +- 修复后预期: 继续应用 `solution/code.patch` 并重新编译后,0-size Tensor 输入正常返回空 Tensor,P2P 存量测试仍然通过。 + +## 6. 环境与资源 + +- 是否能提供 Docker: 无 +- Dockerfile 或镜像地址: 暂无 +- Paddle 来源: `PaddlePaddle/Paddle` source checkout at `base_commit`,需要源码编译。 +- OS / Python / CUDA / cuDNN / 其他关键依赖: Linux CPU + Python + numpy;编译需要 CMake、GCC;不要求 CUDA/cuDNN(CPU 编译即可验证)。 +- 硬件: CPU 即可(编译和测试均不需要 GPU)。 +- patch 类型: 含 C++ kernel 修改(CPU/GPU 双端)+ InferMeta + 符号推导,需要重新编译 Paddle。 +- 最小测试命令: `bash tests/test.sh` +- 是否有 oracle 日志: 无 + +## 7. 风险自查 + +- 泄露风险: 正式 `instruction.md` 只描述「gather_tree 对 0-size Tensor 输入的行为异常」,不指出具体 `out->numel() == 0` 分支逻辑或具体代码位置。 +- 环境风险: 中。任务涉及 C++ kernel 修改,需要源码编译 Paddle,编译时间较长。 +- flaky 风险: 低。测试使用固定的 0-size Tensor 构造,不依赖随机数差异或多设备同步。 +- 拆分风险: 低。该 PR 目标集中在 `gather_tree` 算子的 0-size 边界处理,涉及三个层面的修改,测试明确指向 `TestGatherTreeOp_ZeroSize` 和 `TestGatherTreeOp_ZeroSize2`,适合作为一个独立样本。 +- 其他不确定点: 完整任务包阶段应确认新增 F2P(`TestGatherTreeOp_ZeroSize` 和 `TestGatherTreeOp_ZeroSize2`)在 `base_commit` 编译后确实失败。 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/solution/code.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/solution/code.patch new file mode 100644 index 000000000..d54e132f6 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/solution/code.patch @@ -0,0 +1,65 @@ +diff --git a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/binary_infer_sym.cc b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/binary_infer_sym.cc +index 16b7a8186c4ab..5c08b36179ad7 100644 +--- a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/binary_infer_sym.cc ++++ b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/binary_infer_sym.cc +@@ -1058,6 +1058,9 @@ bool GatherTreeOpInferSymbolicShape( + "shape of Input(Ids).")); + size_t rank = ids_shape.size(); + for (size_t i = 0; i < rank; ++i) { ++ if (ids_shape[i] == 0) { ++ continue; ++ } + infer_context->AddEqualCstr(ids_shape[i], parents_shape[i]); + } + +diff --git a/paddle/phi/infermeta/binary.cc b/paddle/phi/infermeta/binary.cc +index k911dab1a6d0f..013a8e9488b6b 100644 +--- a/paddle/phi/infermeta/binary.cc ++++ b/paddle/phi/infermeta/binary.cc +@@ -2138,11 +2138,13 @@ void GatherTreeMeta(const MetaTensor& ids, + MetaTensor* out) { + auto ids_dims = ids.dims(); + auto parents_dims = parents.dims(); +- PADDLE_ENFORCE_EQ(ids_dims == parents_dims, +- true, +- common::errors::InvalidArgument( +- "The shape of Input(Parents) must be same with the " +- "shape of Input(Ids).")); ++ if (common::product(ids_dims) != 0) { ++ PADDLE_ENFORCE_EQ(ids_dims == parents_dims, ++ true, ++ common::errors::InvalidArgument( ++ "The shape of Input(Parents) must be same with the " ++ "shape of Input(Ids).")); ++ } + out->set_dims(ids_dims); + out->set_dtype(ids.dtype()); + } +diff --git a/paddle/phi/kernels/cpu/gather_tree_kernel.cc b/paddle/phi/kernels/cpu/gather_tree_kernel.cc +index a7e85a731b824..4e3bc2be0f2ff 100644 +--- a/paddle/phi/kernels/cpu/gather_tree_kernel.cc ++++ b/paddle/phi/kernels/cpu/gather_tree_kernel.cc +@@ -28,6 +28,9 @@ void GatherTreeKernel(const Context &dev_ctx, + const auto *parents_data = parents.data(); + + T *out_data = dev_ctx.template Alloc(out); ++ if (out && out->numel() == 0) { ++ return; ++ } + + auto &ids_dims = ids.dims(); + int64_t max_length = ids_dims[0]; +diff --git a/paddle/phi/kernels/gpu/gather_tree_kernel.cu b/paddle/phi/kernels/gpu/gather_tree_kernel.cu +index 76aa7f4b00f2a..4ea564105ff5f 100644 +--- a/paddle/phi/kernels/gpu/gather_tree_kernel.cu ++++ b/paddle/phi/kernels/gpu/gather_tree_kernel.cu +@@ -63,6 +63,9 @@ void GatherTreeKernel(const Context &dev_ctx, + const auto *ids_data = ids.data(); + const auto *parents_data = parents.data(); + T *out_data = dev_ctx.template Alloc(out); ++ if (out && out->numel() == 0) { ++ return; ++ } + + PADDLE_ENFORCE_NOT_NULL(ids_data, + common::errors::InvalidArgument( diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.patch new file mode 100644 index 000000000..9019b0296 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.patch @@ -0,0 +1,38 @@ +diff --git a/test/legacy_test/test_gather_tree_op.py b/test/legacy_test/test_gather_tree_op.py +index 5379ec37c774a..289a82c4c2fa6 100644 +--- a/test/legacy_test/test_gather_tree_op.py ++++ b/test/legacy_test/test_gather_tree_op.py +@@ -150,6 +150,33 @@ def test_parents_ndim(): + paddle.disable_static() + + ++class TestGatherTreeOp_ZeroSize(OpTest): ++ def init_shape(self): ++ self.ids_shape = (0, 2, 2) ++ self.parents_shape = (0, 2, 2) ++ ++ def setUp(self): ++ self.op_type = "gather_tree" ++ self.python_api = paddle.nn.functional.gather_tree ++ self.init_shape() ++ ids_shape = self.ids_shape ++ parents_shape = self.parents_shape ++ max_length, batch_size, beam_size = ids_shape ++ ids = np.random.randint(0, high=10, size=ids_shape) ++ parents = np.random.randint(0, high=beam_size, size=parents_shape) ++ self.inputs = {"Ids": ids, "Parents": parents} ++ self.outputs = {'Out': ids} ++ ++ def test_check_output(self): ++ self.check_output(check_pir=True) ++ ++ ++class TestGatherTreeOp_ZeroSize2(TestGatherTreeOp_ZeroSize): ++ def init_shape(self): ++ self.ids_shape = (0, 2, 2) ++ self.parents_shape = (1, 2, 2) ++ ++ + if __name__ == "__main__": + paddle.enable_static() + unittest.main() diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.sh b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.sh new file mode 100644 index 000000000..3ebba15e2 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +# P2P tests (pass-to-pass) +python -m pytest test/legacy_test/test_gather_tree_op.py::TestGatherTreeOp -q + +# F2P tests (fail-to-pass) +python -m pytest test/legacy_test/test_gather_tree_op.py::TestGatherTreeOp_ZeroSize -q +python -m pytest test/legacy_test/test_gather_tree_op.py::TestGatherTreeOp_ZeroSize2 -q