@@ -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