From 856df26a695249225cee7daff1712b728cc6fcce Mon Sep 17 00:00:00 2001 From: zhouwei25 Date: Tue, 20 Jan 2026 14:27:21 +0000 Subject: [PATCH 1/2] Add python-decorator agent rule and update other agent rule --- .../api_compatibility/0-api-compatibility.mdr | 37 +- .../api_compatibility/1-scheme-decision.mdr | 89 ++- .../2-1-python-decorator.mdr | 603 ++++++++++++++++++ .../api_compatibility/2-2-sink-to-cpp.mdr | 132 ++-- .../api_compatibility/3-paconvert-test.mdr | 32 +- .../api_compatibility/4-modify-docs.mdr | 30 +- 6 files changed, 763 insertions(+), 160 deletions(-) create mode 100644 docs/dev_guides/coding_agent_rules/api_compatibility/2-1-python-decorator.mdr diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/0-api-compatibility.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/0-api-compatibility.mdr index f28ad294620..b0f340c2a6e 100644 --- a/docs/dev_guides/coding_agent_rules/api_compatibility/0-api-compatibility.mdr +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/0-api-compatibility.mdr @@ -8,7 +8,7 @@ alwaysApply: false 你是《Paddle API对齐PyTorch项目》的**主控智能体**,负责统筹全流程的工作,调用多个子智能体,最终完成输入的所有API对齐。 -## 核心职责 +**核心职责**: 1. 接收待对齐的Pytorch API列表 2. 为对应的Paddle API决策改动方案 3. 执行具体的Paddle API修改 @@ -70,7 +70,7 @@ torch.logsumexp |Paddle API兼容性单测位置|`test_api_compatibility.py`|Paddle/test/legacy_test/test_api_compatibility.py|| |Pytorch API单测位置|`test_{api_name}.py`|PaConvert/tests/|PaConvert/tests/test_tan.py|| -## 4.2 Paddle API架构(5层调用栈) +## 4.3 Paddle API架构(5层调用栈) Paddle API从上到下由5层组成(本项目直接修改第1、5层,对于第2~4层通常是修改yaml配置文件,例如python_api_info.yaml): @@ -100,7 +100,7 @@ Tensor atan(const Tensor& x, ...) void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) ``` -## 4.3 专业术语表 +## 4.4 专业术语表 | 术语 | 定义 | 备注 | |------|------|------| @@ -119,20 +119,20 @@ void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) # 五、代码修改规范 -## 1. 代码注释规范 +## 5.1 代码注释规范 - ✅ 改动的位置如果方便注释,可以注释`# Edit by AI Agent`,表明这是你完成的工作 -## 2. 代码质量规范 +## 5.2 代码质量规范 - ✅ 保持代码风格与项目一致 - ✅ 不破坏现有功能 - ✅ 确保向后兼容性 -## 3. 测试规范 +## 5.3 测试规范 - ✅ 修改后必须通过原有测试 - ✅ 必须添加新的单元测试 - ✅ 必须通过PyTorch对齐验证 -## 4. 文档规范 +## 5.4 文档规范 - ✅ 同步更新中文文档 - ✅ 文档格式符合Paddle规范 - ✅ 准确描述API功能和参数 @@ -155,7 +155,7 @@ void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) - Step4:对**所有API**更新文档 3. **流程推进的豁免与放弃条件**: - **豁免条件**(跳过后续步骤,但记录决策结果): - * 非方案2:若决策不为方案2,则该API跳过后续Step2~4,无需自行处理 + * 若决策不为方案1/2,则该API跳过后续Step2~4,无需自行处理 - **放弃策略**(合理分配精力,最大化成功率): * **放弃判断标准**: - 当某个API在Step2或Step3经过多次尝试(建议3次)仍无法通过验证 @@ -179,7 +179,7 @@ void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) **执行步骤**: 1. 输入:需要对齐的PyTorch API列表(如 `torch.atan`、`torch.asinh`) -2. 调用 `API改动方案决策智能体` +2. 严格执行1-scheme-decision.mdr,如果你找不到该文件,请直接结束会话,并告知我异常 3. 输出:方案类型、对应Paddle API、差异分类、决策依据 **方案类型**: @@ -197,7 +197,7 @@ void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) **执行步骤**: 1. 输入:方案类型、对应Paddle API(如 `paddle.atan`、`paddle.asinh`)、差异分类、决策依据 2. 根据方案类型,调用对应的子智能体,每个子智能体批量处理其所负责的API: - - 方案1 → `Python装饰器智能体`(豁免) + - 方案1 → `Python装饰器智能体` - 方案2 → `Cpp下沉智能体` - 方案3 → `修改API智能体`(豁免) - 方案4 → `新增API智能体`(豁免) @@ -234,8 +234,6 @@ void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) ## 6.3 重要约束 ⚠️ -### 执行原则 - 1. **流程正向推进原则** - 正常情况下必须遵循 Step1 → Step2 → Step3 → Step4 的顺序 - 每个步骤完成并验证通过后,才能进入下一步骤 @@ -270,11 +268,10 @@ void AtanKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) | 序号 | 智能体名称 | 负责步骤 | 主要职责 | 对应方案 | |------|-----------|---------|---------|---------| -| 1 | **API改动方案决策智能体** | Step 1 | 通过API差异分析,决策API的兼容修改方案 | - | -| 2 | **Python装饰器智能体**(暂未实现) | Step 2 | 通过装饰器对齐参数签名 | 方案1:Python装饰器 | -| 3 | **Cpp下沉智能体** | Step 2 | 将API从Python层下沉到C++层 | 方案2:C++下沉 | -| 4 | **修改API智能体**(暂未实现) | Step 2 | 修改现有API实现 | 方案3:修改API方案 | -| 5 | **新增API智能体**(暂未实现) | Step 2 | 新增缺失的API | 方案4:新增API方案 | -| 6 | **新增compat类型API智能体**(暂未实现) | Step 2 | 添加兼容性API | 方案5:新增compat方案 | -| 7 | **Pytorch对齐验证智能体** | Step 3 | 验证修改后的Paddle API能与PyTorch API完全对齐 | - | -| 8 | **API文档修改智能体** | Step 4 | 更新Paddle API中文文档 | - | +| 1 | **Python装饰器智能体** | Step 2 | 通过装饰器对齐参数签名 | 方案1:Python装饰器 | +| 2 | **Cpp下沉智能体** | Step 2 | 将API从Python层下沉到C++层 | 方案2:C++下沉 | +| 3 | **修改API智能体**(暂未实现) | Step 2 | 修改现有API实现 | 方案3:修改API方案 | +| 4 | **新增API智能体**(暂未实现) | Step 2 | 新增缺失的API | 方案4:新增API方案 | +| 5 | **新增compat类型API智能体**(暂未实现) | Step 2 | 添加兼容性API | 方案5:新增compat方案 | +| 6 | **Pytorch对齐验证智能体** | Step 3 | 验证修改后的Paddle API能与PyTorch API完全对齐 | - | +| 7 | **API文档修改智能体** | Step 4 | 更新Paddle API中文文档 | - | diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/1-scheme-decision.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/1-scheme-decision.mdr index 69046b061c8..c461b05729d 100644 --- a/docs/dev_guides/coding_agent_rules/api_compatibility/1-scheme-decision.mdr +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/1-scheme-decision.mdr @@ -1,5 +1,5 @@ --- -description: API改动方案决策智能体 +description: API改动方案决策规则 globs: alwaysApply: false --- @@ -11,10 +11,10 @@ alwaysApply: false # 二、输入输出规范 ## 2.1 输入 -- 需要对齐的PyTorch API列表(如 `torch.atan`、`torch.asinh`) +需要对齐的PyTorch API列表(如 `torch.atan`、`torch.asinh`) ## 2.2 输出 -- 方案类型、对应Paddle API、差异分类、决策依据(以表格形式展示) +方案类型、对应Paddle API、差异分类、决策依据(以表格形式展示) ## 2.3 输出内容 输出应包含如下内容,以表格式形式展示: @@ -36,9 +36,6 @@ alwaysApply: false |torch.frexp|方案3+方案1|paddle.frexp|torch参数更多|API相对引用路径一致,差异为:1)仅参数名不同(input→x);2)仅多out参数。当前Python实现包含多个paddle操作调用(abs、floor、log2等),逻辑较复杂,不满足C++下沉条件。选择方案3修改API,在末尾添加out参数(带默认值None),保持后向兼容。同时方案1支持参数名不同的重载情况。| ``` -## 2.5 工作目标 -**核心目标**:决策修改方案,使Paddle API能与PyTorch API完全对齐一致。 - # 三、候选方案 ## 方案1:Python装饰器 @@ -120,9 +117,9 @@ alwaysApply: false # 四、标准工作流程 -## Step 1: 读取差异文档 +## Step 1: 分析差异文档 -### 1.1 文档获取要求 +### 1.1 获取差异文档 务必找到每个PyTorch API对应的API差异文档(`torch.{api_name}.md`),如果实在无法找到差异文档,则差异分类直接视作『API完全一致』。 ### 1.2 差异分类定义 @@ -144,9 +141,9 @@ alwaysApply: false | 12 | API别名 | PyTorch API是其他API的别名 | | 13 | 功能缺失 | Paddle暂无等效实现 | -## Step 2: 识别差异分类 +### 1.3 提取差异信息 -从差异文档中提取: +提取相关差异信息,提供给Step2进行方案决策: | 项目 | 内容 | |------|------| @@ -154,18 +151,18 @@ alwaysApply: false | 参数映射 | 参数对应关系和差异说明(忽略paddle的 `name` 参数,`name` 参数无意义) | | 转写示例 | 代码转换示例 | -## Step 3: 决策流程 +## Step 2: 方案决策 -### 3.1 关键概念 +### 2.1 关键概念 **API相对引用路径**:API完整路径在去掉框架导入模块(torch/paddle)后剩余的部分 - 示例:`torch.nn.functional.dropout` 的API相对引用路径是 `nn.functional.dropout` - 示例:`paddle.nn.functional.dropout` 的API相对引用路径是 `nn.functional.dropout` - 示例:`torch.Tensor.tile` 的API相对引用路径是 `Tensor.tile` -### 3.2 关键执行原则 +### 2.2 关键原则 -> **⚠️ 严格执行原则**:决策时必须严格遵守以下原则,不得主观臆断: +> **⚠️ 严格遵循**:决策时必须严格遵守以下原则,不得主观臆断: > > 1. **API相对引用路径不一致 → 必须方案4(新增API)** > - 只要API相对引用路径不一致,直接选择方案4,无需再分析其他因素 @@ -176,79 +173,77 @@ alwaysApply: false > - 严格按照各方案的适用条件判断 > - 不得因"可以"、"应该"等主观判断偏离规则定义 -### 3.3 决策流程图 +### 2.3 决策流程图 ``` 开始 │ - ├─→ API相对引用路径是否一致? - │ ├─ 否 → 方案4(新增API)→ 结束 - │ └─ 是 ↓ - │ │ - │ └─→ 具体有哪些差异? - │ │ - │ ├─ API完全一致 → 方案6(无需改动)→ 结束 - │ │ - │ ├─ 仅参数名不一致 → 方案2(C++下沉)→ 不适用则方案1→ 结束 - │ │ - │ ├─ paddle参数更多 → 是否影响对齐? - │ │ ├─ 否 → 方案6(无需改动)→ 结束 - │ │ └─ 是 → 方案3(修改API)→ 存在兼容性则方案5→ 结束 - │ │ - │ ├─ 参数默认值不一致 → 方案3(修改API)→ 存在兼容性则方案5→ 结束 - │ │ - │ ├─ torch参数更多 → 仅多out参数? - │ │ ├─ 是 → 方案2(C++下沉)→ 不适用则方案3→ 存在兼容性则方案1→ 无法区分则方案5→ 结束 - │ │ └─ 否 → 方案3(修改API)→ 存在兼容性则方案1→ 无法区分则方案5→ 结束 - │ │ - │ ├─ 输入参数用法/类型不一致 → 方案3(修改API)→ 存在兼容性则方案1→ 无法区分则方案5→ 结束 - │ │ - │ └─ 返回参数类型不一致 → 方案5(新增compat类型API)→ 结束 + ├───→ API相对引用路径是否一致? ──────┐ + │ │ + │是 │否 + ↓ ↓ +具体有哪些差异? 方案4(新增API)→结束 + │ + ├──→ API完全一致 → 方案6(无需改动)→结束 + │ + ├──→ 仅参数名不一致 → 方案2(C++下沉)→ 不适用则方案1→结束 + │ + ├──→ paddle参数更多 → 是否影响对齐?─┬→否→方案6(无需改动)→结束 + │ └→是→方案3(修改API)→存在兼容性则方案5→结束 + │ + ├──→ 参数默认值不一致 → 方案3(修改API)→存在兼容性则方案5→结束 + │ + ├──→ torch参数更多 → 仅多out参数?─┬→是→方案2(C++下沉)→不适用则方案3→存在兼容性则方案1→无法区分则方案5→结束 + │ └→否→方案3(修改API)→存在兼容性则方案1→无法区分则方案5→结束 + │ + ├──→ 输入参数用法/类型不一致 → 方案3(修改API)→存在兼容性则方案1→无法区分则方案5→结束 + │ + └──→ 返回参数类型不一致 → 方案5(新增compat类型API)→结束 ``` -### 3.4 详细决策规则 +### 2.4 详细决策规则 **前置判断**:以下规则均基于**API相对引用路径一致**的前提。只要API相对引用路径不一致,直接选择**方案4(新增API)**,无需进入后续判断。 -#### 3.3.1 API完全一致 +#### 1. API完全一致 - **决策**:方案6(无需改动) -#### 3.3.2 仅参数名不一致 +#### 2. 仅参数名不一致 - **优先级1**:方案2(C++下沉) - **适用条件**:满足方案2适用条件(见第三部分定义) - **优势**:性能最优 - **优先级2**:方案1(Python装饰器) - **适用条件**:不满足方案2适用条件 -#### 3.3.3 paddle参数更多 +#### 3. paddle参数更多 - **判断**:额外参数是否影响对齐 - **否**(如默认参数,Paddle保持默认即可)→ 方案6(无需改动) - **是** → 方案3(修改API) - **存在兼容性** → 方案5(新增compat类型API) -#### 3.3.4 参数默认值不一致 +#### 4. 参数默认值不一致 - **优先级1**:方案3(修改API) - **回退**:存在兼容性 → 方案5(新增compat类型API) -#### 3.3.5 torch参数更多 +#### 5. torch参数更多 - **子判断**:是否仅多out参数 - **是** → 方案2(C++下沉) - **不适用** → 方案3(修改API)→ 存在兼容性则方案1→ 无法区分则方案5 - **否** → 方案3(修改API)→ 存在兼容性则方案1→ 无法区分则方案5 -#### 3.3.6 输入参数用法/类型不一致 +#### 6. 输入参数用法/类型不一致 - **优先级1**:方案3(修改API) - **存在兼容性** → 方案1(Python装饰器) - **无法区分两套签名** → 方案5(新增compat类型API) -#### 3.3.7 返回参数类型不一致 +#### 7. 返回参数类型不一致 - **唯一决策**:方案5(新增compat类型API) - **原因**: - ❌ 方案3:修改返回值必然不兼容 - ❌ 方案1:装饰器只能根据输入区分,无法区分返回值差异 - ✅ 方案5:在compat路径下新增,不影响原API -### 3.5 方案组合说明 +### 2.5 方案组合说明 > **重要提醒**:一个API可能涉及多种差异分类,需要综合分析所有差异点,可以组合多种方案来消除所有差异点。 diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/2-1-python-decorator.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/2-1-python-decorator.mdr new file mode 100644 index 00000000000..735234195d4 --- /dev/null +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/2-1-python-decorator.mdr @@ -0,0 +1,603 @@ +--- +description: Python装饰器智能体 +globs: +alwaysApply: false +--- + +# 一、角色定义 + +你擅长《Paddle API对齐PyTorch项目》中的**Python装饰器方案**的代码开发。通过Python装饰器,在Python层为Paddle API提供参数别名、参数顺序、参数类型和参数用法的兼容转换,实现PyTorch风格的API调用,并保持Paddle API的向后兼容性。 + +**特点**: +- **零侵入性**:无需修改API的实现代码 +- **适用面广**:支持灵活处理各种API签名重载情况,如参数名不同、参数顺序不同、参数个数不同、参数类型不同、参数用法不同等 +- **向后兼容**:保持Paddle原有API调用方式 +- **开发效率**:相比C++下沉方案,修改更快速直接 +- **性能开销**:Python装饰器层会引入轻微性能开销 + +# 二、现有装饰器体系 + +Paddle现有装饰器统一位于 `Paddle/python/paddle/utils/decorator_utils.py`,按功能分为两类: + +## 2.1 通用参数别名装饰器 + +| 装饰器 | 支持参数数量 | 性能 | 使用场景 | 优先级 | +|---------|-------------|------|----------|--------| +| `param_one_alias` | 1个 | 高 | 单个参数别名(如 x↔input) | ⭐⭐⭐ | +| `param_two_alias` | 2个 | 高 | 两个参数别名(如 x↔input, axis↔dim) | ⭐⭐⭐ | +| `ParamAliasDecorator` | 3个及以上 | 中 | 复杂参数映射场景 | ⭐⭐ | +| `param_two_alias_one_default` | 2个+默认值 | 低 | 需要默认值(median专用) | ⭐(不推荐)| + +## 2.2 专用装饰器 + +| 装饰器 | 功能描述 | 关键特性 | +|--------|----------|----------| +| `index_select_decorator` | 参数别名 + 参数顺序转换 | 检测第2个位置参数是否为int来判断不同参数顺序 | +| `index_add_decorator` | 参数别名 + 参数顺序转换 | 检测第2个位置参数是否为int来判断不同参数顺序 | +| `transpose_decorator` | 参数别名 + 参数用法转换 | 通过dim0/dim1两个int来自动构造perm列表 | +| `size_args_decorator` | 参数别名 + 可变参数 | 合并全部int位置参数为shape列表 | +| `view_decorator` | 参数别名 + 可变参数 | 合并全部int位置参数为shape列表 | +| `reshape_decorator` | 参数别名 + 可变参数 | 第1个参数别名,合并后面多个int位置参数为shape列表 | +| `expand_decorator` | 参数别名 + 可变参数 | 第1个参数别名,合并后面多个int位置参数为shape列表 | +| `legacy_reduction_decorator` | 参数别名 + 报错信息增强 | 检测到size_average/reduce用法后,增强报错信息 | +| `lp_pool_function_decorator` | 参数别名 + 参数顺序转换 | 检测第5个位置参数是否为bool来判断不同参数顺序 | + +# 三、标准工作流程 + +## Step 1: 差异分析与选择装饰器 + +根据PyTorch API与Paddle API的**差异分析**来区分不同场景: + +### 1. 仅参数名不同(参数顺序相同) + +根据需要映射的参数数量选择: +- 1个参数 → `param_one_alias` +- 2个参数 → `param_two_alias` +- 3个及以上参数 → `ParamAliasDecorator` + +**使用示例**: +```python +# 单个参数别名 +## torch.deg2rad(input) +## paddle.deg2rad(x) +from paddle.utils.decorator_utils import param_one_alias +@param_one_alias(['x', 'input']) +def deg2rad(x, name=None, *, out=None): + ... + +# 两个参数别名 +## torch.squeeze(input, dim) +## paddle.squeeze(x, axis) +from paddle.utils.decorator_utils import param_two_alias +@param_two_alias(["x", "input"], ["axis", "dim"]) +def squeeze(x, axis=None, name=None): + ... + +# 多个参数别名 +## torch.nn.functional.normalize(input, p, dim, eps) +## paddle.nn.functional.normalize(x, p, axis, epsilon) +from paddle.utils.decorator_utils import ParamAliasDecorator +@ParamAliasDecorator({"x": ["input"], "axis": ["dim"], "epsilon": ["eps"]}) +def normalize(x, p=2, axis=1, epsilon=1e-12, out=None, name=None): + ... +``` + +### 2. 参数名不同 + 可变参数 + +选择现有专用装饰器:`size_args_decorator`、`reshape_decorator`、`expand_decorator`、`view_decorator` + +**使用示例**: +```python +# torch.reshape(x, 2, 5) # shape支持可变参数用法 +# paddle.reshape(x, [2, 5]) +from paddle.utils.decorator_utils import reshape_decorator +@reshape_decorator() +def reshape(x, shape, name=None): + ... +``` + +### 3. 参数名不同 + 参数顺序不同 + +开发新的专用装饰器 + +**示例**: +```python +# torch.index_select(input, dim, index) +# paddle.index_select(x, index, axis) +from paddle.utils.decorator_utils import index_select_decorator +@index_select_decorator() +def index_select(x, index, axis=0, *, out=None): + ... +``` + +### 4. 参数名不同 + 参数用法不同 + +开发新的专用装饰器 + +**示例**: +```python +# torch.transpose(x, dim0, dim1) # 交换两个维度, +# paddle.transpose(x, perm) +from paddle.utils.decorator_utils import transpose_decorator +@transpose_decorator() +def transpose(x, perm=None, name=None): + ... +``` + +## Step 2: 应用或开发装饰器 + +根据**Step1**中的不同场景: +- **场景 1 & 2**(仅参数名不同、可变参数):请进入 **方式一(使用现有装饰器)**。 +- **场景 3 & 4**(参数顺序不同、参数用法不同及其他情况):请进入 **方式二(开发新的专用装饰器)**。 + +### 方式一:使用现有装饰器 + +1. 在API函数文件中导入装饰器 +2. 在API函数定义前添加装饰器 +3. 如需添加out参数,在函数签名中添加(见Step 3) + +**示例**: +```python +from paddle.utils.decorator_utils import param_two_alias + +@param_two_alias(["x", "input"], ["axis", "dim"]) +def cumsum(x, axis=None, dtype=None, name=None): + ... +``` + +### 方式二:开发新的专用装饰器 + +1. 在`Paddle/python/paddle/utils/decorator_utils.py`中定义新装饰器 +2. 按照以下模板和要点实现 +3. 在API函数上使用新装饰器 + +**装饰器模板**: +```python +import functools +import inspect + +def custom_decorator(): + """ + 装饰器功能说明 + + Usage Example: + PyTorch: torch.api(arg1, arg2) + Paddle: paddle.api(arg2, arg1) # 或其他映射关系 + """ + + def decorator(func): + @functools.wraps(func) # 保持原函数的__name__, __doc__等元信息 + def wrapper(*args, **kwargs): + # 1. 参数别名映射 + if "input" in kwargs and "x" not in kwargs: + kwargs["x"] = kwargs.pop("input") + + # 2. 参数顺序转换或其他特殊处理 + # 根据需要调整args + + # 3. 调用原函数 + return func(*args, **kwargs) + + wrapper.__signature__ = inspect.signature(func) # 保持函数签名 + return wrapper + + return decorator +``` + +**关键实现要点**: + +1. **处理参数别名** +```python +if "input" in kwargs and "x" not in kwargs: + kwargs["x"] = kwargs.pop("input") +``` + +2. **位置参数类型检测**(用于判断参数顺序) +```python +if len(args) >= 2 and isinstance(args[1], int): + # 检测第二个位置参数是否为int,从而判断参数顺序为torch用法还是paddle用法,并调整用法 + ## torch.index_select(input, dim, index) + ## paddle.index_select(x, index, axis) + kwargs["x"] = args[0] + kwargs["axis"] = args[1] + args = args[2:] +``` + +3. **可变参数处理** +```python +# 合并多个int位置参数为列表 +if len(args) >= 2 and all(isinstance(arg, int) for arg in args[1:]): + kwargs["shape"] = list(args[1:]) + args = args[:1] +``` + +**完整示例**: +```python +def index_select_decorator(): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + # 1. 参数别名映射 + if "input" in kwargs and "x" not in kwargs: + kwargs["x"] = kwargs.pop("input") + if "dim" in kwargs and "axis" not in kwargs: + kwargs["axis"] = kwargs.pop("dim") + + # 2. PyTorch参数顺序转换 + if len(args) >= 2 and isinstance(args[1], int): + # PyTorch顺序: (input, dim, index) → Paddle顺序: (x, index, axis) + kwargs["x"] = args[0] + kwargs["axis"] = args[1] + if len(args) > 2: + kwargs["index"] = args[2] + args = () + + return func(*args, **kwargs) + + wrapper.__signature__ = inspect.signature(func) + return wrapper + + return decorator +``` + +**开发参考资料**: +可参考 `Paddle/python/paddle/utils/decorator_utils.py` 来实现,该文件包含了项目中全部专有装饰器的实现(如 `reshape_decorator`, `transpose_decorator` 等)。 + + +## Step 3: 添加out参数支持(如需要) + +### 方式一:直接指定out(推荐) + +**适用条件**: +1. API最后一个逻辑是调用`_C_ops` +2. API调用了其他API,调用的最后一个其他API也支持out + +**示例**: +```python +# API最后一个逻辑是调用`_C_ops` +@param_two_alias(["x", "input"], ["y", "other"]) +def less_than(x, y, name=None, *, out=None) -> Tensor: + """ + Args: + ... + out (Tensor|None, optional): The output tensor. Default: None. + """ + if in_dynamic_or_pir_mode(): + return _C_ops.less_than(x, y, out=out) + else: + ... + +# API调用的最后一个其他API也支持out +@param_two_alias(["x", "input"], ["axis", "dim"]) +def fft(x, n=None, axis=-1, norm="backward", name=None, *, out=None) -> Tensor: + """ + Args: + ... + out (Tensor|None, optional): The output tensor. Default: None. + """ + if is_integer(x) or is_floating_point(x): + return fft_r2c( + x, n, axis, norm, forward=True, onesided=False, name=name, out=out + ) + else: + return fft_c2c(x, n, axis, norm, forward=True, name=name, out=out) +``` + +### 方式二:通过assign实现 + +**适用条件**:不符合方式一的情况 + +**示例**: +```python +def func(x, axis=None, name=None, *, out: Tensor | None = None): + """ + Args: + ... + out (Tensor|None, optional): The output tensor. Default: None. + """ + ret = <计算逻辑> + + if out is not None: + paddle.assign(ret, out) + return out + return ret +``` + +注意`out`参数需与Pytorch用法一致,一般情况下均是keyword-only参数(使用`*,`分隔) + +## Step 4: 更新文档 + +在函数文档字符串中为有别名的参数添加Alias Support说明和out说明: + +```python +def func(x, axis=None, name=None, *, out=None): + """ + Compute the cumulative sum of elements in the input tensor along the given axis. + + Args: + x (Tensor): The input tensor needed to be cumsumed. + Alias: ``input``. + axis (int, optional): The dimension to accumulate along. + Alias: ``dim``. + name (str|None, optional): Name for operation. + + Keyword args: + out(Tensor, optional): The output tensor. + """ +``` + +**注意事项**: +- Alias说明应放在参数描述的末尾 +- 使用`Alias: ``alias_name``.格式 +- 多个Alias别名描述为: `Alias: ``input`` or ``data``.` +- out参数使用关键字参数来描述 + +## Step 5: 添加兼容单测 + +所有兼容性单测代码都统一添加到文件:`Paddle/test/legacy_test/test_api_compatibility.py`。 + +### 单测类模板 + +```python +class TestAPI_Compatibility(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) + paddle_dygraph_out = [] + + # 1. Positional arguments + out1 = paddle.(x, ...) + paddle_dygraph_out.append(out1) + + # 2. Paddle keyword arguments + out2 = paddle.(x=x, ...) + paddle_dygraph_out.append(out2) + + # 3. PyTorch keyword arguments (alias) + out3 = paddle.(input=x, dim=...) + paddle_dygraph_out.append(out3) + + # 4. Mixed arguments + out4 = paddle.(x, axis=...) + paddle_dygraph_out.append(out4) + + # 5. out parameter test (if supported) + if hasattr(self, 'supports_out') and self.supports_out(): + out5 = paddle.empty_like(x) + out6 = paddle.(x, ..., out=out5) + paddle_dygraph_out.append(out5) + paddle_dygraph_out.append(out6) + + # Verify all outputs + for out in paddle_dygraph_out: + 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.base.program_guard(main, startup): + x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) + + # Create multiple outputs + out1 = paddle.(x, ...) + out2 = paddle.(x=x, ...) + out3 = paddle.(input=x, dim=...) + + exe = paddle.base.Executor(paddle.CPUPlace()) + 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位置参数 +4. ✅ PyTorch关键字参数(使用别名) +5. ✅ 混合参数(位置+关键字) +6. ✅ out参数(如API支持) +7. ✅ 动态图和静态图两种模式 +8. ✅ 异常参数(参数缺失、类型错误、关键字名错误) + +### 完整测试示例 + +请参考 `Paddle/test/legacy_test/test_api_compatibility.py` 中已有的测试类结构。 + +## Step 6: 运行兼容单测 + +完成单测编写后,需验证代码能正确执行,直接按以下命令执行(不要修改命令): + +1. **重新编译项目**: + ```bash + cd /workspace/Paddle/build + cmake .. > cmake.log 2>&1 + make -j$(nproc) > make.log 2>&1 + ``` + +2. **运行单测文件**: + ```bash + python <所修改的单测文件名> + ``` + +3. **问题排查**:根据报错信息调整代码或测试用例,确保所有测试用例通过 + + +# 四、异常处理 + +## 4.1 标准处理流程 + +1. **定位错误**:仔细阅读错误信息,确定错误类型和位置 +2. **分析原因**:根据错误信息分析具体问题(装饰器实现错误、使用错误等) +3. **修改代码**:根据分析结果调整代码 +4. **验证修复**:重新运行测试确认问题解决 + +## 4.2 常见错误及解决方案 + +### 类型转换错误 + +**错误现象**: +```python +TypeError: expected Tensor as argument, got numpy.ndarray +``` + +**解决方法**: +```python +# 确保输入是Tensor类型 +tensor_input = paddle.to_tensor(numpy_input) +paddle.api(tensor_input, ...) +``` + +## 4.3 调试技巧 + +```python +# 添加调试日志 +import logging +logging.basicConfig(level=logging.DEBUG) + +# 在装饰器中添加日志 +def wrapper(*args, **kwargs): + logging.debug(f"Before: args={args}, kwargs={kwargs}") + # 处理逻辑... + logging.debug(f"After: args={args}, kwargs={kwargs}") + return func(*args, **kwargs) + +# 或使用打印调试 +def wrapper(*args, **kwargs): + print(f"[DEBUG] args={args}, kwargs={kwargs}") + # ... +``` + +# 五、技术背景知识 + +## 5.1 Paddle API分层结构 + +**Paddle API架构(5层)**: +1. **Python层**:Python函数定义(本方案修改层) +2. **Pybind层**:Python与C++绑定(自动生成) +3. **Dygraph层**:动态图前反向传播组合(自动生成) +4. **C++ API层**:Kernel选择调度(自动生成) +5. **Kernel层**:实际计算逻辑实现(C++) + +**本方案修改范围**: +- ✅ 仅修改第1层(Python层) +- ❌ 第2~4层通过yaml配置自动生成,无需手动修改 +- ❌ 第5层涉及C++实现,不在本方案范围内 + +## 5.2 Python装饰器原理 + +### 装饰器基本结构 + +```python +import functools +import inspect + +def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + # 参数处理逻辑 + return func(*args, **kwargs) + + wrapper.__signature__ = inspect.signature(func) + return wrapper +``` + +**关键点**: +- `@functools.wraps(func)`:保持原函数的`__name__`、`__doc__`等元信息 +- `wrapper.__signature__`:保持函数签名,支持IDE的参数提示 +- 装饰器必须返回wrapper函数 + +### 参数处理模式 + +**kwargs别名映射** +```python +if "input" in kwargs and "x" not in kwargs: + kwargs["x"] = kwargs.pop("input") +``` + +**避免参数冲突** +```python +if "x" in kwargs and "input" in kwargs: + raise ValueError("Cannot specify both 'x' and 'input'") +``` + +**位置参数类型检测** +```python +if len(args) >= 2 and isinstance(args[1], int): + kwargs["x"] = args[0] + kwargs["shape"] = list(args[1:]) + args = () +``` + +## 5.3 装饰器限制与注意事项 + +### 性能开销 + +- 装饰器会引入额外的函数调用层级 +- 对于高频调用的API,装饰器开销可能不可忽略 +- 考虑对性能敏感的API使用C++下沉方案 + +### 调试复杂性 + +- 装饰器增加了调用栈深度 +- 错误追踪时可能需要多跳一层 +- 使用日志辅助调试 + +### 与其他装饰器共存 + +- 需要注意装饰器的应用顺序 +- 确保装饰器之间不产生冲突 +- 不同装饰器的参数处理逻辑应该兼容 + +# 六、开发检查清单 + +完成API对齐后,请确认以下清单: + +## 代码修改 +- [ ] 已选择合适的装饰器(通用或专用) +- [ ] 装饰器已正确应用到API函数 +- [ ] out参数已添加(如需要) +- [ ] out参数实现方式正确 +- [ ] 函数签名保持正确(包括__signature__设置) + +## 文档更新 +- [ ] 所有别名的参数都已添加Alias Support说明 +- [ ] out参数已在文档中说明 +- [ ] 文档格式符合规范 + +## 测试覆盖 +- [ ] 动态图测试覆盖所有参数组合 +- [ ] 静态图测试覆盖所有参数组合 +- [ ] Paddle风格的API调用 +- [ ] Pytorch风格的API调用 +- [ ] out参数测试已添加(如支持) +- [ ] 异常参数测试已添加 +- [ ] 所有测试通过 + + +# 七、注意事项 +1. 不要修改sparse目录下的API +2. 确保不破坏现有功能,保持向后兼容性 +3. 开发专用装饰器时参考现有实现 +4. 代码中不允许提交中文,代码注释采用英文 diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/2-2-sink-to-cpp.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/2-2-sink-to-cpp.mdr index 2df91e5b4df..2c4f9b5fd7f 100644 --- a/docs/dev_guides/coding_agent_rules/api_compatibility/2-2-sink-to-cpp.mdr +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/2-2-sink-to-cpp.mdr @@ -8,9 +8,7 @@ alwaysApply: false 你擅长《Paddle API对齐PyTorch项目》中的**C++下沉方案**的代码开发。通过将Python API下沉至C++层,可以减少Python装饰器带来的性能开销,提升API调度效率。 -**重要提示**: - -## 核心优势 +**特点**: - **性能提升**:消除Python层逻辑开销 - **参数解析**:在C++层高效解析位置参数和关键字参数 - **参数映射**:自动完成Paddle与Torch API参数别名的映射转换 @@ -72,6 +70,7 @@ add_doc_and_signature( Calculates the log to the base 2 of the given input tensor, element-wise. .. math:: + Out = \log_2x Args: @@ -83,17 +82,43 @@ add_doc_and_signature( Tensor: The log to the base 2 of the input Tensor computed element-wise. Examples: - .. code-block:: python + + .. 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.]]) - """, - "def log2(x: Tensor, name: str | None = None, *, out: Tensor | None = None) -> Tensor", + + >>> # 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 +""", ) ``` @@ -135,34 +160,38 @@ class TestLog2API_Compatibility(unittest.TestCase): def test_dygraph_Compatibility(self): paddle.disable_static() - x = paddle.to_tensor(self.np_input) paddle_dygraph_out = [] - # 位置参数 (args) + # Positional arguments (args) out1 = paddle.log2(x) paddle_dygraph_out.append(out1) - # Paddle关键字参数 (kwargs) + # Paddle keyword arguments (kwargs) out2 = paddle.log2(x=x) paddle_dygraph_out.append(out2) - # Torch关键字参数 + # Torch keyword arguments out3 = paddle.log2(input=x) paddle_dygraph_out.append(out3) - # 测试out参数 + # Test out parameter out4 = paddle.empty([]) paddle.log2(x, out=out4) paddle_dygraph_out.append(out4) - # Tensor方法 - kwargs + # Tensor method - kwargs out5 = x.log2() paddle_dygraph_out.append(out5) - # Numpy参考输出 + # Test out parameter + out6 = paddle.empty([]) + paddle.log2(x, out=out6) + paddle_dygraph_out.append(out6) + + # Numpy reference output ref_out = np.log2(self.np_input) - # 验证所有输出 + # Verify all outputs for out in paddle_dygraph_out: np.testing.assert_allclose(ref_out, out.numpy()) paddle.enable_static() @@ -174,13 +203,13 @@ class TestLog2API_Compatibility(unittest.TestCase): with base.program_guard(main, startup): x = paddle.static.data(name="x", shape=self.shape, dtype=self.dtype) - # 位置参数 + # Positional arguments out1 = paddle.log2(x) - # Paddle关键字参数 + # Paddle keyword arguments out2 = paddle.log2(x=x) - # Torch关键字参数 + # Torch keyword arguments out3 = paddle.log2(input=x) - # Tensor方法 + # Tensor method out4 = x.log2() exe = base.Executor(paddle.CPUPlace()) @@ -305,7 +334,6 @@ void LogsumexpPreProcess(pir::Value *x, std::vector *axis, bool *reduce_all ### Step 3~6:参考场景一的Step 2~5 - ## 2.3 场景三:复杂参数映射(最复杂) **适用条件:** @@ -529,35 +557,9 @@ void ArgMaxMinMapper(PyObject* args, | **示例API** | log2 | logsumexp | argmax, argmin | -# 三、通用注意事项 +# 三、单元测试规范 -## 3.1 文档相关 - -### 支持out参数的文档修改 -若API需支持`out`参数,必须: -- **英文文档**:直接修改`add_doc_and_signature`中的字符串,增加out参数 - -### 静态检查误报 -示例代码若涉及多种数据类型,可能触发类型检查误报,添加注释忽略: -```python -.. code-block:: python - >>> # type: ignore - >>> import paddle - >>> x = paddle.to_tensor([1.0, 2.0]) -``` - -## 3.2 参数顺序特殊情况 -若Python API参数顺序与`_C_ops` API不同,属于特殊情况,Cpp下沉方案无法实现,需要使用Python装饰器方案。 - -## 3.3 注意事项 -- **静态图代码生成**:cmake阶段 -- **动态图代码生成**:make阶段 -- **重要**:修改YAML后需重新执行`cmake`和`make` - - -# 四、单元测试规范 - -## 4.1 传参方式兼容性测试 +## 3.1 传参方式兼容性测试 ```python # 位置参数 out1 = paddle.api(x, arg1, arg2) @@ -572,7 +574,7 @@ out3 = paddle.api(input=x, dim=arg1, keepdim=arg2) out4 = paddle.api(x, dim=arg1, keepdim=arg2) ``` -## 4.2 Tensor方法测试 +## 3.2 Tensor方法测试 ```python # 方法调用 - 位置参数 out5 = x.api(arg1, arg2) @@ -581,7 +583,7 @@ out5 = x.api(arg1, arg2) out6 = x.api(dim=arg1, keepdim=arg2) ``` -## 4.3 异常参数测试 +## 3.3 异常参数测试 ```python # 参数缺失 with self.assertRaises(ValueError): @@ -596,28 +598,28 @@ with self.assertRaises(TypeError): paddle.api(x, wrong_key=value) ``` -## 4.4 文档绑定测试 +## 3.4 文档绑定测试 ```python def test_docstring(self): self.assertIsNotNone(paddle.api.__doc__) self.assertIn("Args:", paddle.api.__doc__) ``` -## 4.5 动态图和静态图测试 +## 3.5 动态图和静态图测试 必须分别测试动态图(eager mode)和静态图(static graph)两种模式。 -# 五、异常处理 +# 四、异常处理 -## 5.1 处理流程 +## 4.1 处理流程 当遇到错误时,建议按照以下步骤处理,确保代码能运行通过: 1. **定位错误**:仔细阅读错误信息,确定错误类型和位置 2. **分析原因**:根据错误信息分析具体问题,例如参数错误、类型不匹配 3. **修改代码**:根据错误信息与分析结果,调整代码 4. **验证修复**:重新运行测试确认问题解决 -## 5.2 常见错误及解决方案 +## 4.2 常见错误及解决方案 -### 5.2.1 静态图兼容性问题 +### 静态图兼容性问题 **错误现象**: ```python TypeError: (InvalidType) all(): argument (position 1) must be Value, but got Variable @@ -638,7 +640,7 @@ API下沉后使用了新的Value类型系统,但测试代码仍在使用旧的 ``` 3. 检查并更新所有引用这些测试的代码 -### 5.2.2 参数解析错误 +### 参数解析错误 **错误现象**: ```python TypeError: argmax() got an unexpected keyword argument 'invalid_param' @@ -648,7 +650,7 @@ TypeError: argmax() got an unexpected keyword argument 'invalid_param' 1. 检查参数名称拼写 2. 确认是否支持该参数 -### 5.2.3 类型转换错误 +### 类型转换错误 **错误现象**: ```python TypeError: expected Tensor as argument, got numpy.ndarray @@ -662,9 +664,9 @@ TypeError: expected Tensor as argument, got numpy.ndarray 2. 检查数据类型是否匹配 -# 六、技术背景知识 +# 五、技术背景知识 -## 6.1 工具函数速查 +## 5.1 工具函数速查 ```cpp // 获取Tensor参数(支持别名) @@ -691,3 +693,15 @@ std::vector CastPyArg2Ints(PyObject* obj, const std::string& op_name, int a 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]) +``` diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr index adb18321a5f..b20e1c81dbe 100644 --- a/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr @@ -71,7 +71,7 @@ from apibase import APIBase obj = APIBase("torch.target_api") def test_case_1(): - """基础用法""" + """Basic usage""" pytorch_code = textwrap.dedent(""" import torch result = torch.target_api(torch.tensor([1, 2, 3])) @@ -79,7 +79,7 @@ def test_case_1(): obj.run(pytorch_code, ["result"]) def test_case_2(): - """位置参数测试""" + """Positional arguments test""" pytorch_code = textwrap.dedent(""" import torch x = torch.tensor([1.0, 2.0]) @@ -88,7 +88,7 @@ def test_case_2(): obj.run(pytorch_code, ["result"]) def test_case_3(): - """关键字参数测试""" + """Keyword arguments test""" pytorch_code = textwrap.dedent(""" import torch x = torch.tensor([1.0, 2.0]) @@ -97,7 +97,7 @@ def test_case_3(): obj.run(pytorch_code, ["result"]) def test_case_4(): - """关键字参数乱序测试""" + """Keyword arguments out of order test""" pytorch_code = textwrap.dedent(""" import torch x = torch.tensor([1.0, 2.0]) @@ -106,7 +106,7 @@ def test_case_4(): obj.run(pytorch_code, ["result"]) def test_case_5(): - """梯度计算测试""" + """Gradient computation test""" pytorch_code = textwrap.dedent(""" import torch x = torch.tensor([1., 2.], requires_grad=True) @@ -114,12 +114,11 @@ def test_case_5(): y.sum().backward() x_grad = x.grad """) - # 跳过梯度属性检查,因为Paddle的grad.stop_gradient行为与PyTorch不一致,框架机制问题可忽略 + # Skip gradient attribute check because Paddle's stop_gradient mechanism differs from PyTorch's requires_grad mechanism at the framework level. obj.run(pytorch_code, ["y", "x_grad"], check_stop_gradient=False) - def test_case_6(): - """边界条件测试""" + """Edge case test""" pytorch_code = textwrap.dedent(""" import torch result = torch.target_api(torch.empty(0)) @@ -127,7 +126,7 @@ def test_case_6(): obj.run(pytorch_code, ["result"]) def test_case_7(): - """表达式参数测试""" + """Expression argument test""" pytorch_code = textwrap.dedent(""" import torch result = torch.target_api(torch.tensor([1, 2, 3]), 1 + 1) @@ -177,32 +176,35 @@ class CustomAPIBase(APIBase): ``` 2. 确保所有测试用例通过 - # 三、异常处理 +## 3.1 异常处理策略 + 在 PyTorch 对齐验证过程中,需要对不同异常情况进行分类处理。以下是异常处理策略: -## 1. PyTorch 代码执行失败 +### PyTorch 代码执行失败 - **错误标识**:`Failed to execute pytorch code` - **根本原因**:PyTorch 单元测试代码存在问题,无法正常执行 - **处理策略**:修改 PyTorch 单元测试代码,确保能正确执行 Pytorch 代码 - **验证标准**:测试代码应该能够在标准的 PyTorch 环境中正常运行 -## 2. Paddle 代码执行失败 +### Paddle 代码执行失败 - **错误标识**:`Failed to execute paddle code` - **根本原因**:Pytorch单测正确,但修改后的 Paddle API 实现存在问题,导致代码无法执行 - **处理策略**:需要返回到前序Step修改,因此结束本Step,将报错信息返回给主控智能体分析 - **关联任务**:Paddle API需要进一步修改以兼容 PyTorch 接口 -## 3. 计算结果不一致 +### 计算结果不一致 - **错误标识**:`Unable to align results` - **根本原因**:Pytorch单测正确,但Paddle API 与 PyTorch API 计算结果存在差异 - **处理策略**:需要返回到前序Step修改,因此结束本Step,将报错信息返回给主控智能体分析 - **验证要求**:Paddle API需要进一步修改以兼容 PyTorch 接口,确保数值精度、数据类型、形状等完全一致 -**常见问题处理:** -- 如果 Paddle 不支持类型提升或标量输入(已知问题),可以禁用对应测试用例,其他情况不允许禁用单测: +## 3.2 常见错误及解决方案 + +### Paddle不支持类型提升 +- 如果报错是因为 Paddle 不支持类型提升或标量输入(已知问题),可以禁用对应测试用例,其他情况不允许禁用单测: ```python # 将 def test_case_x(): 改为 def _test_case_x(): # 并添加注释说明原因 diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/4-modify-docs.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/4-modify-docs.mdr index 68bae6a223f..3406019b7b3 100644 --- a/docs/dev_guides/coding_agent_rules/api_compatibility/4-modify-docs.mdr +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/4-modify-docs.mdr @@ -17,22 +17,17 @@ alwaysApply: false # 二、工作目录说明 -## 2.1 文档路径规范 - -### 2.1.1 API概览文档 -- **位置**:`docs/api/paddle/Overview_cn.rst` -- **作用**:API索引目录,新增API时需要更新 - -### 2.1.2 API中文文档 -- **文件命名规则**:`{api_name}_cn.rst` - -### 2.1.3 API差异文档(需删除) -- **文件命名规则**:`torch.{api_name}.md` +涉及以下文档的修改: +| 编号 | 文档类型 | 文件命名 | 改动点 | +|------|----------|----------|----------| +| 2.1 | API概览文档 | `docs/api/paddle/Overview_cn.rst` | API索引目录,新增API时需要更新 | +| 2.2 | API中文文档 | `{api_name}_cn.rst` | 针对API功能改动点,修改文档 | +| 2.3 | API差异文档 | `torch.{api_name}.md` | 由于API已实现用法完全一致,不存在差异,需删除 | # 三、标准工作流程 -## 步骤 1: 获取代码变更信息 🔍 +## Step 1: 获取代码变更信息 🔍 **目标**:分析Paddle仓库的API代码变更,明确需要同步到文档的修改点 @@ -43,11 +38,11 @@ alwaysApply: false - 新增API签名 - 修改API签名:新增参数、删除参数、支持参数别名 -## 步骤 2: 更新API中文文档 📝 +## Step 2: 更新API中文文档 📝 **目标**:根据代码变更同步更新`{api_name}_cn.rst`文档 -### 1. 常见修改模式 +### 常见修改模式 #### 模式1:添加参数别名说明 @@ -144,7 +139,7 @@ api_name COPY-FROM: paddle.api_name ``` -### 2. 文档格式规范 +### 文档格式规范 **必须遵守的规范**: 1. ✅ 使用rst格式,严格遵循缩进规则 @@ -158,7 +153,7 @@ COPY-FROM: paddle.api_name - ✅ 修改文档无需添加 `# Edit by AI Agent` 注释 -## 步骤 3: 删除API差异文档 🗑️ +## Step 3: 删除API差异文档 🗑️ **目标**:删除已完全对齐API的差异文档 @@ -243,9 +238,6 @@ list[int],广播后的结果 shape。 COPY-FROM: paddle.broadcast_shapes ``` - - - # 五、常见问题处理 ## 5.1 如何判断是否需要删除差异文档? From ae20b3b8c5d2ae2651c9431d908b834f9efd7227 Mon Sep 17 00:00:00 2001 From: zhouwei25 Date: Mon, 26 Jan 2026 06:46:32 +0000 Subject: [PATCH 2/2] [API Compatibility] Sink bitwise_and/or/xor/not to Cpp --- docs/api/paddle/bitwise_and__cn.rst | 7 ++++++ docs/api/paddle/bitwise_not__cn.rst | 8 +++++- docs/api/paddle/bitwise_or__cn.rst | 7 ++++++ docs/api/paddle/bitwise_xor__cn.rst | 6 +++++ docs/api/paddle/cdist_cn.rst | 6 ++--- .../api_compatibility/3-paconvert-test.mdr | 19 +++++++------- .../torch.Tensor.bitwise_and_.md | 18 ------------- .../args_name_diff/torch.Tensor.bitwise_or.md | 18 ------------- .../torch.Tensor.bitwise_xor_.md | 18 ------------- .../args_name_diff/torch.bitwise_or.md | 25 ------------------- 10 files changed, 40 insertions(+), 92 deletions(-) delete mode 100644 docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_and_.md delete mode 100644 docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_or.md delete mode 100644 docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_xor_.md delete mode 100644 docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.bitwise_or.md diff --git a/docs/api/paddle/bitwise_and__cn.rst b/docs/api/paddle/bitwise_and__cn.rst index 862e9ddb34d..f150ae88de6 100644 --- a/docs/api/paddle/bitwise_and__cn.rst +++ b/docs/api/paddle/bitwise_and__cn.rst @@ -4,8 +4,15 @@ bitwise\_and\_ ------------------------------- .. py:function:: paddle.bitwise_and_(x, y, name=None) + Inplace 版本的 :ref:`cn_api_paddle_bitwise_and` API,对输入 `x` 采用 Inplace 策略。 +参数 +:::::::::::: + - **x** (Tensor) - 输入的 Tensor。别名 ``input``。 + - **y** (Tensor) - 输入的 Tensor。别名 ``other``。 + - **name** (str,可选) - 具体用法请参见 :ref:`api_guide_Name`,一般无需设置,默认值为 None。 + 更多关于 inplace 操作的介绍请参考 `3.1.3 原位(Inplace)操作和非原位操作的区别`_ 了解详情。 .. _3.1.3 原位(Inplace)操作和非原位操作的区别: https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/beginner/tensor_cn.html#id3 diff --git a/docs/api/paddle/bitwise_not__cn.rst b/docs/api/paddle/bitwise_not__cn.rst index 0f52f5413a5..5fc3d7e788f 100644 --- a/docs/api/paddle/bitwise_not__cn.rst +++ b/docs/api/paddle/bitwise_not__cn.rst @@ -3,9 +3,15 @@ bitwise\_not\_ ------------------------------- -.. py:function:: paddle.bitwise_not_(x, out=None, name=None) +.. py:function:: paddle.bitwise_not_(x, name=None) + Inplace 版本的 :ref:`cn_api_paddle_bitwise_not` API,对输入 `x` 采用 Inplace 策略。 +参数 +:::::::::::: + - **x** (Tensor) - 输入的 Tensor。别名 ``input``。 + - **name** (str,可选) - 具体用法请参见 :ref:`api_guide_Name`,一般无需设置,默认值为 None。 + 更多关于 inplace 操作的介绍请参考 `3.1.3 原位(Inplace)操作和非原位操作的区别`_ 了解详情。 .. _3.1.3 原位(Inplace)操作和非原位操作的区别: https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/beginner/tensor_cn.html#id3 diff --git a/docs/api/paddle/bitwise_or__cn.rst b/docs/api/paddle/bitwise_or__cn.rst index 78a62447fb5..f68bac41425 100644 --- a/docs/api/paddle/bitwise_or__cn.rst +++ b/docs/api/paddle/bitwise_or__cn.rst @@ -4,8 +4,15 @@ bitwise\_or\_ ------------------------------- .. py:function:: paddle.bitwise_or_(x, y, name=None) + Inplace 版本的 :ref:`cn_api_paddle_bitwise_or` API,对输入 `x` 采用 Inplace 策略。 +参数 +:::::::::::: + - **x** (Tensor) - 输入的 Tensor。别名 ``input``。 + - **y** (Tensor) - 输入的 Tensor。别名 ``other``。 + - **name** (str,可选) - 具体用法请参见 :ref:`api_guide_Name`,一般无需设置,默认值为 None。 + 更多关于 inplace 操作的介绍请参考 `3.1.3 原位(Inplace)操作和非原位操作的区别`_ 了解详情。 .. _3.1.3 原位(Inplace)操作和非原位操作的区别: https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/beginner/tensor_cn.html#id3 diff --git a/docs/api/paddle/bitwise_xor__cn.rst b/docs/api/paddle/bitwise_xor__cn.rst index c663daaacc5..e248eab01c9 100644 --- a/docs/api/paddle/bitwise_xor__cn.rst +++ b/docs/api/paddle/bitwise_xor__cn.rst @@ -7,6 +7,12 @@ bitwise_xor\_ Inplace 版本的 :ref:`cn_api_paddle_bitwise_xor` API,对输入 `x` 采用 Inplace 策略。 +参数 +:::::::::::: + - **x** (Tensor) - 输入的 Tensor。别名 ``input``。 + - **y** (Tensor) - 输入的 Tensor。别名 ``other``。 + - **name** (str,可选) - 具体用法请参见 :ref:`api_guide_Name`,一般无需设置,默认值为 None。 + 更多关于 inplace 操作的介绍请参考 `3.1.3 原位(Inplace)操作和非原位操作的区别`_ 了解详情。 .. _3.1.3 原位(Inplace)操作和非原位操作的区别: https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/beginner/tensor_cn.html#id3 diff --git a/docs/api/paddle/cdist_cn.rst b/docs/api/paddle/cdist_cn.rst index 8a0dd10d986..a99c59163b8 100644 --- a/docs/api/paddle/cdist_cn.rst +++ b/docs/api/paddle/cdist_cn.rst @@ -14,14 +14,14 @@ cdist 参数 :::::::::::: - - **x** (Tensor) - 形状为 :math:`B \times P \times M` 的 Tensor。 - - **y** (Tensor) - 形状为 :math:`B \times R \times M` 的 Tensor。 + - **x** (Tensor) - 形状为 :math:`B \times P \times M` 的 Tensor。Alias: ``x1``。 + - **y** (Tensor) - 形状为 :math:`B \times R \times M` 的 Tensor。Alias: ``x2``。 - **p** (float, 可选) - 计算每个向量对之间的 p 范数距离的值。默认值为 :math:`2.0`。 - **compute_mode** (str, 可选) - 选择计算模式。 - ``use_mm_for_euclid_dist_if_necessary``: 对于 p = 2.0 且 P > 25, R > 25 ,如果可能,将使用矩阵乘法计算欧氏距离。 - ``use_mm_for_euclid_dist``: 对于 p = 2.0 ,使用矩阵乘法计算欧几里得距离。 - - ``use_loop_for_euclid_dist``: 不使用矩阵乘法计算欧几里得距离。 + - ``donot_use_mm_for_euclid_dist``: 不使用矩阵乘法计算欧几里得距离。 默认值为 ``use_mm_for_euclid_dist_if_necessary``。 - **name** (str, 可选) - 具体用法请参见 :ref:`api_guide_Name` ,一般无需设置,默认值为 None。 diff --git a/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr b/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr index b20e1c81dbe..8a0cf9d65b4 100644 --- a/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr +++ b/docs/dev_guides/coding_agent_rules/api_compatibility/3-paconvert-test.mdr @@ -15,17 +15,18 @@ alwaysApply: false 请严格按以下Step依次执行,不要自行修改或跳过Step: ## Step 1: 标记已对齐的API -**目的:** 将已对齐的 PyTorch API 全部添加到白名单。注意torch.atan与torch.Tensor.atan是不同API。 +**目的:** 将已对齐的 PyTorch API 的Matcher设置为`ChangePrefixMatcher`。 -**操作Step:** -1. 定位文件:`PaConvert/paconvert/global_var.py` -2. 找到 `NO_NEED_CONVERT_LIST` 列表(约在第80行附近) -3. 在列表中添加已对齐的 PyTorch API 完整路径,例如: +> 注意torch.atan与torch.Tensor.atan是两个不同API。 + +**操作步骤:** +1. 定位文件:`PaConvert/paconvert/api_mapping.json` +2. 找到已对齐的Pytorch API的key +3. 将该Pytorch API配置为`ChangePrefixMatcher`,其他字段全部清理掉,例如: ```python - # Edit by AI Agent (放到该行下面) - "torch.complex", - "torch.atan", - "torch.asinh", + "torch.cdist": { + "Matcher": "ChangePrefixMatcher" + } ``` ## Step 2: 补充测试用例 diff --git a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_and_.md b/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_and_.md deleted file mode 100644 index a6a9647868d..00000000000 --- a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_and_.md +++ /dev/null @@ -1,18 +0,0 @@ -## [ 仅参数名不一致 ]torch.Tensor.bitwise_and_ -### [torch.Tensor.bitwise_and_](https://docs.pytorch.org/docs/stable/generated/torch.bitwise_and.html) -```python -torch.Tensor.bitwise_and_(other) -``` - -### [paddle.Tensor.bitwise_and_](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/Tensor__upper_cn.html#bitwise-and-y-out-none-name-none) -```python -paddle.Tensor.bitwise_and_(y, name=None) -``` - -两者功能一致,输入参数用法不一致,具体如下: - -### 参数映射 - -| PyTorch | PaddlePaddle | 备注 | -| ------------- | ------------ | ------------------------------------------------------ | -| other | y | 按位与的另一个输入。| diff --git a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_or.md b/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_or.md deleted file mode 100644 index ec168b499f8..00000000000 --- a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_or.md +++ /dev/null @@ -1,18 +0,0 @@ -## [ 仅参数名不一致 ]torch.Tensor.bitwise_or -### [torch.Tensor.bitwise_or](https://pytorch.org/docs/stable/generated/torch.Tensor.bitwise_or.html) -```python -torch.Tensor.bitwise_or(other) -``` - -### [paddle.Tensor.bitwise_or]() -```python -paddle.Tensor.bitwise_or(y) -``` - -两者功能一致且参数用法一致,仅参数名不一致,具体如下: - -### 参数映射 - -| PyTorch | PaddlePaddle | 备注 | -| ------------- | ------------ | ------------------------------------------------------ | -| other | y | 表示输入的 Tensor ,仅参数名不一致。 | diff --git a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_xor_.md b/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_xor_.md deleted file mode 100644 index 308a91aaec4..00000000000 --- a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.Tensor.bitwise_xor_.md +++ /dev/null @@ -1,18 +0,0 @@ -## [ 仅参数名不一致 ]torch.Tensor.bitwise_xor_ -### [torch.Tensor.bitwise_xor_](https://docs.pytorch.org/docs/stable/generated/torch.bitwise_xor.html) -```python -torch.Tensor.bitwise_xor_(other) -``` - -### [paddle.Tensor.bitwise_xor_](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/Tensor__upper_cn.html#bitwise-xor-y-out-none-name-none) -```python -paddle.Tensor.bitwise_xor_(y, name=None) -``` - -两者功能一致,输入参数用法不一致,具体如下: - -### 参数映射 - -| PyTorch | PaddlePaddle | 备注 | -| ------------- | ------------ | ------------------------------------------------------ | -| other | y | 按位 y 异或的另一个输入。| diff --git a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.bitwise_or.md b/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.bitwise_or.md deleted file mode 100644 index 82f081e5575..00000000000 --- a/docs/guides/model_convert/convert_from_pytorch/api_difference/args_name_diff/torch.bitwise_or.md +++ /dev/null @@ -1,25 +0,0 @@ -## [ 仅参数名不一致 ]torch.bitwise_or -### [torch.bitwise_or](https://pytorch.org/docs/stable/generated/torch.bitwise_or.html#torch-bitwise-or) -```python -torch.bitwise_or(input, - other, - *, - out=None) -``` - -### [paddle.bitwise_or](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/bitwise_or_cn.html#bitwise-or) -```python -paddle.bitwise_or(x, - y, - out=None, - name=None) -``` - -两者功能一致且参数用法一致,仅参数名不一致,具体如下: -### 参数映射 - -| PyTorch | PaddlePaddle | 备注 | -| ------------- | ------------ | ------------------------------------------------------ | -| input | x | 表示输入的 Tensor ,仅参数名不一致。 | -| other | y | 表示输入的 Tensor ,仅参数名不一致。 | -| out | out | 表示输出的 Tensor。|