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
44 changes: 44 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# PaddlePaddle__Paddle-74594

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

## Source

| Field | Value |
| --- | --- |
| Repo | `PaddlePaddle/Paddle` |
| PR | [74594](https://github.com/PaddlePaddle/Paddle/pull/74594) |
| PR title | `[API compatibility] add broadcast_shapes api` |
| Base commit | `20e50fb447d8e34bafb337c180ca28d77dfb82ca` |
| Merged at | `2025-08-15` |
| Task type | `new_feature` / `api_compatibility` |
| Resource | CPU |

## Summary

Add the public `paddle.broadcast_shapes` API so callers can compute the common broadcasted shape of zero, one, or multiple input shapes while preserving the existing two-shape `broadcast_shape` behavior.

## Why This Is A Good SWE-Paddle Candidate

- It comes from a merged Paddle API-compatibility PR and represents a real user-facing compatibility gap.
- The expected behavior is directly observable through returned shapes and exceptions rather than implementation-specific source structure.
- The production change is small and Python-only, with a clear separation between public exports and the multi-shape wrapper behavior.
- Stable P2P and F2P tests can run on CPU without a Paddle native source build or GPU.

## Files

- `proposal.md`: candidate proposal for maintainer triage.
- `instruction.md`: self-contained task statement.
- `solution/code.patch`: production-only Gold patch.
- `tests/test.patch`: independent regression tests.
- `tests/test.sh`: minimal target test command.
- `environment/README.md`: reproduction environment notes.
- `README.md`: task overview and verification entrypoint.

## Verification

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

Expected behavior: with only `tests/test.patch` applied to the Base commit, the existing `broadcast_shape` P2P test passes while the new multi-shape API tests fail. After applying `solution/code.patch`, all target tests pass.
27 changes: 27 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/environment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Environment Notes

This candidate is part of the SWE-Paddle community task set.

## Expected Environment

- Repository: `PaddlePaddle/Paddle`
- Base commit: `20e50fb447d8e34bafb337c180ca28d77dfb82ca`
- Resource: CPU
- GPU required: no
- Build path: Paddle source checkout at the base commit. This Python-only task may be verified with an AST overlay and a controlled broadcasting double; a Paddle native source build is not 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 multi-shape API behavior should fail before the change.
4. Apply `solution/code.patch`.
5. Run `bash tests/test.sh` again; all target tests should pass.

## Minimal Test Command

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

The verifier is responsible for deriving stable F2P and P2P node IDs from repeated runs.
27 changes: 27 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 新增 `paddle.broadcast_shapes` API

## 详细描述

`paddle.broadcast_shape` 只能计算两个 shape 的广播结果。如果要同时处理三个或更多 shape 时,调用方只能重复调用该接口。

需要新增 `paddle.broadcast_shapes(*shapes)`,一次计算所有输入 shape 广播后的结果。每个 shape 可以使用 list 或 tuple 表示,返回值为 list。

未传入 shape 时返回 `[]`;只传入一个 shape 时,返回该 shape 对应的 list。传入多个 shape 时,按照现有广播规则计算结果,无法广播时抛出 `ValueError`。

## 验收说明

- 可以通过 `paddle.broadcast_shapes` 和 `paddle.tensor.broadcast_shapes` 调用该接口
- 支持传入任意数量的 list 或 tuple 形式的 shape
- 多个可广播的 shape 返回正确结果
- 未传入 shape 时返回 `[]`
- 只传入一个 shape 时返回对应的 list
- 空 shape 以及包含 `0` 或 `-1` 的 shape 按照现有广播规则处理
- 输入无法广播时抛出 `ValueError`
- 返回值为 list
- 现有 `paddle.broadcast_shape` 的行为保持不变

## 技术要求

- 熟悉 Python
- 了解 Tensor 的广播规则
- 了解 Paddle Python API 的定义和导出方式
55 changes: 55 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Task Proposal: PaddlePaddle__Paddle-74594

## 1. 来源信息

* Instance ID:`PaddlePaddle__Paddle-74594`
* PR 链接:https://github.com/PaddlePaddle/Paddle/pull/74594
* PR 标题:`[API compatibility] add broadcast_shapes api`
* `base_commit`:`20e50fb447d8e34bafb337c180ca28d77dfb82ca`
* merged 时间:`2025-08-15`
* 你的身份:熟悉该模块的 contributor
* 后续联系人:TBD

## 2. 问题一句话

新增 `paddle.broadcast_shapes` 公共 API,使调用方能够一次计算任意数量 shape 的共同广播结果,并在输入无法广播时得到明确的异常。

## 3. 为什么适合作为 SWE-Paddle 样本

* **真实性**:该任务来自已合入的 Paddle PR #74594,不是人工构造的需求。
* **代表性**:该任务涉及 API compatibility、公共 API 导出以及多个 shape 的广播规则。
* **边界清楚**:production change 集中在 `python/paddle/__init__.py`、`python/paddle/tensor/__init__.py` 和 `python/paddle/tensor/math.py`,测试代码可以与 Gold production patch 清晰分离。
* **非平凡性**:任务需要正确处理多个 shape、无输入、单输入和空 shape,并在输入不兼容时保持明确的异常行为,同时保证现有二元 `broadcast_shape` 不受影响。

## 4. 任务类型和标签

* 任务类型:`feature_enhancement`
* 执行后端:`cpu`
* 设备范围:`cpu_only`
* 模块标签:`[api_compatibility, tensor, math, broadcasting, shape]`

## 5. 验证思路

* 目标测试命令:`bash tests/test.sh`
* 目标测试文件:`test/swe_paddle/test_pr74594_broadcast_shapes.py`
* 修复前预期:已有 `broadcast_shape` delegation P2P 应 pass;`broadcast_shapes` 的多输入、零输入和单输入测试应因 API 尚不存在而 fail。
* 修复后预期:应用 production Gold patch 后,P2P 与全部 F2P 均应 pass;多个可广播 shape 应得到正确结果,不兼容输入应继续抛出 `ValueError`。
* P2P 候选:已有 `broadcast_shape` 继续按原有方式接收两个 shape,并返回底层广播结果。
* F2P 候选:三个及以上 shape 的共同广播;零输入、单输入和 empty shape;不兼容输入的异常行为。

## 6. 环境与资源

* 资源需求:CPU
* Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit`
* 是否能提供 Docker:暂无
* patch 类型:Python-only
* 环境建议:有Paddle 环境,使用能够运行 `pytest` 的 Python 环境
* 最小测试命令:`bash tests/test.sh`
* 是否有 oracle 日志:由 SWE-Paddle verifier 结果另行维护

## 7. 风险自查

* 泄露风险:`instruction.md` 只描述公共 API 的输入、输出、异常和兼容性要求,不透露 Gold patch 的循环结构、内部函数调用方式或具体实现步骤。
* 环境风险:测试不依赖历史 Paddle wheel、GPU、CUDA、native extension 或网络环境。
* flaky 风险:测试使用固定 shape 和确定性的异常断言,不涉及随机数、并发或异步执行。
* 拆分风险:公共命名空间导出和多 shape 广播行为共同构成 `broadcast_shapes` 的完整能力,适合作为单个任务。
98 changes: 98 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/solution/code.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py
index b1bdc05813d8c0dc810eeb5deb49696457f06e2c..6c220f760cf5d03911348cbe94b8f7185058901a 100644
--- a/python/paddle/__init__.py
+++ b/python/paddle/__init__.py
@@ -406,6 +406,7 @@ from .tensor.math import ( # noqa: F401
bitwise_right_shift,
bitwise_right_shift_,
broadcast_shape,
+ broadcast_shapes,
cartesian_prod,
ceil,
clip,
@@ -1024,6 +1025,7 @@ __all__ = [
'DataParallel',
'argmin',
'prod',
+ 'broadcast_shapes',
'broadcast_shape',
'conj',
'neg',
diff --git a/python/paddle/tensor/__init__.py b/python/paddle/tensor/__init__.py
index 824d8d681f4e594193da345e9c0bb34d8e3dd09e..6c6a77df9be0465a5c8c4dbca38ea223c1e8f0d6 100644
--- a/python/paddle/tensor/__init__.py
+++ b/python/paddle/tensor/__init__.py
@@ -269,6 +269,7 @@ from .math import ( # noqa: F401
bitwise_right_shift,
bitwise_right_shift_,
broadcast_shape,
+ broadcast_shapes,
cartesian_prod,
ceil,
ceil_,
@@ -638,6 +639,7 @@ tensor_method_func = [
'isneginf',
'isposinf',
'isreal',
+ 'broadcast_shapes',
'broadcast_shape',
'conj',
'neg',
diff --git a/python/paddle/tensor/math.py b/python/paddle/tensor/math.py
index 72a73ab931ab0024e5e672c7713039f25d7ebc60..ad845d5c1fb4220b2fe968086940d9be593e53ca 100644
--- a/python/paddle/tensor/math.py
+++ b/python/paddle/tensor/math.py
@@ -5448,6 +5448,53 @@ def any(
return out


+def broadcast_shapes(*shapes: Sequence[int]) -> list[int]:
+ """
+ The function returns the shape of doing operation with broadcasting on tensors of shape list.
+
+ Note:
+ If you want know more about broadcasting, please refer to `Introduction to Tensor`_ .
+
+ .. _Introduction to Tensor: ../../guides/beginner/tensor_en.html#chapter5-broadcasting-of-tensor
+
+ Args:
+ *shapes (list[int]|tuple[int]): A shape list of multiple tensors.
+
+
+ Returns:
+ list[int], the result shape.
+
+ Examples:
+ .. code-block:: python
+
+ >>> import paddle
+
+ >>> shape = paddle.broadcast_shapes([2, 1, 3], [1, 3, 1])
+ >>> shape
+ [2, 3, 3]
+
+ >>> # shape = paddle.broadcast_shapes([2, 1, 3], [3, 3, 1])
+ >>> # ValueError (terminated with error message).
+
+ >>> shape = paddle.broadcast_shapes([5, 1, 3], [1, 4, 1], [1, 1, 3])
+ >>> shape
+ [5, 4, 3]
+
+ >>> # shape = paddle.broadcast_shapes([5, 1, 3], [1, 4, 1], [1, 2, 3])
+ >>> # ValueError (terminated with error message).
+
+ """
+ if len(shapes) == 0:
+ return []
+ elif len(shapes) == 1:
+ return list(shapes[0])
+ else:
+ current_shape = list(shapes[0])
+ for next_shape in shapes[1:]:
+ current_shape = broadcast_shape(current_shape, next_shape)
+ return current_shape
+
+
def broadcast_shape(
x_shape: Sequence[int], y_shape: Sequence[int]
) -> list[int]:
107 changes: 107 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/tests/test.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
diff --git a/test/swe_paddle/test_pr74594_broadcast_shapes.py b/test/swe_paddle/test_pr74594_broadcast_shapes.py
new file mode 100644
index 0000000..c2d8800
--- /dev/null
+++ b/test/swe_paddle/test_pr74594_broadcast_shapes.py
@@ -0,0 +1,101 @@
+from __future__ import annotations
+
+import ast
+import copy
+from pathlib import Path
+from types import SimpleNamespace
+from typing import Sequence
+
+import pytest
+
+
+TARGET = Path("python/paddle/tensor/math.py")
+
+
+def _extract_function(name, namespace):
+ tree = ast.parse(TARGET.read_text(encoding="utf-8"), filename=str(TARGET))
+ node = next(
+ (
+ copy.deepcopy(item)
+ for item in tree.body
+ if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef))
+ and item.name == name
+ ),
+ None,
+ )
+ assert node is not None, f"paddle.tensor.math must provide {name}"
+ module = ast.Module(body=[node], type_ignores=[])
+ ast.fix_missing_locations(module)
+ scope = {"Sequence": Sequence, **namespace}
+ exec(compile(module, str(TARGET), "exec"), scope)
+ return scope[name]
+
+
+def _broadcast_two(x_shape, y_shape):
+ left = list(x_shape)
+ right = list(y_shape)
+ width = max(len(left), len(right))
+ left = [1] * (width - len(left)) + left
+ right = [1] * (width - len(right)) + right
+ result = []
+ for x_dim, y_dim in zip(left, right):
+ if x_dim == y_dim:
+ result.append(x_dim)
+ elif x_dim == 1:
+ result.append(y_dim)
+ elif y_dim == 1:
+ result.append(x_dim)
+ else:
+ raise ValueError(
+ f"shapes {x_shape!r} and {y_shape!r} are not broadcastable"
+ )
+ return result
+
+
+def _load_broadcast_shapes():
+ core = SimpleNamespace(broadcast_shape=_broadcast_two)
+ broadcast_shape = _extract_function("broadcast_shape", {"core": core})
+ return _extract_function(
+ "broadcast_shapes", {"broadcast_shape": broadcast_shape}
+ )
+
+
+def test_existing_broadcast_shape_delegation_remains_compatible():
+ calls = []
+ marker = [2, 3, 3]
+
+ def broadcast_shape(x_shape, y_shape):
+ calls.append((x_shape, y_shape))
+ return marker
+
+ function = _extract_function(
+ "broadcast_shape",
+ {"core": SimpleNamespace(broadcast_shape=broadcast_shape)},
+ )
+ x_shape = [2, 1, 3]
+ y_shape = (1, 3, 1)
+
+ result = function(x_shape, y_shape)
+
+ assert result is marker
+ assert calls == [(x_shape, y_shape)]
+
+
+def test_broadcast_shapes_combines_multiple_shapes_and_rejects_mismatch():
+ function = _load_broadcast_shapes()
+
+ assert function([5, 1, 3], [1, 4, 1], [1, 1, 3]) == [5, 4, 3]
+ assert function([2, 3, 1], [6], [3, 1]) == [2, 3, 6]
+ assert function([0, 3], [3], []) == [0, 3]
+
+ with pytest.raises(ValueError):
+ function([5, 1, 3], [1, 4, 1], [1, 2, 3])
+
+
+def test_broadcast_shapes_supports_empty_and_single_shape_inputs():
+ function = _load_broadcast_shapes()
+
+ assert function() == []
+ assert function((8, 3)) == [8, 3]
+ assert function([]) == []
+ assert function([], [2, 3, 4]) == [2, 3, 4]
4 changes: 4 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-74594/tests/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail

python -m pytest test/swe_paddle/test_pr74594_broadcast_shapes.py -q