Skip to content
Open
37 changes: 37 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-58323/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# PaddlePaddle__Paddle-58323

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

## Source

| Field | Value |
| --- | --- |
| Repo | `PaddlePaddle/Paddle` |
| PR | [58323](https://github.com/PaddlePaddle/Paddle/pull/58323) |
| PR title | 【Hackathon 5th No.33】为 Paddle 新增 atleast_1d / atleast_2d / atleast_3d API -part |
| Base commit | `431a0d53bd1578385ebcd3021f08d88ed6f75c70` |
| Merged at | `2023-11-16T12:29:50Z` |
| Hackathon | `5th` task `33` |
| Task type | `feature_enhancement` |
| Resource | CPU |

## Summary

Add `atleast_1d`, `atleast_2d`, `atleast_3d` APIs for Paddle, which convert scalar or low-dimensional inputs to tensors with at least the requested number of dimensions, preserving higher-dimensional inputs unchanged.

## 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.
- `tests/test.patch`: test patch exposing the target behavior.
- `tests/test.sh`: minimal target test command.
- `environment/README.md`: environment notes for reproduction.

## Verification

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

Expected behavior: applying `tests/test.patch` to `base_commit` should fail on the target behavior; applying both `tests/test.patch` and `solution/code.patch` should pass the target tests.
26 changes: 26 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-58323/environment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Environment Notes

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

## Expected Environment

- Repository: `PaddlePaddle/Paddle`
- Base commit: `431a0d53bd1578385ebcd3021f08d88ed6f75c70`
- Resource: CPU
- Build path: Paddle source checkout at the base commit. This task only involves Python-side changes (tensor manipulation APIs built on existing `reshape`/`unsqueeze`), so a source build with Python tests is sufficient.

## 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.
35 changes: 35 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-58323/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 新增 atleast_1d / atleast_2d / atleast_3d API

## 详细描述

为 Paddle 新增 `atleast_1d`、`atleast_2d`、`atleast_3d` 三个张量操作 API。它们将标量或低维输入转换为至少具有指定维度的张量,高维输入保持不变。对标量输入(0-D)会先转换为 1-D 张量,不足目标维度时通过 reshape / unsqueeze 补维。

要求支持:

- `paddle.atleast_1d(*inputs, name=None)`:输出至少 1-D
- 0-D 输入 reshape 为 `[1]`
- 1-D 及以上输入原样返回
- `paddle.atleast_2d(*inputs, name=None)`:输出至少 2-D
- 0-D 输入 reshape 为 `[1, 1]`
- 1-D 输入 unsqueeze 到 `[1, N]`
- 2-D 及以上输入原样返回
- `paddle.atleast_3d(*inputs, name=None)`:输出至少 3-D
- 0-D、1-D、2-D 输入分别补维到 3-D
- 3-D 及以上输入原样返回
- 支持可变参数:单输入返回单个 Tensor,多输入返回 Tensor 列表
- 输入可为 Tensor 或可被 `paddle.to_tensor` 转换的标量
- 在 `paddle` 顶层命名空间、`paddle.tensor` 命名空间及 tensor 方法中导出
- 支持的数据类型:float16, float32, float64, int16, int32, int64, int8, uint8, complex64, complex128, bfloat16, bool

## 验收说明

- `atleast_1d` / `atleast_2d` / `atleast_3d` 前向行为与 NumPy 对应 API(`np.atleast_1d/2d/3d`)一致
- 单输入返回 Tensor,多输入返回 Tensor 列表
- 高维输入保持不变,低维输入正确补维
- 静态图与动态图下均可用

## Acceptance Criteria

- The behavior described above should be fixed.
- Existing valid behavior should remain unchanged.
- Do not satisfy the task by deleting tests, weakening assertions, or bypassing validation broadly.
54 changes: 54 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-58323/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Task Proposal: PaddlePaddle__Paddle-58323

## 1. 来源信息

- Instance ID:`PaddlePaddle__Paddle-58323`
- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/58323
- PR 标题:`【Hackathon 5th No.33】为 Paddle 新增 atleast_1d / atleast_2d / atleast_3d API -part`
- `base_commit`:`431a0d53bd1578385ebcd3021f08d88ed6f75c70`
- merge commit:`557499bd9b511515309bcf8665b215ef7f016e71`
- merged 时间:`2023-11-16T12:29:50Z`
- 后续联系人:megemini

## 2. 问题一句话

为 Paddle 新增 `atleast_1d` / `atleast_2d` / `atleast_3d` API,将标量或低维输入转换为至少具有指定维度的张量。

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

- **真实性**:该任务来自已合入的 Paddle Hackathon 5th 框架开发 PR,关联 RFC(community#679),不是合成任务。
- **代表性**:它覆盖 Python 层张量操作 API 的完整落地链路:`paddle` 顶层命名空间与 `paddle.tensor` 命名空间导出、tensor 方法注册、可变参数处理(单输入返回 Tensor / 多输入返回列表)以及基于既有 reshape/unsqueeze 的实现。
- **边界清楚**:目标行为集中在三个 API 的补维语义(0-D/1-D/2-D 输入分别如何补维),测试补丁可直接暴露目标行为。
- **非平凡性**:需要正确处理可变参数、标量输入转换、不同维度的分支逻辑,并保证与 NumPy 语义一致;非纯配置修改。

## 4. 任务类型和标签

- 任务类型:`feature_enhancement`
- 执行后端:`cpu`
- 设备范围:`cpu`
- 模块标签:`[python_api, tensor, manipulation]`

## 5. 验证思路

- 目标测试命令:`bash tests/test.sh`
- 目标测试文件:`test/legacy_test/test_atleast_nd.py`
- 修复前预期:在 `base_commit` 上应用 `tests/test.patch` 后,`test_atleast_nd.py` 中 `atleast_1d/2d/3d` 相关测试应 fail(API 不存在)。
- 修复后预期:继续应用 `solution/code.patch` 后,目标测试应 pass。
- P2P 候选:`test_atleast_nd.py` 为 PR 新增文件,无存量测试;可从同模块存量 manipulation 测试中选取回归护栏,由 verifier 自动抽取稳定 nodeid。

## 6. 环境与资源

- 资源需求:CPU
- Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit`
- 是否能提供 Docker:暂无,建议后续补充 source-build Dockerfile
- patch 类型:纯 Python(tensor manipulation API,基于既有 reshape/unsqueeze 实现)
- 环境建议:该样本仅涉及 Python 层改动,source build 后运行 Python 测试即可
- 最小测试命令:`bash tests/test.sh`
- 是否有 oracle 日志:由 SWE-Paddle verifier 结果另行维护

## 7. 风险自查

- 泄露风险:正式 `instruction.md` 应描述目标行为和验收标准,不直接指出具体修改行。
- 环境风险:仅 Python 改动,复现成本低于含 C++ kernel 的样本。
- flaky 风险:`atleast_nd` 为确定性张量操作,flaky 风险低;verifier 仍应重复运行抽取稳定 F2P/P2P nodeid。
- 拆分风险:该 PR 的目标集中在新增三个 atleast API,适合作为一个样本。
237 changes: 237 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-58323/solution/code.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py
index 92ac2fcbb5..c71af71396 100644
--- a/python/paddle/__init__.py
+++ b/python/paddle/__init__.py
@@ -199,6 +199,9 @@ from .tensor.logic import ( # noqa: F401


from .tensor.manipulation import ( # noqa: F401
+ atleast_1d,
+ atleast_2d,
+ atleast_3d,
cast,
cast_,
concat,
@@ -833,6 +836,9 @@ __all__ = [
'logspace',
'reshape',
'reshape_',
+ 'atleast_1d',
+ 'atleast_2d',
+ 'atleast_3d',
'reverse',
'nonzero',
'CUDAPinnedPlace',
diff --git a/python/paddle/tensor/__init__.py b/python/paddle/tensor/__init__.py
index 1d6d6f8a4f..a1557bb458 100644
--- a/python/paddle/tensor/__init__.py
+++ b/python/paddle/tensor/__init__.py
@@ -112,6 +112,9 @@ from .logic import allclose # noqa: F401
from .logic import isclose # noqa: F401
from .logic import equal_all # noqa: F401
from .logic import is_tensor # noqa: F401
+from .manipulation import atleast_1d # noqa: F401
+from .manipulation import atleast_2d # noqa: F401
+from .manipulation import atleast_3d # noqa: F401
from .manipulation import cast # noqa: F401
from .manipulation import cast_ # noqa: F401
from .manipulation import concat # noqa: F401
@@ -731,6 +734,9 @@ tensor_method_func = [
'normal_',
'index_fill',
'index_fill_',
+ 'atleast_1d',
+ 'atleast_2d',
+ 'atleast_3d',
]

# this list used in math_op_patch.py for magic_method bind
diff --git a/python/paddle/tensor/manipulation.py b/python/paddle/tensor/manipulation.py
index 0686a17fa9..3195ffe8a8 100644
--- a/python/paddle/tensor/manipulation.py
+++ b/python/paddle/tensor/manipulation.py
@@ -3955,6 +3955,184 @@ def reshape_(x, shape, name=None):
return out


+def atleast_1d(*inputs, name=None):
+ """
+ Convert inputs to tensors and return the view with at least 1-dimension. Scalar inputs are converted,
+ one or high-dimensional inputs are preserved.
+
+ Args:
+ inputs (Tensor|list(Tensor)): One or more tensors. The data type is ``float16``, ``float32``, ``float64``, ``int16``, ``int32``, ``int64``, ``int8``, ``uint8``, ``complex64``, ``complex128``, ``bfloat16`` or ``bool``.
+ name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
+
+ Returns:
+ One Tensor, if there is only one input.
+ List of Tensors, if there are more than one inputs.
+
+ Examples:
+ .. code-block:: python
+
+ >>> import paddle
+
+ >>> # one input
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> out = paddle.atleast_1d(x)
+ >>> print(out)
+ Tensor(shape=[1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [123])
+
+ >>> # more than one inputs
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> y = paddle.to_tensor([1.23], dtype='float32')
+ >>> out = paddle.atleast_1d(x, y)
+ >>> print(out)
+ [Tensor(shape=[1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [123]), Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
+ [1.23000002])]
+
+ >>> # more than 1-D input
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> y = paddle.to_tensor([[1.23]], dtype='float32')
+ >>> out = paddle.atleast_1d(x, y)
+ >>> print(out)
+ [Tensor(shape=[1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [123]), Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
+ [[1.23000002]])]
+ """
+ out = []
+ for tensor in inputs:
+ tensor = paddle.to_tensor(tensor)
+ if tensor.dim() == 0:
+ result = tensor.reshape((1,))
+ else:
+ result = tensor
+ out.append(result)
+
+ if len(out) == 1:
+ return out[0]
+ else:
+ return out
+
+
+def atleast_2d(*inputs, name=None):
+ """
+ Convert inputs to tensors and return the view with at least 2-dimension. Two or high-dimensional inputs are preserved.
+
+ Args:
+ inputs (Tensor|list(Tensor)): One or more tensors. The data type is ``float16``, ``float32``, ``float64``, ``int16``, ``int32``, ``int64``, ``int8``, ``uint8``, ``complex64``, ``complex128``, ``bfloat16`` or ``bool``.
+ name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
+
+ Returns:
+ One Tensor, if there is only one input.
+ List of Tensors, if there are more than one inputs.
+
+ Examples:
+ .. code-block:: python
+
+ >>> import paddle
+
+ >>> # one input
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> out = paddle.atleast_2d(x)
+ >>> print(out)
+ Tensor(shape=[1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [[123]])
+
+ >>> # more than one inputs
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> y = paddle.to_tensor([1.23], dtype='float32')
+ >>> out = paddle.atleast_2d(x, y)
+ >>> print(out)
+ [Tensor(shape=[1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [[123]]), Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
+ [[1.23000002]])]
+
+ >>> # more than 2-D input
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> y = paddle.to_tensor([[[1.23]]], dtype='float32')
+ >>> out = paddle.atleast_2d(x, y)
+ >>> print(out)
+ [Tensor(shape=[1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [[123]]), Tensor(shape=[1, 1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
+ [[[1.23000002]]])]
+ """
+ out = []
+ for tensor in inputs:
+ tensor = paddle.to_tensor(tensor)
+ if tensor.dim() == 0:
+ result = tensor.reshape((1, 1))
+ elif tensor.dim() == 1:
+ result = paddle.unsqueeze(tensor, axis=0)
+ else:
+ result = tensor
+ out.append(result)
+
+ if len(out) == 1:
+ return out[0]
+ else:
+ return out
+
+
+def atleast_3d(*inputs, name=None):
+ """
+ Convert inputs to tensors and return the view with at least 3-dimension. Three or high-dimensional inputs are preserved.
+
+ Args:
+ inputs (Tensor|list(Tensor)): One or more tensors. The data type is ``float16``, ``float32``, ``float64``, ``int16``, ``int32``, ``int64``, ``int8``, ``uint8``, ``complex64``, ``complex128``, ``bfloat16`` or ``bool``.
+ name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
+
+ Returns:
+ One Tensor, if there is only one input.
+ List of Tensors, if there are more than one inputs.
+
+ Examples:
+ .. code-block:: python
+
+ >>> import paddle
+
+ >>> # one input
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> out = paddle.atleast_3d(x)
+ >>> print(out)
+ Tensor(shape=[1, 1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [[[123]]])
+
+ >>> # more than one inputs
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> y = paddle.to_tensor([1.23], dtype='float32')
+ >>> out = paddle.atleast_3d(x, y)
+ >>> print(out)
+ [Tensor(shape=[1, 1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [[[123]]]), Tensor(shape=[1, 1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
+ [[[1.23000002]]])]
+
+ >>> # more than 3-D input
+ >>> x = paddle.to_tensor(123, dtype='int32')
+ >>> y = paddle.to_tensor([[[[1.23]]]], dtype='float32')
+ >>> out = paddle.atleast_3d(x, y)
+ >>> print(out)
+ [Tensor(shape=[1, 1, 1], dtype=int32, place=Place(cpu), stop_gradient=True,
+ [[[123]]]), Tensor(shape=[1, 1, 1, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
+ [[[[1.23000002]]]])]
+ """
+ out = []
+ for tensor in inputs:
+ tensor = paddle.to_tensor(tensor)
+ if tensor.dim() == 0:
+ result = tensor.reshape((1, 1, 1))
+ elif tensor.dim() == 1:
+ result = paddle.unsqueeze(tensor, axis=[0, 2])
+ elif tensor.dim() == 2:
+ result = paddle.unsqueeze(tensor, axis=2)
+ else:
+ result = tensor
+ out.append(result)
+
+ if len(out) == 1:
+ return out[0]
+ else:
+ return out
+
+
def gather_nd(x, index, name=None):
"""

Loading