已关闭
test(fx): add ShapeEnv API tests (self-written + upstream patch) for v2.12.0 #36523
小辉懂编程创建于 5月22日关闭于 7月4日
test(fx): add ShapeEnv API tests (self-written + upstream patch) for v2.12.0 #36523
已关闭
小辉懂编程创建于 5月22日关闭于 7月4日
小辉懂编程
5月22日

【关联任务】

【合入来源】

请勿携带内部流程信息(需求链接、问题单、内部 issue 等)。

【背景与目标】

完善 Torch-NPU 对 PyTorch 原生 FX ShapeEnv 相关 API 的兼容性验证,覆盖以下 5 个 API 在 NPU 环境下的可用性,提升昇腾 NPU 上 PyTorch 框架的易用性与模型适配覆盖率。

API 全名 功能说明
torch.fx.experimental.symbolic_shapes.ShapeEnv.produce_guards 生成符号 guard 表达式
torch.fx.experimental.symbolic_shapes.ShapeEnv.get_nontrivial_guards 提取非平凡 guard
torch.fx.experimental.symbolic_shapes.ShapeEnv.get_pruned_guards 裁剪与给定 symbol 相关的 guard
torch.fx.experimental.symbolic_shapes.ShapeEnv.is_unbacked_symint 判断符号是否为 unbacked SymInt
torch.fx.experimental.symbolic_shapes.ShapeEnv.ignore_fresh_unbacked_symbols 上下文管理 fresh unbacked symbols 记录

【API 详细功能说明】

1. ShapeEnv.produce_guards

  • 作用:根据 ShapeEnv 累积的符号约束,为占位符生成 guard 表达式列表(编译期对输入形状的约束记录,运行期用作合法性检查)。
  • 签名:produce_guards(placeholders, sources, *, ignore_static=True, ...) -> list[str],返回 sympy 形式 guard 字符串(如 "L['a'].size()[0] >= 2")。
  • 场景:torch._dynamo 编译落盘 guard、torch.export 导出 ExportedProgram。

2. ShapeEnv.get_nontrivial_guards

  • 作用:过滤掉恒真等平凡约束,仅返回有语义的非平凡 guard。
  • 签名:get_nontrivial_guards() -> list[ShapeGuard]。
  • 场景:上游 _assert_no_guards 内部调用,断言 fx tracing 后无新约束。

3. ShapeEnv.get_pruned_guards

  • 作用:给定 sympy 符号集合,裁剪出仅依赖该集合的 guard 子集。
  • 签名:get_pruned_guards(symints: list[sympy.Symbol]) -> list[ShapeGuard]。
  • 场景:模块化导出 / 分图编译时按 symbol 范围筛选 guard。

4. ShapeEnv.is_unbacked_symint

  • 作用:判断 sympy 符号是否为 unbacked SymInt(运行期才能确定具体值的符号,如 nonzero / unique 输出尺寸)。
  • 签名:is_unbacked_symint(symbol: sympy.Symbol) -> bool。
  • 场景:图优化决策——unbacked 符号需走 data-dependent control flow 等特殊路径。

5. ShapeEnv.ignore_fresh_unbacked_symbols

  • 作用:上下文管理器。with 块内产生的 fresh unbacked symbol 不注册到全局 unbacked 列表,块结束后恢复。
  • 签名:ignore_fresh_unbacked_symbols() -> ContextManager。
  • 场景:cond / while 等高阶算子内部,避免分支符号污染主图。

【上游社区支持情况】

项目 结论
是否属于上游 PyTorch API 是,均位于 torch.fx.experimental.symbolic_shapes,为纯 Python 实现,不依赖 CUDA 专用算子
上游是否提供公开 API 是,已在 ShapeEnv 类上公开
上游是否有相关用例 部分有。produce_guards / get_nontrivial_guards 在 test/test_proxy_tensor.py::TestSymbolicTracing::test_mega_guard、test_guards_equal 中被覆盖;ignore_fresh_unbacked_symbols 在 test/functorch/test_control_flow.py::test_cond_gen_schema_symbool_inputs 中被使用;get_pruned_guards / is_unbacked_symint 在基线 test/ 中缺少直接用例
上游用例能否直接证明 NPU 支持 不能单独证明。上游现有用例运行在 CPU/CUDA 路径,未覆盖 NPU tensor 输入与 NPU 设备语义
本 PR 的补充价值 1) 对上游已覆盖的 produce_guards / get_nontrivial_guards 通过最小化 patch 引入 NPU 设备路径;2) 对上游未覆盖的 get_pruned_guards / is_unbacked_symint 新增专项自写用例;3) 对 ignore_fresh_unbacked_symbols 补齐独立行为验证

上游用例审计命令(可复现):

cd /workspace/pytorch_v2.12.0
grep -rn "produce_guards\|get_nontrivial_guards\|get_pruned_guards\|is_unbacked_symint\|ignore_fresh_unbacked_symbols" test/ --include="*.py"

v2.12.0 共命中 10 行(PyTorch 主线持续在补充 ShapeEnv 测试),主要集中在 test/test_proxy_tensor.py 与 test/functorch/test_control_flow.py。

结论:5 个 API 属于上游能力范围,但测试覆盖对 NPU 场景不完整。本 PR 通过「上游 patch + 自写测试」组合补齐 Torch-NPU 侧可验证性。

【修改方案】

1. 上游 patch:test_upstream/test/test_proxy_tensor.py.patch

针对 PyTorch v2.12.0 官方源码 test/test_proxy_tensor.py 做最小化 NPU 适配,覆盖:

上游测试方法 行号范围(原文件) 覆盖 API
TestSymbolicTracing.test_mega_guard 1820-1832 ShapeEnv.produce_guards
TestSymbolicTracing.test_guards_equal 1880-1900 ShapeEnv.produce_guards / ShapeEnv.get_nontrivial_guards(_assert_no_guards 内部使用)

适配方式(按一致性文档 六.3 原社区用例最小化原则):

  • 严格用 git diff 在 PyTorch v2.12.0 官方源码上生成(合规要求六.5)
  • 张量切到 NPU 直接使用 .npu()——原文件无获取设备命令时的最小化做法,不引入 device_type 推导
  • 在 test_guards_equal 内部以同名局部 _trace shadow 模块级 _trace,不引入新函数名,所有 _trace 调用点 0 改动
  • 不修改 patch 后手工编辑

patch 体量:仅 24 行 / 2 个 hunk

Hunk 位置 改动
#1 test_mega_guard torch.randn(16), torch.randn(8) → 各加 .npu(),单行替换
#2 test_guards_equal 测试方法开头新增 4 行 _trace 局部定义(inps 用 torch.randn(arg).npu())

patch 已在 /workspace/pytorch_v2.12.0 通过 patch --dry-run 验证,无 hunk 失败、无文件冲突。

2. 自写测试:test/fx/test_symbolic_shapes.py

新增 8 个测试方法,写法与同目录 FX 测试一致(TestCase + run_tests,默认 NPU 环境可用):

用例 覆盖 API / 场景
test_is_accessor_node_with_call_method is_accessor_node + NPU tensor example_value
test_is_accessor_node_with_call_function is_accessor_node 正负分支
test_is_concrete_int_with_literal_and_device_shape is_concrete_int / is_symbolic + NPU tensor .size()
test_is_concrete_float_with_literal_and_symbolic_value is_concrete_float / is_symbolic
test_is_concrete_bool_with_literal_and_symbolic_value is_concrete_bool / is_symbolic
test_shape_env_get_pruned_guards ShapeEnv.get_pruned_guards(无直接上游用例,补齐空白)
test_shape_env_ignore_fresh_unbacked_symbols ShapeEnv.ignore_fresh_unbacked_symbols 上下文管理
test_shape_env_is_unbacked_symint ShapeEnv.is_unbacked_symint(无直接上游用例,补齐空白)

代码规范(合规要求六.8):

  • 文件头加入英文 docstring,说明文件用途、覆盖范围
  • 移除 unittest / torch_npu 导入与 @unittest.skipUnless 装饰器
  • 使用 torch.accelerator.current_accelerator() 推导 device_type,张量统一通过 .to(device_type) 切到 NPU
  • 全部断言使用 self.assert*,无 print / try / except
  • 类前后空 2 行、方法前空 1 行,符合 PEP8

2.1 新增用例必要性与完整性分析

  • produce_guards / get_nontrivial_guards:上游 test_mega_guard / test_guards_equal 已覆盖语义,但运行在 CPU 路径;本 PR patch 把 tensor 输入切到 NPU,验证 ShapeEnv guard 生成在 NPU 张量元数据下结果一致,属于必要的设备路径补齐。
  • get_pruned_guards / is_unbacked_symint:上游基线 test/ 缺少这两个方法的直接用例。本 PR 新增专项用例验证返回类型、unbacked 标识等核心语义,为必要补齐。
  • ignore_fresh_unbacked_symbols:上游 test/functorch/test_control_flow.py 间接使用,但作为上下文管理器的独立行为验证不充分。本 PR 通过自写用例补齐独立的 enter/exit 语义验证。

结论:本 PR 的新增测试属于「对上游部分覆盖场景做 NPU 侧补齐 + 对上游缺失场景补空白」,不是重复建设。

3. 多版本分支合入

按 Ascend PyTorch 版本线分别提 MR,每个 MR 仅 1 个 squash 提交:

目标分支 MR
v2.7.1 #36524
v2.9.0 #36519
v2.10.0 #36521
v2.11.0 #36522
v2.12.0 本 MR:#36523
master #36446

【资料变更】

本 PR 仅新增测试用例,不在本 PR 内修改任何资料文档。

资料补齐的统一处理方式:

所有 5 个 ShapeEnv API 的文档支持状态由独立的文档 PR 统一处理,详见:

  • 文档 PR:#37392 docs: add ShapeEnv API support entries to torch.fx native_apis docs(提交至 v2.7.1 分支)
  • v2.12.0 文档目录的 5 个 API 此前完全缺失,由 #37392 一次性补齐

本 PR 已检查的文档状态:

  • v2.12.0 测试结论与文档支持状态保持一致
  • 文档侧无遗留待处理项

【接口变更】

本 PR 不涉及任何对外接口的变更,具体说明:

1. 不修改 torch_npu 任何公共 / 私有 API

  • 未新增、修改、删除任何 .py / .cpp / .h 源码文件
  • 未改动 torch_npu/__init__.py 等导出清单
  • 未变更任何函数签名、参数列表、返回值类型

2. 仅引入测试代码

文件 性质 内容
test/fx/test_symbolic_shapes.py 新增 8 个自写测试方法,覆盖 get_pruned_guards / is_unbacked_symint / ignore_fresh_unbacked_symbols 3 个 API
test_upstream/test/test_proxy_tensor.py.patch 新增 上游 PyTorch test/test_proxy_tensor.py 的 NPU 适配 patch,覆盖 produce_guards / get_nontrivial_guards 2 个 API

3. 不改变 PyTorch / torch_npu 现有 API 行为

  • 5 个 ShapeEnv API 均沿用 PyTorch 上游原生实现,未做任何 wrapper / monkey-patch / 行为重写
  • 验证目标仅为 "接口在 NPU 设备上是否能正常调用且结果与 CPU 一致",无功能性修改

【功能验证】

1. 测试环境

  • 设备:Ascend NPU(910B4,Health: OK)
  • CANN:8.5.0
  • 框架:Torch-NPU v2.12.0 + PyTorch v2.12.0
  • Python:3.11.13
  • 说明:测试默认 NPU 可用,不额外做 torch.npu.is_available() 跳过逻辑

2. 自写测试命令

cd /tmp && python3.11 /workspace/pytorch/test/fx/test_symbolic_shapes.py

3. 自写测试结果

测试成功 v2.12.png

4. 上游 patch 验证

cd /workspace/pytorch_v2.12.0
patch -p1 --dry-run < /workspace/pytorch/test_upstream/test/test_proxy_tensor.py.patch

输出:所有 hunk 应用成功,无文件冲突。

Patch Apply v2.12.png

5. 与上游用例的关系

验证维度 上游现有用例 本 PR 新增 / patch
produce_guards / get_nontrivial_guards 语义 test_mega_guard / test_guards_equal(CPU 路径) 通过 patch 切到 NPU 张量路径
get_pruned_guards 无直接用例 新增 test_shape_env_get_pruned_guards
is_unbacked_symint 无直接用例 新增 test_shape_env_is_unbacked_symint
ignore_fresh_unbacked_symbols test_control_flow.py 中间接使用 新增 test_shape_env_ignore_fresh_unbacked_symbols 独立验证上下文管理
NPU 侧验证闭环 不完整 本 PR 补齐

【CheckList】

likedislike
当前Pull Request已关闭, 关闭人@小辉懂编程
小辉懂编程小辉懂编程
5月22日 创建了 pull request,commit 37917088
小辉懂编程小辉懂编程
5月22日 关联了issue:[API补齐] ShapeEnv.ignore_fresh_unbacked_symbols NPU适配无需修改说明
openLiBingCI成员5月22日进行代码检视1
test_upstream/test/test_proxy_tensor.py.patch
已过期
@@ -0,0 +27,2 @@
27+ from torch._dynamo.source import LocalSource
28+ self.assertExpectedInline(
29+ str(fx_g.shape_env.produce_guards(fx_placeholder_vals(fx_g), [LocalSource("a"), LocalSource("b")], ignore_static=False)), # noqa: B950
openLiBingCI5月22日评论:

此条代码评论区间+27至+13

【openlibing.ci】识别到代码检查告警抑制注释,匹配工具:flake8,请Committer检视其合理性。

likedislike
ascend-robot
ascend-robot成员
5月22日 评论:

Thanks for your pull-request.
The full list of commands accepted by me can be found at here。
You can get sig-info at here


PR Approval Progress

⚠️ This PR does not yet meet the following requirements:lgtm (requires ≥ 2 person(s) per module)、approve (requires ≥ 1 person(s) per module)

Module Approval Details

module lgtm status approve status
test ❌ (0/2)(You can also ask: kisnwang, zichun_ye, liangchenghui, 李伟, zhenyu10) ❌ (0/1)(You can also ask: zhenyu10, chenrayray, htchu, guoqi1024, 陈豪)
test_upstream ❌ (0/2)(You can also ask: daifu1234567, TonyYA, 李伟, sunyu-xuan, zyw-hw) ❌ (0/1)(You can also ask: crazyDannyBoy, zichun_ye, zqwenn, zhenyu10, adrian07110)

💡 Tip:

  • Committer can comment /approve or /lgtm
  • Commenting /approve implies both code review (lgtm) and intent to merge (approve)

CLA Signature Pass

gehui-2026, thanks for your pull request. All authors of the commits have signed the CLA. 👍

likedislike
此处折叠了634条消息 查看更多
ascend-robotascend-robot成员
7月4日 添加了label:docs-ci-pipeline-running
小辉懂编程小辉懂编程
7月4日 关闭了 pull request
ascend-robot
ascend-robot成员
7月4日 评论:

✅ 跳过 docs ci 检查,没有需要检查的文档文件

likedislike
ascend-robotascend-robot成员
7月4日 删除了label:docs-ci-pipeline-running
ascend-robotascend-robot成员
7月4日 添加了label:docs-ci-pipeline-success