Skip to content

Commit a42a5ed

Browse files
author
peng.li24
committed
docs: update AGENTS.md
1 parent 7f53fc7 commit a42a5ed

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,77 @@ OpenBLAS 而非 numpy 的 ILP64 OpenBLAS。`dlsym("dgesv_64_")` 返回 nullptr
3030
- BLAS 函数指针用 function-local static 缓存,首次调用时通过 `resolve_blas()` 解析。
3131
`resolve_blas()` 要么返回有效指针要么 throw——绝无 nullptr 返回。
3232
- 不做 fallback。`blas_bridge.h` 不存在任何 "if BLAS unavailable, do X" 的代码路径。
33+
34+
## C++ API 与 numpy 严格对齐规则
35+
36+
**核心原则:C++ API 必须与 numpy 在接口层面完全一致。禁止为绕过对齐而修改测试代码。**
37+
38+
### 1. 参数顺序必须与 numpy 一致
39+
40+
- **错误示例:** `compress(arr, mask)` — C++ 把数组放前面
41+
- **正确:** `compress(condition, a)` — 严格匹配 `numpy.compress(condition, a)`
42+
43+
### 2. 默认值语义必须与 numpy 一致
44+
45+
- **错误示例:** `eye(N, M=-1, k=0)` — 用 `-1` 作哨兵值表示"使用 N"
46+
- **正确:** `eye(N, M=py::none(), k=0)` — 匹配 `numpy.eye(N, M=None, k=0)`
47+
- **规则:** 禁止使用自定义哨兵值(如 `-1`)。如果 numpy 用 `None`,C++ 必须用 `py::none()`
48+
49+
### 3. 数学函数必须经 SVML 桥,禁止直接调用 std::
50+
51+
- **禁止:** `std::pow(base, x)` / `std::atan2(y, x)` / `std::exp(x)` — 这些与 numpy 的 SVML 实现差 1-3 ULP
52+
- **正确:** 通过 `detail::pow()` / `detail::atan2()` / `detail::exp()` 调用 — 这些在 bit_exact 模式下经 dlsym 解析 numpy 的 SVML 向量符号
53+
54+
### 4. SVML 符号必须用向量版本,不能用标量 fallback
55+
56+
- **错误:** `resolve_svml("npy_atan2")` — 解析到 libm 的标量 `atan2`,与 numpy 内部使用的 `__svml_atan28` 差 1 ULP
57+
- **正确:** `resolve_svml("__svml_atan28")` — 解析 numpy 实际使用的 Intel SVML 向量符号
58+
- **关键符号对照表:**
59+
60+
| 函数 | 错误符号(标量 libm) | 正确符号(Intel SVML) |
61+
|------|----------------------|----------------------|
62+
| pow f64 | `npy_pow` | `__svml_pow8` |
63+
| pow f32 | `npy_powf` | `__svml_powf16` |
64+
| atan2 f64 | `npy_atan2` | `__svml_atan28` |
65+
| atan2 f32 | `npy_atan2f` | `__svml_atan2f16` |
66+
67+
SVML 向量函数签名:`__m512d (*)(__m512d, __m512d)` (f64) / `__m512 (*)(__m512, __m512)` (f32)。
68+
调用方式:标量 → `_mm512_set1_pd(x)` 广播 → SVML → `_mm512_cvtsd_f64()` 提取。
69+
70+
### 5. 头文件 include 顺序
71+
72+
`numpy.h` 必须在所有子模块头文件之前加载 `svml_bridge.h`,确保 `detail::pow` 等符号在 `init.h``logspace` 等函数中可用。
73+
74+
### 6. 修复流程:诊断 → 定位根因 → 修 C++ → 严禁改测试
75+
76+
当发现 bit_exact 测试失败时:
77+
78+
1. **诊断:** `python3 -c "import numpycpp, numpy as np; ..."` 确认差异和 ULP 量级
79+
2. **定位根因:** 追溯 C++ 调用链,找出哪一步使用了非 numpy 等价的实现(`std::`、标量 `npy_*`、错误参数顺序等)
80+
3. **修 C++ 源码:** 修改头文件中的实现,使其严格匹配 numpy 的等效路径
81+
4. **严禁改测试:** 测试代码是"黄金标准"——它反映 numpy 的真实行为。修改测试来绕过 C++ 实现缺陷等同于作弊
82+
83+
## 编译模式
84+
85+
### bit_exact 模式(默认)
86+
87+
```cmake
88+
# cmake -S tests -B tests/build
89+
```
90+
91+
关键 flag:
92+
- `-O2 -ffp-contract=off` — 禁用 FMA 融合
93+
- `-fno-builtin-{exp,log,sin,cos,tan,pow,atan2,sqrt,...}` — 阻止 GCC 替换 dlsym 路径
94+
- `-msse4.1 -mfma` — SIMD 支持
95+
-`-march=native` — 避免编译器自动矢量化改变计算顺序
96+
- 通过 `dlsym` 从 numpy 的 `_multiarray_umath.so` 解析 SVML 符号
97+
98+
### std 模式
99+
100+
```cmake
101+
# cmake -S tests -B tests/build_std -DNUMPYCPP_STD_ONLY=ON
102+
```
103+
104+
- `-O3 -march=native` — 无精度约束,允许编译器自由优化
105+
-`-fno-builtin-*` — 使用 GCC 内置数学函数
106+
- 与 numpy 的 SVML 实现预期差 0-2 ULP

0 commit comments

Comments
 (0)