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-73855/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# PaddlePaddle__Paddle-73855

This directory converts Paddle PR #73855 into a SWE-Paddle community task candidate.

## Source

| Field | Value |
| --- | --- |
| Repo | `PaddlePaddle/Paddle` |
| PR | [73855](https://github.com/PaddlePaddle/Paddle/pull/73855) |
| PR title | `[0-size Tensor Job2 No.56] Add 0-size Tensor support for paddle.nn.functional.dice_loss` |
| Base commit | `0a23433eddfd286cbdb8746240eaf662cd027c69` |
| Gold commit | `1d3518f4ab0bb6f188e222152529f0b31f6acee3` |
| Merged at | `2025-07-08` |
| Task type | `bug_fix` |
| Resource | CPU |
| Scope | Python Tensor API |

## Summary

Fix `paddle.nn.functional.dice_loss` to correctly handle 0-size tensors by removing the assertion that rejects inputs with any dimension equal to 0.

## 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 Python Tensor API and does not require rebuilding C++ kernels.
- The failure is deterministic: the base revision fails when processing 0-size tensors due to an explicit assertion check.
- 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.
- `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) | dice_loss F2P |
| --- | ---: | ---: |
| Base + `tests/test.patch` | PASS | FAIL |
| Base + `tests/test.patch` + `solution/code.patch` | PASS | PASS |
37 changes: 37 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73855/environment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Environment Notes

## Expected Environment

- Repository: `PaddlePaddle/Paddle`
- Base commit: `0a23433eddfd286cbdb8746240eaf662cd027c69`
- Gold commit: `1d3518f4ab0bb6f188e222152529f0b31f6acee3`
- Resource: CPU
- GPU required: no
- Patch type: Python-only
- Python dependencies: PaddlePaddle, NumPy, pytest

The verifier should execute against the Paddle source revision represented by the selected patch state. A source build is not required when an equivalent Python overlay is available and the underlying runtime remains API-compatible.

## Run Order

1. Check out `PaddlePaddle/Paddle` at the base commit.
2. Apply `tests/test.patch`.
3. Run the P2P tests; existing non-zero-size behavior should pass.
4. Run the 0-size tensor tests; the target case should fail before the fix.
5. Apply `solution/code.patch`.
6. Run `bash tests/test.sh`; all target tests should pass.

## Minimal Test Command

```bash
bash tests/test.sh
```

## Expected Matrix

| Revision state | P2P | dice_loss F2P |
| --- | ---: | ---: |
| Base + test patch | PASS | FAIL |
| Base + test patch + solution patch | PASS | PASS |

No GPU, distributed runtime, external service, or additional dataset is required.
60 changes: 60 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73855/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 修复 `paddle.nn.functional.dice_loss` 对 0-size Tensor 的处理

## 详细描述

当 `paddle.nn.functional.dice_loss(input, label, epsilon)` 的输入 `input` 或 `label` 中存在大小为 `0` 的 dimension 时,当前实现在 Python 层会直接触发断言错误,拒绝处理任何包含 0 维度的输入。

典型表现包括:

- 抛出 AssertionError: "Any dimension of input and label cannot be equal to 0."
- 调用失败并无法继续执行

例如:

```python
import numpy as np
import paddle

paddle.disable_static()
input = paddle.randn([0, 2]).astype(paddle.float64)
input.stop_gradient = False
label = paddle.randn([0, 1]).astype(paddle.int64)
label.stop_gradient = False
out = paddle.nn.functional.dice_loss(input, label, 1e-5)
```

上述调用中 `input` 的 shape 为 `[0, 2]`,`label` 的 shape 为 `[0, 1]`,不包含任何元素。按照 API semantics,当输入 tensor 的某个维度为 0 时,dice_loss 应正常完成计算并返回结果(值为 NaN),而不是在 Python 层直接抛出断言错误。

当前 Python 层在进入计算逻辑之前,使用 `functools.reduce(operator.mul, shape)` 检查输入 shape 中是否存在 0 维度,如果存在则直接抛出异常。需要移除该检查,使 0-size tensor 能够正常进入后续计算流程。

## 验收说明

- 当输入 tensor 的任意维度为 0 时,`paddle.nn.functional.dice_loss` 应正常完成,不再抛出断言错误
- 返回的结果应为 NaN(因为 0-size tensor 的计算结果在数学上未定义)
- 反向传播应正常工作,梯度 shape 应与输入 shape 一致
- 非 0-size tensor 输入下的 dice_loss 行为不得退化

## 测试用例说明

### P2P 测试(Pass-to-Pass):`TestDiceLossOpApi`

验证非 0-size tensor 的 dice_loss 行为保持不变:
- 输入 shape 为 `[3, 224, 224, 2]` 的 tensor,经过 softmax 归一化
- 标签 shape 为 `[3, 224, 224, 1]`,值为 0 或 1 的整数
- 验证输出 shape 为标量(`[]`)
- 验证反向传播正常,梯度 shape 与输入一致

### F2P 测试(Fail-to-Pass):`TestDiceLossOpApi_ZeroSize`

验证 0-size tensor 的 dice_loss 处理:
- 输入 shape 为 `[0, 2]` 的 0-size tensor
- 标签 shape 为 `[0, 1]` 的 0-size tensor
- 验证不再抛出断言错误,正常返回结果
- 验证输出值为 NaN
- 验证反向传播正常,梯度 shape 与输入 shape 一致

## 技术要求

- 熟悉 Python 和 Paddle Tensor API
- 了解 Tensor shape、0-size Tensor 和动态图执行路径
- 了解 dice_loss 算子的计算语义
51 changes: 51 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73855/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Task Proposal: PaddlePaddle__Paddle-73855

## 1. 来源信息
- Instance ID:`PaddlePaddle__Paddle-73855`
- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/73855
- PR 标题:`[0-size Tensor Job2 No.56] Add 0-size Tensor support for paddle.nn.functional.dice_loss`
- `base_commit`:`0a23433eddfd286cbdb8746240eaf662cd027c69`
- merged 时间:`2025-07-08`
- 你的身份:contributor

## 2. 问题一句话
`paddle.nn.functional.dice_loss` 在 Python 层对 0-size tensor(任意维度含有 0)的输入显式抛出断言错误,需要移除该检查以补齐 0-size tensor 支持。

## 3. 为什么适合作为 SWE-Paddle 样本
- **真实性**:该问题来自 Paddle 的「0-size Tensor 机制建设」系列任务,是真实研发需求,目标是为 `dice_loss` 算子补齐 0-size tensor 支持。
- **代表性**:覆盖 Python API 层面的损失函数边界处理,涉及 dynamic mode 下的 tensor 维度断言检查,是 Paddle API 算子机制增强的典型样本。
- **边界清楚**:目标仅限移除 Python 层对 0-size tensor 输入的显式断言检查,使 0-size tensor 能够正常进入后续计算流程。
- **非平凡性**:修复需要移除 `loss.py` 中的 `functools.reduce(operator.mul, shape) != 0` 断言,并清理不再使用的 `functools` 和 `operator` 导入,不是简单机械修改。
- **回归护栏明确**:目标 F2P 可覆盖 0-size tensor 输入的 `dice_loss` 动态图调用;同文件中新增的标准 dice_loss 测试用例可作为 P2P 护栏。

## 4. 任务类型和标签
- 任务类型:`bug_fix`
- 执行后端:`cpu`
- 设备范围:`cpu_only`
- 模块标签:`[python_api, loss, 0-size_tensor, dice_loss, dynamic_mode]`

## 5. 验证思路
- 目标测试命令:`bash tests/test.sh`
- 目标测试文件:
- `test/legacy_test/test_nn_dice_loss.py`(`TestDiceLossOpApi_ZeroSize`)
- P2P 候选:同文件中新增的 `TestDiceLossOpApi` 标准 dice_loss 测试用例。
- 修复前预期:`base_commit` + `tests/test.patch` 后,0-size tensor 输入在 `paddle.nn.functional.dice_loss` 的动态图调用中失败(Python 层断言错误)。
- 修复后预期:继续应用 `solution/code.patch` 后,0-size tensor 输入正常完成计算并返回 NaN,P2P 存量测试仍然通过。

## 6. 环境与资源
- 是否能提供 Docker:无
- Dockerfile 或镜像地址:暂无
- Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit`,纯 Python 修改可直接 patch。
- 如果使用 wheel,请填写 wheel URL、Python 版本和平台标签:可由 verifier 选择与 base 兼容的 CPU wheel 或本地源码环境;proposal 阶段不固定 wheel URL。
- OS / Python / CUDA / cuDNN / 其他关键依赖:Linux CPU + Python + numpy + pytest;不要求 CUDA/cuDNN。
- 硬件:CPU 即可。
- patch 类型:纯 Python 修改 + Python legacy test,无需 C++ rebuild。
- 最小测试命令:`bash tests/test.sh`
- 是否有 oracle 日志:无;由 SWE-Paddle verifier 记录 Run/Test/Fix 结果。

## 7. 风险自查
- 泄露风险:正式 `instruction.md` 只描述「dice_loss 对 0-size tensor 输入的断言错误」,不指出具体移除哪行代码或哪些导入。
- 环境风险:低。任务为 Python-only,无需特殊镜像、外部服务或不可固定下载。
- flaky 风险:低。测试使用固定的 0-size tensor 构造,不依赖随机数差异或多设备同步。
- 拆分风险:低。该 PR 目标集中在 `loss.py` 中 `dice_loss` 函数的 0-size 断言检查,测试也明确指向 dice_loss 的零尺寸分支,适合作为一个独立样本。
- 其他不确定点:完整任务包阶段应确认新增 F2P(`TestDiceLossOpApi_ZeroSize`)在 `base_commit` 上确实失败,并选择同文件中新增的 `TestDiceLossOpApi` 标准测试用例作为在 base 与修复后都稳定通过的 P2P nodeid。
25 changes: 25 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73855/solution/code.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
diff --git a/python/paddle/nn/functional/loss.py b/python/paddle/nn/functional/loss.py
index f294cc376c..1aab1bd950 100644
--- a/python/paddle/nn/functional/loss.py
+++ b/python/paddle/nn/functional/loss.py
@@ -14,9 +14,7 @@

from __future__ import annotations

-import functools
import math
-import operator
from typing import TYPE_CHECKING, Literal, overload

import paddle
@@ -110,10 +108,6 @@ def dice_loss(
assert (
input.shape[:-1] == label.shape[:-1]
), "All dimensions should be equal except the last one."
- assert (
- functools.reduce(operator.mul, input.shape) != 0
- and functools.reduce(operator.mul, label.shape) != 0
- ), "Any dimension of input and label cannot be equal to 0."

label = paddle.squeeze(label, [-1])
label = paddle.nn.functional.one_hot(label, input.shape[-1])
48 changes: 48 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73855/tests/test.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
diff --git a/test/legacy_test/test_nn_dice_loss.py b/test/legacy_test/test_nn_dice_loss.py
index 8734bc05e7..b1c2d3e4f5 100644
--- a/test/legacy_test/test_nn_dice_loss.py
+++ b/test/legacy_test/test_nn_dice_loss.py
@@ -14,9 +14,43 @@

import unittest

+import numpy as np
+from op_test import get_places
+
+import paddle
+
num_classes = 4
eps = 1e-6


+class TestDiceLossOpApi(unittest.TestCase):
+ def test_api_with_dygraph(self):
+ for place in get_places():
+ paddle.disable_static(place)
+ input = paddle.randn([3, 224, 224, 2]).astype(paddle.float64)
+ input = paddle.nn.functional.softmax(input)
+ input.stop_gradient = False
+ label = paddle.randint(0, 2, [3, 224, 224, 1]).astype(paddle.int64)
+ label.stop_gradient = False
+ out = paddle.nn.functional.dice_loss(input, label, 1e-5)
+ self.assertEqual(out.shape, [])
+ out.sum().backward()
+ self.assertEqual(input.grad.shape, input.shape)
+
+
+class TestDiceLossOpApi_ZeroSize(unittest.TestCase):
+ def test_api_with_dygraph(self):
+ for place in get_places():
+ paddle.disable_static(place)
+ input = paddle.randn([0, 2]).astype(paddle.float64)
+ input.stop_gradient = False
+ label = paddle.randn([0, 1]).astype(paddle.int64)
+ label.stop_gradient = False
+ out = paddle.nn.functional.dice_loss(input, label, 1e-5)
+ np.testing.assert_allclose(out.numpy(), paddle.nan)
+ out.sum().backward()
+ np.testing.assert_allclose(input.grad.shape, input.shape)
+
+
if __name__ == "__main__":
unittest.main()
8 changes: 8 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73855/tests/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail

# P2P tests (pass-to-pass)
python -m pytest test/legacy_test/test_nn_dice_loss.py::TestDiceLossOpApi -q

# F2P tests (fail-to-pass)
python -m pytest test/legacy_test/test_nn_dice_loss.py::TestDiceLossOpApi_ZeroSize -q