`slanet-plus.onnx` has ONNX-prohibited nested-scope name collisions and fails to import in MIGraphX
Summary
The published table-recognition model slanet-plus.onnx has 16 nested-scope name collisions: formal inputs of its Loop body subgraph reuse names that are already visible from the parent graph at that point. The currently published ONNX IR prohibits this, so the artifact is rejected during ONNX import by AMD MIGraphX (reproduced on the released 2.15.0 and on a develop source build, commit 49f842b). In the tested versions, onnx.checker.check_model() passes and ONNX Runtime (CPU) executes the model, so those code paths do not reject the artifact for these collisions. This is a published-artifact portability / ONNX-conformance issue; the model remains usable in runtimes that currently accept such name collisions, and becomes visible in runtimes that validate this IR rule during import.
Affected artifact
- Official model repository: Hugging Face
opendatalab/PDF-Extract-Kit-1.0(linked as the model source by the README of GitHubopendatalab/PDF-Extract-Kit). The.onnxis not claimed to be stored in the GitHub code repository. - Path:
models/TabRec/SlanetPlus/slanet-plus.onnx - Repository revision tested:
ed6b654c018d742e65a17671e379c5e6ecc87ec9(the current repository HEAD at the time of testing; the file's introducing commit was not separately determined) - SHA-256:
d57a942af6a2f57d6a4a0372573c696a2379bf5857c45e2ac69993f3b334514b - Size: 7,758,305 bytes
- ONNX: opset
ai.onnx:14, IR version 7; producerPaddlePaddle(producer_versionis empty in the file, so a specific exporter version is not identifiable)
Minimal reproduction
# 1. Obtain the artifact from the official HF model repo, pinned to the revision above
curl -L -o slanet-plus.onnx \
"https://huggingface.co/opendatalab/PDF-Extract-Kit-1.0/resolve/ed6b654c018d742e65a17671e379c5e6ecc87ec9/models/TabRec/SlanetPlus/slanet-plus.onnx"
sha256sum slanet-plus.onnx # d57a942af6a2f57d6a4a0372573c696a2379bf5857c45e2ac69993f3b334514b
# 2. Minimal, model-specific check for the known Loop-body-input collisions.
python3 - <<'PY'
import onnx
m = onnx.load("slanet-plus.onnx"); g = m.graph
# This intentionally checks only Loop body formal inputs against names visible in the
# parent graph before each Loop. A recursive audit (offered below) additionally covers
# body initializers, sparse initializers, node outputs, and nested graph attributes.
visible = set(i.name for i in g.input) | set(t.name for t in g.initializer)
hits_total = 0
for idx, node in enumerate(g.node):
if node.op_type == "Loop":
body = next(a.g for a in node.attribute if a.name == "body")
hits = [vi.name for vi in body.input if vi.name in visible]
print(f"Loop at node #{idx}: {len(hits)} body inputs collide with visible parent names")
hits_total += len(hits)
for o in node.output:
visible.add(o)
print("total Loop body-input collisions:", hits_total)
PY
# -> total Loop body-input collisions: 16
# 3. (Optional) confirm a runtime that validates the rule rejects the import
migraphx-driver read slanet-plus.onnx --onnx
# -> ... parse_inputs: module "Loop_..." has parameter name "gru_cell_0.w_1"
# existing in parent graph! (non-zero exit)Observed and expected behavior
- Nested-scope check (minimal, model-specific): 16
Loopbody formal inputs collide with parent-graphConstantoutputs defined before theLoop. onnx.load: OK.onnx.checker.check_model: PASS on onnx 1.19.1 and 1.22.0.- ONNX Runtime 1.27.0 (CPU): executes the model.
- AMD MIGraphX
read … --onnx: rejected at import on both tested builds:- released 2.15.0 —
onnx_parser.cpp:366 parse_inputs: … "gru_cell_0.w_1" existing in parent graph! developsource build (commit49f842b, reported 2.17.0) —onnx_parser.cpp:409 parse_inputs: … "gru_cell_0.w_1" existing in parent graph!
- released 2.15.0 —
Expected: nested-subgraph local definitions whose names are distinct from any name visible from an enclosing scope, so the artifact imports consistently across runtimes that follow the published ONNX IR rule.
ONNX specification / root cause
Nested subgraphs are lexically scoped: a subgraph may capture outer-scope values, but its
own inputs, initializers, and node outputs must not reuse a name visible from an outer
scope. The published ONNX IR states (onnx/onnx docs/IR.md, pinned to tag v1.22.0):
"… a node output name and names of inputs and initializers of the subgraph MUST be distinct from the names from the outer scopes that are visible in the nested subgraph. That is, variable shadowing is not allowed."
History (verified): the question of whether this covers subgraph inputs/initializers was raised in onnx issue #2303 and resolved by PR #6955 ("Clarify that variable shadowing is not allowed"); that wording is present from onnx v1.19.0 through the current v1.22.0. A later proposal to allow variable shadowing, RFC #7012, was closed as not planned, so the prohibition remains in the published specification. (onnx.checker, in the versions tested, does not reject this artifact for these collisions; that is not a statement about all ONNX versions or a judgment of the checker.)
The collisions most likely originate in the Paddle-originated export pipeline; the exact exporter and version are not identifiable from the artifact (producer_version is empty).
Validated candidate fix
A conservative, scope-safe local alpha-renaming resolves the conformance defect on this artifact (audit and rewrite scripts were used locally; I can provide them in a follow-up or a small reproduction repository if useful — they are not attached here). The rewrite:
- renames only the 16 colliding nested-subgraph-local definitions (and the references that resolve to them by lexical scoping; a subgraph
outputname is changed only when it equals the renamed value); - does not touch parent-graph names, initializer tensor contents, node order, opset, IR version, or the
Loopnode's positional actual inputs.
Verification:
- After rewriting: nested-scope audit reports 0 collisions;
onnx.checkerstill passes (onnx 1.19.1 and 1.22.0); structural diff confirms only the intended name changes (opset, IR, top-level I/O names, per-graph node sequence, and initializer bytes all unchanged). - For one tested input (
x, shape[1,3,488,488], float32, generated bynp.random.default_rng(0).standard_normal((1,3,488,488)).astype(np.float32), NumPy 1.26.4; input-array SHA-2561b577ec5b09f9626d594d2b756479b64e175c15c29f36df9bf2b0d7c9cc5b6b5), the original and rewritten models produce exactly equal outputs (np.array_equal == True;max_abs_diff == 0.0; no NaN/Inf) for both outputs. This is a single test case, not a proof of equivalence for all inputs. - On MIGraphX, the rewritten artifact is accepted past the nested-scope naming check on both 2.15.0 and the
developbuild. It then reaches a separate MIGraphX limitation triggered by the model's data-dependentRangeoperation (PARSE_RANGEon 2.15.0; on thedevelopbuildRangeitself is handled but a downstream op on the resulting data-dependent dynamic shape fails). That limitation is independent of the nested-scope naming defect reported here.
A verified re-export that produces unique names across visible nested scopes is the preferred long-term fix.
Requested action
Could the affected artifact be replaced with either a verified re-export or a semantics-preserving alpha-renamed version? For a replacement it would be helpful to:
- keep the official distribution locations synchronized (Hugging Face and the ModelScope mirror referenced in the README);
- publish the new artifact checksum;
- record the exporter version, if available.
Suggested acceptance criteria: nested-scope audit clean; onnx.checker still passes; outputs on a fixed test input match the current artifact; MIGraphX no longer fails at the nested-scope naming check on this artifact; new checksum traceable. There is no expectation
that the maintainers adopt any specific binary I generated.
Environment
- OS: Linux x86_64 (Ubuntu, kernel 6.8.0-79-generic); GPU: AMD gfx1100; ROCm 7.2.1.
- MIGraphX: released 2.15.0; and a
developsource build (commit49f842b,git describe --always=49f842b, reported version 2.17.0). - Python 3.12.3; onnx 1.19.1 and 1.22.0; onnxruntime 1.27.0 (CPU); NumPy 1.26.4.
Additional evidence
Full list of 16 Loop-body-input collisions (audit output)model: slanet-plus.onnx sha256: d57a942a… opset ai.onnx:14 ir 7
subgraph: model.graph > Loop.body — all 16 are body formal inputs; each collides with a
parent-graph Constant node-output defined before the Loop (Constant@373–388):
gru_cell_0.w_1, linear_2.w_0, gru_cell_0.w_0, linear_6.b_0, linear_4.w_0, linear_5.w_0,
linear_3.w_0, linear_5.b_0, linear_0.w_0, linear_6.w_0, linear_1.b_0, gru_cell_0.b_0,
gru_cell_0.b_1, linear_4.b_0, linear_3.b_0, linear_1.w_0# released 2.15.0
migraphx-driver read slanet-plus.onnx --onnx
-> onnx_parser.cpp:366 parse_inputs: ... "gru_cell_0.w_1" existing in parent graph! (exit 134)
migraphx-driver read slanet-plus.rewritten.onnx --onnx
-> accepted past the nested-scope naming check; then:
checks.cpp:35 check_arg_empty: PARSE_RANGE: start arg dynamic shape is not supported (exit 134)
# develop source build (commit 49f842b, reported 2.17.0)
migraphx-driver read slanet-plus.onnx --onnx
-> onnx_parser.cpp:409 parse_inputs: ... "gru_cell_0.w_1" existing in parent graph! (exit 134)
migraphx-driver read slanet-plus.rewritten.onnx --onnx
-> accepted past the nested-scope naming check; then:
checks.cpp:35 check_arg_empty: PARSE_TILE: dynamic shape is not supported (exit 134)input: x, shape [1,3,488,488], float32
expr: np.random.default_rng(0).standard_normal((1,3,488,488)).astype(np.float32)
numpy 1.26.4, seed 0
input-array SHA-256: 1b577ec5b09f9626d594d2b756479b64e175c15c29f36df9bf2b0d7c9cc5b6b5
test cases: 1
output[0] save_infer_model/scale_0.tmp_0 [1,18,8] float32 np.array_equal=True max_abs_diff=0.0 NaN=no Inf=no
output[1] save_infer_model/scale_1.tmp_0 [1,18,50] float32 np.array_equal=True max_abs_diff=0.0 NaN=no Inf=no
=> exactly equal for this tested input (single case; not a general equivalence proof)Source: opendatalab/PDF-Extract-Kit