Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/README.md
Original file line number Diff line number Diff line change
@@ -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 |
52 changes: 52 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/environment/README.md
Original file line number Diff line number Diff line change
@@ -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.
57 changes: 57 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/instruction.md
Original file line number Diff line number Diff line change
@@ -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 以验证修改
62 changes: 62 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/proposal.md
Original file line number Diff line number Diff line change
@@ -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` 编译后确实失败。
65 changes: 65 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/solution/code.patch
Original file line number Diff line number Diff line change
@@ -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>();

T *out_data = dev_ctx.template Alloc<T>(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<T>();
const auto *parents_data = parents.data<T>();
T *out_data = dev_ctx.template Alloc<T>(out);
+ if (out && out->numel() == 0) {
+ return;
+ }

PADDLE_ENFORCE_NOT_NULL(ids_data,
common::errors::InvalidArgument(
38 changes: 38 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.patch
Original file line number Diff line number Diff line change
@@ -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()
9 changes: 9 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73387/tests/test.sh
Original file line number Diff line number Diff line change
@@ -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