Closed
[L3] math/div_no_nan 造数脚本 golden.py 三个分支的 or 两侧都是 x1.dtype,x2 的 dtype 从未被判断(L41/L46/L51) #3435
eieieiAdd on  Oct 8Closed at  23 days ago
eieiei
eieiei
Oct 8 Create

现象

math/div_no_nan/tests/ut/op_host/assets/golden.py 里三个分支的判断条件,or 两侧写的是同一个表达式:

# L41 / L46 / L51
if   x1.dtype == np.int8   or x1.dtype == np.int8:      # uint8 分支 cast 回 tf.int8
    ...  out = tf.cast(out, tf.int8)
elif x1.dtype == np.uint8  or x1.dtype == np.uint8:
    ...  out = tf.cast(out, tf.uint8)
elif x1.dtype == np.int32  or x1.dtype == np.int32:
    ...  out = tf.cast(out, tf.int32)
else:
    out = tf.compat.v1.div_no_nan(x1_placeholder, x2_placeholder, name="divnonan")

x2.dtype 在整个函数里只出现在 L39(placeholder 的 dtype),条件里一次都没参与判断。

为什么这看起来是笔误而不是有意

分支体自己就把两个输入都算了进去:

x1_fp16 = tf.cast(x1_placeholder, tf.float16)
x2_fp16 = tf.cast(x2_placeholder, tf.float16)     # 两个输入同批处理
out = tf.compat.v1.div_no_nan(x1_fp16, x2_fp16, name="divnonan")
out = tf.cast(out, tf.int8)

而本函数 L38-L39 明确分别拿了两条输入张量的 dtype 建 placeholder。按这个结构,x1.dtype == np.int8 or x2.dtype == np.int8 才是自洽的写法;同一行把 x1.dtype == np.int8 写两遍没有额外信息。

可观察后果(我只做静态推理,没有 NPU 也跑不了 tf1.x 静态图)

  • 两条输入都是 int8 / uint8 / int32 时:行为与现在一致(第一个操作数就命中),所以现在跑的 UT 不会暴露它;
  • 只有 x2 是整型、x1 是浮点时(例如 x1=float16、x2=int8):三个分支都不命中,直接走 else,把 fp16 与 int8 两个不同 dtype 的 placeholder 一起喂给 tf.compat.v1.div_no_nan,TF 静态图会在建图阶段就报类型不匹配,golden 生成失败;
  • 算子定义侧没有把三个张量钉成同一类型:math/div_no_nan/op_host/div_no_nan_def.cpp L22-L38 对 x1 / x2 / y 各自独立列了 {DT_BF16, DT_FLOAT16, DT_FLOAT, DT_INT32, DT_UINT8, DT_INT8},全文件 grep Register | make_tuple 命中 0(也就是没有 RegisterBasicEqualType 这类交叉约束)。所以「x1 与 x2 类型不同」在接口层面是允许的输入。

想确认

  1. 这三处的第二个操作数原意是不是 x2.dtype == …?如果确认,我就提 PR 把 L41/L46/L51 改成 x1.dtype == np.int8 or x2.dtype == np.int8 这种形态(3 行改动)。
  2. 如果原意是「两侧都只看 x1,写成 or 只是冗余」,那正确的处理是把 or x1.dtype == np.int8 删掉(同样 3 行),并在算子侧补一个「x1/x2 必须同类型」的交叉约束,避免上面那条混合类型输入走到 else 崩在建图阶段。
  3. 顺带一个同类观察:math/complex_abs/tests/assets/golden.py L42 与 conversion/strided_slice/tests/assets/golden.py L112、ops-nn 的 quant/swiglu_mx_quant/tests/assets/golden.py L321 有 if/else 两分支体逐语句完全相同 的结构(我用 AST 按语句 dump 比对得到的)。这类我暂时没结论 —— 可能确实是同一处理,等这条 Issue 你们看完要不要我再单开,我先不刷条数。

(这条面我用脚本扫了 18 个仓的 .py:if/elif 链条件重复、a and a/a or a 两侧同表达式、if/else 分支体完全相同。判据先过了自测(三种缺陷各造一例、另造 3 例不应报的),命中里我只挑了这条能讲清意图与后果的。)

likedislike
陈思
陈思Collaborators
29 days ago Comments:

您好,感谢反馈!核对 cann/ops-math 提交 9e6334a5 的 golden.py 后,确认三处 or 两端重复判断 x1.dtype,可以简化。

不过,混合 dtype 并非当前算子的有效输入:除了 算子原型明确要求 x2、y 与 x1 类型一致,arch35 tiling 的 DoOpTiling()也实际比较三个 dtype,不一致便返回 GRAPH_FAILED。因此,不能仅凭 OpDef 分别列出类型集合,就推断接口允许任意混合类型。

基于这个契约,更合适的方向是删除重复的 or x1.dtype == ...,保留按 x1 类型选择分支;如需改善 golden 对非法输入的提示,可在入口增加同类型校验。直接改为判断 x2 并据此接受混合类型,会扩展 golden 的行为,且与现有算子约束不一致。您提到的其他相同分支结构仍需结合各自契约判断,不能仅凭代码相同认定功能错误。

likedislike
陈思陈思Collaborators
25 days ago Assign chensi79 as responsible person
陈思陈思Collaborators
25 days ago Assign zhang-song-rui as responsible person
zhang-song-rui
zhang-song-ruiCollaborators
23 days ago Comments:

补充本次复核与处理结果:

  • 核对当前上游 bc2f8925,三处 or 两端重复判断仍存在,确认为可清理的冗余条件;但不能据此认定合法输入计算错误。混合 dtype 不是当前有效输入:proto 明确要求同类型,arch35 DoOpTiling() 也校验 x1/x2/y,不一致直接返回 GRAPH_FAILED。因此不改为判断 x2,也不新增已有的类型约束。

  • 已基于当前上游建立 fix/div-no-nan-golden-duplicate-dtype 分支,仅删除 int8、uint8、int32 三处重复的 or x1.dtype == ...,保留原有分支和转换行为。修复提交。

  • 验证:Python 编译及 git diff --check 通过;将原代码的三处 a or a 按幂等规则简化后,整个模块 AST 与修改后一致。未进行 TensorFlow/NPU 数值运行.

likedislike
CANN-robotCANN-robotCollaborators
23 days ago Close issue
CANN-robotCANN-robotCollaborators
22 days ago Added label: resolved