---
description: Cpp下沉智能体
globs:
alwaysApply: false
---

# 一、角色定义

你擅长《Paddle API对齐PyTorch项目》中的**C++下沉方案**的代码开发。通过将Python API下沉至C++层，可以减少Python装饰器带来的性能开销，提升API调度效率。

**特点**：
- **性能提升**：消除Python层逻辑开销
- **参数解析**：在C++层高效解析位置参数和关键字参数
- **参数映射**：自动完成Paddle与Torch API参数别名的映射转换
- **默认值处理**：C++层原生支持默认参数
- **灵活扩展**：支持自定义参数处理逻辑


# 二、不同场景分类的标准工作流程

根据API的复杂度，将下沉场景分为三类：

## 2.1 场景一：仅参数名不一致（最简单）

**适用条件（需全部满足）：**
- Paddle Python API与`ops.yaml`中参数**类型或数量一致**
- Paddle API和PyTorch API仅参数名称不一致
- Python端调用`_C_ops`前**无任何前处理逻辑**

**典型示例：** `paddle.log2`

请严格按以下步骤依次执行，不要自行修改或跳过步骤：

### Step 1：配置 `python_api_info.yaml`

添加参数别名映射（按op name的字典序添加）：

```yaml
- op : log2
  name : [paddle.log2, paddle.Tensor.log2]
  args_alias :
    use_default_mapping : True  # 启用默认映射：x->input
```

**说明：**
- `name`：指定对应的Python API名称（函数API和Tensor方法）
- `use_default_mapping: True`：自动将`input`映射为`x`（兼容torch），默认已配置如下字段映射：
```python
# 如果不在以下范围里，请配置自定义映射
args_default_mapping = {
    "x": ["input"],
    "y": ["other"],
    "axis": ["dim"],
    "keepdims": ["keepdim"],
}
```
- 若需自定义映射，使用格式：
```yaml
args_alias:
x : [input]
axis : [dim]
```

### Step 2：迁移文档到 `_paddle_docs.py`

```python
add_doc_and_signature(
    "log2",
    r"""
    Calculates the log to the base 2 of the given input tensor, element-wise.

    .. math::

        Out = \log_2x

    Args:
        x (Tensor): Input tensor must be one of the following types: int32, int64, float16, bfloat16, float32, float64, complex64, complex128.
        name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
        out (Tensor, optional): The output Tensor. If set, the result will be stored in this Tensor. Default: None.

    Returns:
        Tensor: The log to the base 2 of the input Tensor computed element-wise.

    Examples:

        .. code-block:: pycon

            >>> import paddle

            >>> # example 1: x is a float
            >>> x_i = paddle.to_tensor([[1.0], [2.0]])
            >>> res = paddle.log2(x_i)
            >>> res
            Tensor(shape=[2, 1], dtype=float32, place=Place(cpu), stop_gradient=True,
            [[0.],
             [1.]])

            >>> # example 2: x is float32
            >>> x_i = paddle.full(shape=[1], fill_value=2, dtype='float32')
            >>> paddle.to_tensor(x_i)
            >>> res = paddle.log2(x_i)
            >>> res
            Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
            [1.])

            >>> # example 3: x is float64
            >>> x_i = paddle.full(shape=[1], fill_value=2, dtype='float64')
            >>> paddle.to_tensor(x_i)
            >>> res = paddle.log2(x_i)
            >>> res
            Tensor(shape=[1], dtype=float64, place=Place(cpu), stop_gradient=True,
            [1.])
""",
    """
def log2(
    x: Tensor,
    name: str | None = None,
    *,
    out: Tensor | None = None,
) -> Tensor
""",
)
```

**注意事项：**
- 如果API支持`out`参数，必须在_paddle_docs.py中添加`out`参数说明
- 使用 `# type: ignore` 忽略静态检查误报（多类型输入场景）

### Step 3：替换Python实现

找到API的Python实现位置（如 `python/paddle/tensor/math.py`，注意不要误检索到稀疏API位置）：

1. 在文件上方合适位置**导入**C++实现
2. 直接**删除**原有的Python函数实现

```python
# 在文件最上方合适位置导入（不要添加注释）
from paddle._C_ops import log2  # noqa: F401

# 以下内容全部删除（不要添加注释）
# def log2(x: Tensor, name: str | None = None) -> Tensor:
#     ...
```

### Step 4：添加测试用例到 `test_api_compatibility.py`

不要新建任何测试文件，直接在 `test/legacy_test/test_api_compatibility.py` 中添加测试：

```python
class Test<APIName>(unittest.TestCase):
    def setUp(self):
        np.random.seed(2025)
        self.places = get_devices()
        self.shape = [...]
        self.dtype = 'float32'
        self.init_data()

    def init_data(self):
        self.np_x = np.random.rand(*self.shape).astype(self.dtype)
        # Other initializations...

    def test_dygraph_Compatibility(self):
        paddle.disable_static()
        x = paddle.to_tensor(self.np_x)

        # 1. Paddle Positional arguments
        out1 = paddle.<api_name>(x, ...)

        # 2. Paddle keyword arguments
        out2 = paddle.<api_name>(x=x, ...)

        # 3. Pytorch Positional arguments (if different with paddle args)
        out3 = paddle.<api_name>(x, ...)

        # 4. PyTorch keyword arguments (alias)
        out4 = paddle.<api_name>(input=x, dim=...)

        # 5. Mixed arguments
        out5 = paddle.<api_name>(x, axis=...)

        # 6. out parameter test (if supported)
        if hasattr(self, 'supports_out') and self.supports_out():
            out6 = paddle.empty_like(x)
            out7 = paddle.<api_name>(x, ..., out=out5)

        # 7. Tensor method - args
        out8 = x.<api_name>(...)

        # 8. Tensor method - kwargs
        out9 = x.<api_name>(axis=...)

        # Verify all outputs
        for out in [out1, out2, out3, out4, out5, out6, out7, out8, out9]:
            np.testing.assert_allclose(out.numpy(), ...)

        paddle.enable_static()

    def test_static_Compatibility(self):
        paddle.enable_static()
        main = paddle.static.Program()
        startup = paddle.static.Program()
        with paddle.static.program_guard(main, startup):
            x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype)

            # Create multiple outputs
            out1 = paddle.<api_name>(x, ...)
            out2 = paddle.<api_name>(x=x, ...)
            out3 = paddle.<api_name>(input=x, dim=...)

            exe = paddle.static.Executor()
            fetches = exe.run(
                main,
                feed={"x": self.np_x},
                fetch_list=[out1, out2, out3],
            )

            # Verify all outputs
            for out in fetches:
                np.testing.assert_allclose(out, ...)
```

必须覆盖的场景：
1. ✅ Paddle位置参数（全部位置参数）
2. ✅ Paddle关键字参数（全部关键字参数）
3. ✅ PyTorch位置参数（如与Paddle位置参数不同）
4. ✅ PyTorch关键字参数（使用别名）
5. ✅ 混合参数（位置+关键字）
6. ✅ out参数（如API支持）
7. ✅ 类方法调用（位置参数）
8. ✅ 类方法调用（关键字参数）
9. ✅ 动态图和静态图两种模式
10. ✅ 异常参数（参数缺失、类型错误、关键字名错误）

完整测试示例，请参考 `Paddle/test/legacy_test/test_api_compatibility.py` 中已有的测试类结构。

### Step 5：编译并运行

每次修改 Paddle 源码后，必须重新编译方可生效。单测编写完成后，请按以下命令验证执行（不可修改）：

1. **重新编译项目**：
   ```bash
   cd /workspace/Paddle/build
   cmake ..
   make -j$(nproc)
   ```

2. **运行单测文件**：
   ```bash
   python <所修改的单测文件名>
   ```

3. **问题排查**：根据报错信息调整代码或测试用例，确保所有测试用例通过


## 2.2 场景二：具有前处理逻辑（中等复杂度）

**适用条件（全部满足）：**
- Paddle Python API与`ops.yaml`中参数**类型或数量一致**
- Paddle API和PyTorch API仅参数名称不一致
- Python端在调用`_C_ops`前**有其他前处理逻辑**

**典型示例：** `paddle.logsumexp`

请严格按以下步骤依次执行，不要自行修改或跳过步骤：

### Step 1：配置 `python_api_info.yaml`

```yaml
- op : logsumexp
  name : [paddle.logsumexp, paddle.Tensor.logsumexp]
  args_alias:
    use_default_mapping : True  # x->input, axis->dim
  pre_process:
    func : LogsumexpPreProcess(x, axis, reduce_all)  # 前处理函数
```

**关键点：**
- `name`：指定对应的Python API名称
- `args_alias.use_default_mapping`：启用默认参数映射
- `pre_process.func`：指定前处理函数名称及其参数列表


### Step 2：实现前处理函数

将Python端在调用`_C_ops`前的**其他前处理逻辑**，修改为C++的实现。
在 `paddle/fluid/pybind/arg_pre_process.h` 声明：

```cpp
namespace paddle {
namespace pybind {

// 动态图版本
void LogsumexpPreProcess(Tensor *x, std::vector<int> *axis, bool *reduce_all);

// 静态图版本
void LogsumexpPreProcess(pir::Value *x, std::vector<int> *axis, bool *reduce_all);

}  // namespace pybind
}  // namespace paddle
```

在 `paddle/fluid/pybind/arg_pre_process.cc` 实现：

```cpp
#include "paddle/fluid/pybind/arg_pre_process.h"
#include "paddle/fluid/eager/utils.h"
#include "paddle/fluid/pir/utils/general_functions.h"

namespace paddle {
namespace pybind {

// 动态图实现
void LogsumexpPreProcess(Tensor *x, std::vector<int> *axis, bool *reduce_all) {
  // Python原逻辑：
  // if axis == [] or len(axis) == len(x.shape):
  //     reduce_all = True
  // else:
  //     reduce_all = False

  if (axis->empty() || axis->size() == x->dims().size()) {
    *reduce_all = true;
  } else {
    *reduce_all = false;
  }
}

// 静态图实现
void LogsumexpPreProcess(pir::Value *x, std::vector<int> *axis, bool *reduce_all) {
  std::vector<int64_t> x_shape = pir::GetShapeFromValue(*x);
  if (axis->empty() || axis->size() == x_shape.size()) {
    *reduce_all = true;
  } else {
    *reduce_all = false;
  }
}

}  // namespace pybind
}  // namespace paddle
```

**注意事项：**
- 必须同时实现动态图和静态图版本
- 函数通过指针修改参数值
- 直接翻译Python前处理逻辑到C++

### Step 3~6：参考场景一的Step 2~5

## 2.3 场景三：复杂参数映射（最复杂）

**适用条件（全部满足）：**
- Paddle Python API与`ops.yaml`中参数**类型或数量不一致**
- 需要复杂的自定义参数解析逻辑，来匹配Paddle Python API与`ops.yaml`的参数差异

**典型示例：** `paddle.argmax`/`paddle.argmin`

请严格按以下步骤依次执行，不要自行修改或跳过步骤：

### Step 1：配置 `python_api_info.yaml`

```yaml
- op : argmax
  name : [paddle.argmax, paddle.Tensor.argmax]
  args_mapper :
    func : ArgMaxMinMapper  # 自定义参数映射函数
```

**关键点：**
- `name`：指定对应的Python API名称
- `args_mapper.func`：指定自定义Mapper函数名称
- 当使用`args_mapper`时，**不会生成默认参数解析代码**
- Mapper需要手动解析所有参数，包括参数别名支持

### Step 2：实现自定义Mapper

将Python API与`ops.yaml`中参数**类型或数量不一致**的差异，通过C++自定义Mapper来进行匹配与转换。

在 `paddle/fluid/pybind/args_mapper.h` 声明：

```cpp
namespace paddle {
namespace pybind {

// 动态图版本
void ArgMaxMinMapper(PyObject* args,
                     PyObject* kwargs,
                     Tensor* x,
                     paddle::experimental::Scalar* axis,
                     bool* keepdims,
                     bool* flatten,
                     phi::DataType* dtype);

// 静态图版本
void ArgMaxMinMapper(PyObject* args,
                     PyObject* kwargs,
                     pir::Value* x,
                     pir::Value* axis,
                     bool* keepdims,
                     bool* flatten,
                     phi::DataType* dtype);

}  // namespace pybind
}  // namespace paddle
```

在 `paddle/fluid/pybind/args_mapper.cc` 实现：

```cpp
#include "paddle/fluid/pybind/args_mapper.h"
#include "paddle/fluid/eager/utils.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pybind/eager_utils.h"
#include "paddle/fluid/pybind/op_function_common.h"

namespace paddle {
namespace pybind {

// 动态图实现
void ArgMaxMinMapper(PyObject* args,
                     PyObject* kwargs,
                     Tensor* x,
                     paddle::experimental::Scalar* axis,
                     bool* keepdims,
                     bool* flatten,
                     phi::DataType* dtype) {
  // Python参数：(x, axis, keepdim, dtype, name)
  // C++ _C_ops参数：(x, axis, keepdim, flatten, dtype)

  int nargs = args ? static_cast<int>(PyTuple_Size(args)) : 0;
  int remaining_kwargs = kwargs ? static_cast<int>(PyDict_Size(kwargs)) : 0;
  const int max_args = 4;  // 不包括name参数
  CheckParamsCount(nargs, remaining_kwargs, max_args);

  // 1. 解析x参数（支持多个别名）
  *x = GetTensorFromArgsOrKWArgs("argmax",
                                 "x",
                                 args,
                                 0,
                                 kwargs,
                                 {"x", "input"},  // 别名列表
                                 nargs,
                                 &remaining_kwargs,
                                 false);

  // 2. 解析axis参数并处理特殊逻辑
  PyObject* axis_obj = GetItemFromArgsOrKWArgs(
      args, 1, kwargs, {"axis", "dim"}, nargs, &remaining_kwargs);

  // Python逻辑：
  // flatten = False
  // if axis is None:
  //     flatten = True
  //     axis = 0
  *flatten = false;
  if (axis_obj == Py_None || axis_obj == nullptr) {
    *flatten = true;
    *axis = 0;
  } else {
    *axis = CastPyArg2Scalar(axis_obj, "argmax", 1);
  }

  // 3. 解析keepdims参数（支持多个别名）
  PyObject* keepdims_obj = GetItemFromArgsOrKWArgs(
      args, 2, kwargs, {"keepdim", "keepdims"}, nargs, &remaining_kwargs);
  *keepdims = CastPyArg2Boolean(keepdims_obj, "argmax", 2, false);

  // 4. 解析dtype参数并验证
  PyObject* dtype_obj = GetItemFromArgsOrKWArgs(
      args, 3, kwargs, {"dtype"}, nargs, &remaining_kwargs);

  PADDLE_ENFORCE_NE(
      dtype_obj,
      Py_None,
      phi::errors::InvalidArgument("the value of 'dtype' in argmax and argmin "
                                   "could not be None, but received None"));
  *dtype = CastPyArg2DataType(dtype_obj, "argmax", 3, phi::DataType::INT64);

  // 5. 检查剩余参数有效性
  CheckRemainingParamsValidity(args, kwargs, remaining_kwargs, nargs);
}

// 静态图实现
void ArgMaxMinMapper(PyObject* args,
                     PyObject* kwargs,
                     pir::Value* x,
                     pir::Value* axis,
                     bool* keepdims,
                     bool* flatten,
                     phi::DataType* dtype) {
  int nargs = args ? static_cast<int>(PyTuple_Size(args)) : 0;
  int remaining_kwargs = kwargs ? static_cast<int>(PyDict_Size(kwargs)) : 0;
  const int max_args = 4;
  CheckParamsCount(nargs, remaining_kwargs, max_args);

  // 1. 解析Value类型的x
  PyObject* x_obj = GetItemFromArgsOrKWArgs(
      args, 0, kwargs, {"x", "input"}, nargs, &remaining_kwargs);
  *x = CastPyArg2Value(x_obj, "argmax", 0, false);

  // 2. 解析axis并转换为Value类型
  PyObject* axis_obj = GetItemFromArgsOrKWArgs(
      args, 1, kwargs, {"axis", "dim"}, nargs, &remaining_kwargs);

  *flatten = false;
  if (axis_obj == Py_None || axis_obj == nullptr) {
    *flatten = true;
    // 静态图中axis需要是Value类型
    *axis = paddle::dialect::full(
        std::vector<int64_t>{1}, 0, phi::DataType::INT64, phi::CPUPlace());
  } else if (PyObject_CheckIRValue(axis_obj)) {
    *axis = CastPyArg2Value(axis_obj, "argmax", 1);
  } else {
    int64_t axis_tmp = CastPyArg2Long(axis_obj, "argmax", 1);
    *axis = paddle::dialect::full(std::vector<int64_t>{1},
                                  axis_tmp,
                                  phi::DataType::INT64,
                                  phi::CPUPlace());
  }

  // 3-5. 解析其他参数（同动态图）
  PyObject* keepdims_obj = GetItemFromArgsOrKWArgs(
      args, 2, kwargs, {"keepdim", "keepdims"}, nargs, &remaining_kwargs);
  *keepdims = CastPyArg2Boolean(keepdims_obj, "argmax", 2, false);

  PyObject* dtype_obj = GetItemFromArgsOrKWArgs(
      args, 3, kwargs, {"dtype"}, nargs, &remaining_kwargs);

  PADDLE_ENFORCE_NE(
      dtype_obj,
      Py_None,
      phi::errors::InvalidArgument("the value of 'dtype' in argmax and argmin "
                                   "could not be None, but received None"));
  *dtype = CastPyArg2DataType(dtype_obj, "argmax", 3, phi::DataType::INT64);

  CheckRemainingParamsValidity(args, kwargs, remaining_kwargs, nargs);
}

}  // namespace pybind
}  // namespace paddle
```

**关键技术点：**

1. **参数解析工具函数：**
   - `GetTensorFromArgsOrKWArgs`：解析Tensor参数，支持多个别名
   - `GetItemFromArgsOrKWArgs`：获取通用Python对象
   - `CastPyArg2Scalar`：转换为Scalar类型
   - `CastPyArg2Boolean`：转换为bool类型
   - `CastPyArg2DataType`：转换为DataType枚举
   - `CheckParamsCount`：检查参数数量
   - `CheckRemainingParamsValidity`：检查是否有未处理参数

2. **静态图特殊处理：**
   - 使用`pir::Value`代替`Tensor`
   - 常量值需转换为`Value`类型：使用`paddle::dialect::full`

3. **参数别名支持：**
   - 通过传入别名列表`{"x", "input"}`同时支持Paddle和Torch风格

### Step 3~6：参考场景一的Step 2~5

## 2.4 不同场景对比

| 项目 | 场景一：仅参数名不同 | 场景二：有前处理逻辑 | 场景三：复杂参数映射 |
|------|---------------------|---------------------|---------------------|
| **YAML配置** | `args_alias` + `use_default_mapping` | `args_alias` + `pre_process` | `args_mapper` |
| **C++实现** | 无需额外C++代码 | `arg_pre_process.h/cc` | `args_mapper.h/cc` |
| **实现难度** | ⭐ 简单 | ⭐⭐ 中等 | ⭐⭐⭐ 复杂 |
| **参数解析** | 自动生成 | 自动生成 + 前处理 | 完全手动 |
| **适用情况** | 仅参数名不一致 | 仅参数名不一致+前处理逻辑 | 参数类型/数量不一致 |
| **示例API** | log2 | logsumexp | argmax, argmin |


# 三、异常处理

## 3.1 处理流程
当遇到错误时，建议按照以下步骤处理，确保代码能运行通过：
1. **定位错误**：仔细阅读错误信息，确定错误类型和位置
2. **分析原因**：根据错误信息分析具体问题，例如参数错误、类型不匹配
3. **修改代码**：根据错误信息与分析结果，调整代码
4. **验证修复**：重新运行测试确认问题解决

## 3.2 常见错误及解决方案

### 静态图兼容性问题
**错误现象**：
```python
TypeError: (InvalidType) all(): argument (position 1) must be Value, but got Variable
```

**问题原因**：
API下沉后使用了新的Value类型系统，但测试代码仍在使用旧的Variable类型

**解决方法**：
1. 删除过时的测试文件：
   ```bash
   rm -rf test/deprecated/test_xxx.py
   ```
2. 删除`CMakeLists.txt`中涉及的单测配置：
   ```cmake
   # 删除相关单测配置
   # set_tests_properties(test_lbfgs_deprecated PROPERTIES TIMEOUT 100)
   ```
3. 检查并更新所有引用这些测试的代码

### 参数解析错误
**错误现象**：
```python
TypeError: argmax() got an unexpected keyword argument 'invalid_param'
```

**解决方法**：
1. 检查参数名称拼写
2. 确认是否支持该参数

### 类型转换错误
**错误现象**：
```python
TypeError: expected Tensor as argument, got numpy.ndarray
```

**解决方法**：
1. 确保输入数据是Tensor类型：
   ```python
   tensor_input = paddle.to_tensor(numpy_input)
   ```
2. 检查数据类型是否匹配


# 四、技术背景知识

## 4.1 工具函数速查

```cpp
// 获取Tensor参数（支持别名）
Tensor GetTensorFromArgsOrKWArgs(
    const std::string& op_name,
    const std::string& param_name,
    PyObject* args, int arg_idx,
    PyObject* kwargs, const std::vector<std::string>& aliases,
    int nargs, int* remaining_kwargs, bool required);

// 获取通用Python对象
PyObject* GetItemFromArgsOrKWArgs(
    PyObject* args, int arg_idx,
    PyObject* kwargs, const std::vector<std::string>& aliases,
    int nargs, int* remaining_kwargs);

// 类型转换
paddle::experimental::Scalar CastPyArg2Scalar(PyObject* obj, const std::string& op_name, int arg_idx);
bool CastPyArg2Boolean(PyObject* obj, const std::string& op_name, int arg_idx, bool default_value);
phi::DataType CastPyArg2DataType(PyObject* obj, const std::string& op_name, int arg_idx, phi::DataType default_value);
std::vector<int> CastPyArg2Ints(PyObject* obj, const std::string& op_name, int arg_idx);

// 检查函数
void CheckParamsCount(int nargs, int remaining_kwargs, int max_args);
void CheckRemainingParamsValidity(PyObject* args, PyObject* kwargs, int remaining_kwargs, int nargs);
```

# 五、注意事项
1. 若Python API参数顺序与`_C_ops` API不同，属于特殊情况，Cpp下沉方案无法实现，需要使用Python装饰器方案。
2. 代码中不允许提交中文，代码注释采用英文
3. 若API需支持`out`参数，必须修改`add_doc_and_signature`中的字符串，增加out参数
4. 示例代码若涉及多种数据类型，可能触发类型检查误报，添加注释忽略：
```python
  .. code-block:: pycon
      >>> # type: ignore
      >>> import paddle
      >>> x = paddle.to_tensor([1.0, 2.0])
```
