concat: treat fully-unconstrained dynamic dim as a wildcard#4924
concat: treat fully-unconstrained dynamic dim as a wildcard#4924chun-wan wants to merge 5 commits into
Conversation
acd0d1e to
2e90081
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #4924 +/- ##
===========================================
+ Coverage 92.73% 92.90% +0.17%
===========================================
Files 596 603 +7
Lines 31813 32533 +720
===========================================
+ Hits 29500 30223 +723
+ Misses 2313 2310 -3
🚀 New features to boost your workflow:
|
2e90081 to
4d15ef9
Compare
4d15ef9 to
dce30ef
Compare
|
can you add a onnx parser test where the issue you are describing occurs? This will help understand the parsing issue better and also properly test the root cause of the issue. Ideally this would be a small snippet from a failing model |
641dc9a to
afd93a9
Compare
Regressions detected 🔴 * No develop baseline was found for this PR's branch point; compared against the latest available develop run instead. |
|
| // to_dynamic reconciles a transient static+dynamic mix by promoting | ||
| // every input to a dynamic shape instead of rejecting it. Such a mix | ||
| // arises in the dynamic pipeline -- e.g. split_single_dyn_dim | ||
| // specialises one concat input to a static shape while a | ||
| // broadcast_with_dims / ONNX Expand output is still dynamic; once the | ||
| // program is fully specialised the inputs become all-static again. |
There was a problem hiding this comment.
| // to_dynamic reconciles a transient static+dynamic mix by promoting | |
| // every input to a dynamic shape instead of rejecting it. Such a mix | |
| // arises in the dynamic pipeline -- e.g. split_single_dyn_dim | |
| // specialises one concat input to a static shape while a | |
| // broadcast_with_dims / ONNX Expand output is still dynamic; once the | |
| // program is fully specialised the inputs become all-static again. | |
| // convert all shapes to dynamic to handle mixed static-dynamic case |
| // A fully-unconstrained dim (unbounded max == SIZE_MAX) is a wildcard: | ||
| // it is the output of an op whose shape is only known at runtime (e.g. | ||
| // broadcast_with_dims / ONNX Expand whose target shape is computed from | ||
| // another tensor's shape). On a non-concat axis it matches any dim and | ||
| // adopts that dim's constraint in the output; two genuinely-different | ||
| // concrete dims still mismatch. Only broadcast_with_dims / ONNX Expand | ||
| // emits SIZE_MAX -- every other dynamic dim has a finite max -- so the | ||
| // (0 or 1) lower bound is irrelevant and we match on the max alone. |
There was a problem hiding this comment.
| // A fully-unconstrained dim (unbounded max == SIZE_MAX) is a wildcard: | |
| // it is the output of an op whose shape is only known at runtime (e.g. | |
| // broadcast_with_dims / ONNX Expand whose target shape is computed from | |
| // another tensor's shape). On a non-concat axis it matches any dim and | |
| // adopts that dim's constraint in the output; two genuinely-different | |
| // concrete dims still mismatch. Only broadcast_with_dims / ONNX Expand | |
| // emits SIZE_MAX -- every other dynamic dim has a finite max -- so the | |
| // (0 or 1) lower bound is irrelevant and we match on the max alone. | |
| // A fully-unconstrained dim (unbounded max == SIZE_MAX) is a wildcard: | |
| // it is the output of an op whose shape is only known at runtime (e.g. | |
| // broadcast_with_dims / ONNX Expand whose target shape is computed from | |
| // another tensor's shape). |
| // concat axis 1: non-axis (0) wildcard adopts {1,1000}. On the concat axis | ||
| // the unconstrained {0,SIZE_MAX} summed with the static 64 saturates to | ||
| // {64, SIZE_MAX} (operator+ clamps the SIZE_MAX overflow). |
There was a problem hiding this comment.
| // concat axis 1: non-axis (0) wildcard adopts {1,1000}. On the concat axis | |
| // the unconstrained {0,SIZE_MAX} summed with the static 64 saturates to | |
| // {64, SIZE_MAX} (operator+ clamps the SIZE_MAX overflow). |
| // concat axis 0: non-axis (1) wildcard adopts {64,64}; the concat axis sums | ||
| // {0,SIZE_MAX} + {1,1000} -> {1, SIZE_MAX}. |
There was a problem hiding this comment.
| // concat axis 0: non-axis (1) wildcard adopts {64,64}; the concat axis sums | |
| // {0,SIZE_MAX} + {1,1000} -> {1, SIZE_MAX}. |
| // A fully-unconstrained dynamic dim {0, SIZE_MAX} is what | ||
| // broadcast_with_dims / ONNX Expand emits when its target shape is only | ||
| // known at runtime. It must act as a wildcard on the non-concat axes | ||
| // (adopting the other input's constraint) instead of forcing a throw. |
There was a problem hiding this comment.
| // A fully-unconstrained dynamic dim {0, SIZE_MAX} is what | |
| // broadcast_with_dims / ONNX Expand emits when its target shape is only | |
| // known at runtime. It must act as a wildcard on the non-concat axes | |
| // (adopting the other input's constraint) instead of forcing a throw. |
| // A static input concatenated with a fully-unconstrained dynamic input | ||
| // (e.g. a broadcast_with_dims / Expand output) is reconciled by promoting | ||
| // the static input to fixed dynamic dims (shape::to_dynamic), instead of | ||
| // failing with "Cannot mix static and dynamic input shapes". This mix | ||
| // arises transiently in the dynamic pipeline when split_single_dyn_dim | ||
| // specialises one concat input to a static shape. |
There was a problem hiding this comment.
| // A static input concatenated with a fully-unconstrained dynamic input | |
| // (e.g. a broadcast_with_dims / Expand output) is reconciled by promoting | |
| // the static input to fixed dynamic dims (shape::to_dynamic), instead of | |
| // failing with "Cannot mix static and dynamic input shapes". This mix | |
| // arises transiently in the dynamic pipeline when split_single_dyn_dim | |
| // specialises one concat input to a static shape. |
| // A static input mixed with a concrete (non-wildcard) dynamic dim that | ||
| // does not match on a non-concat axis still throws. |
There was a problem hiding this comment.
| // A static input mixed with a concrete (non-wildcard) dynamic dim that | |
| // does not match on a non-concat axis still throws. |
| // axis 1: non-axis (0) wildcard adopts the static 1024; the concat axis | ||
| // sums {64,64} + {0,SIZE_MAX} -> {64, SIZE_MAX}. |
There was a problem hiding this comment.
| // axis 1: non-axis (0) wildcard adopts the static 1024; the concat axis | |
| // sums {64,64} + {0,SIZE_MAX} -> {64, SIZE_MAX}. |
| // The wildcard is identified by an unbounded max, so a {1, SIZE_MAX} | ||
| // lower-bound-1 form (as broadcast_with_dims may emit) is also a wildcard. |
There was a problem hiding this comment.
| // The wildcard is identified by an unbounded max, so a {1, SIZE_MAX} | |
| // lower-bound-1 form (as broadcast_with_dims may emit) is also a wildcard. |
afd93a9 to
9362cd4
Compare
@chun-wan is it possible to add this please? Also, it sounds like in your model, that expand is batch-dependent which is why it resolves to something static via the split_single_dyn_dim / simplify_dyn_ops passes. I think with the new symbolic shape feature, we can write the expand parser much better so that it doesnt use such wildcards in the first place when possible. In any case, we can go with this change to get your use case working, but can you just add the requested parser test. This will help implement a proper parser fix using symbolic shapes in the future. |
9362cd4 to
e18b550
Compare
broadcast_with_dims (ONNX Expand) whose target shape is only known at
runtime emits a fully-unconstrained dynamic dim {0, SIZE_MAX} on every
axis. Concatenating such a tensor with one carrying a real dynamic range
(e.g. a {1, 1000} batch dim) crashed compile in two places:
1. Parse time: concat's all-dynamic branch required non-concat axes to
match exactly, so {0, SIZE_MAX} != {1, 1000} threw
"CONCAT: all input dimensions should match in axis N".
2. Pipeline time: split_single_dyn_dim specialises one concat input to a
static shape while the Expand output is still dynamic, so concat then
saw a static/dynamic mix and threw "Cannot mix static and dynamic
input shapes" -- before simplify_dyn_ops could constant-fold the
Expand into a static multibroadcast.
Fix: in concat's dynamic path, treat {0, SIZE_MAX} as a wildcard that
matches any dim on a non-concat axis (adopting that dim's constraint);
two genuinely-different concrete dynamic dims still throw. On the concat
axis, an unconstrained input yields an unconstrained sum rather than
overflowing SIZE_MAX. Static/dynamic mixes are reconciled by promoting
the static inputs to fixed dynamic dims and running the same logic,
instead of hard-failing. After the program is fully specialised the
exact shape is recovered.
Adds op_shape regression tests test_dyn_concat_unconstrained and
test_dyn_concat_mixed_static_dynamic. Verified end-to-end: an
Expand-on-dynamic + concat model compiles on both the ref and GPU
pipelines and evaluates correctly at batch 1/64/1024; full
test_op_shape_test passes.
e18b550 to
e6e23d6
Compare
…concat)
dynamic_dimension::intersection() drops optimals and accepts merely
overlapping ranges, so using it for the non-concat axes made test_dyn_concat
fail: the axis-0 optimals were lost ({1,3,{3}} vs {1,3}) and the
non-matching-dimension cases stopped throwing ({1,5} vs {1,4} merged to
{1,4} instead of erroring). Revert to the explicit unbounded-max wildcard
check, which preserves optimals via assignment and requires exact equality
otherwise.
|
@shivadbhavsar Thanks for the review — I've addressed the change request:
Code checks are green; only the infra |
| // A fully-unconstrained dim (unbounded max == SIZE_MAX) is a wildcard: | ||
| // it is the output of an op whose shape is only known at runtime (e.g. | ||
| // broadcast_with_dims / ONNX Expand whose target shape is computed from | ||
| // another tensor's shape). It adopts the other input's constraint; | ||
| // otherwise non-concat axes must match exactly. (dynamic_dimension | ||
| // intersection is intentionally not used here: it drops optimals and | ||
| // accepts merely-overlapping ranges, which breaks concat's exact-match | ||
| // requirement -- see test_dyn_concat.) |
There was a problem hiding this comment.
please simplify, no tombstone comments
| // Regression for #4924. ONNX Expand(qu, dims) with a runtime `dims` lowers | ||
| // to broadcast_with_dims, whose output carries fully-unconstrained dynamic | ||
| // dims. Concatenating that with the dynamic `item` previously threw | ||
| // "CONCAT: all input dimensions should match in axis 0" while parsing | ||
| // (add_instruction runs concat shape inference). #4924 treats the | ||
| // unconstrained dim as a wildcard, so the model now parses. |
There was a problem hiding this comment.
| // Regression for #4924. ONNX Expand(qu, dims) with a runtime `dims` lowers | |
| // to broadcast_with_dims, whose output carries fully-unconstrained dynamic | |
| // dims. Concatenating that with the dynamic `item` previously threw | |
| // "CONCAT: all input dimensions should match in axis 0" while parsing | |
| // (add_instruction runs concat shape inference). #4924 treats the | |
| // unconstrained dim as a wildcard, so the model now parses. |
please remove tombstone comments
shivadbhavsar
left a comment
There was a problem hiding this comment.
Thanks for adding the regression test, LGTM.
Please try and address Khaliques comments, there is a migraphx-simplify skill thats available in https://github.com/ROCm/AMDMIGraphX/tree/develop/.claude/skills/migraphx-simplify that might help take care of it.
|
|
||
| TEST_CASE(concat_dyn_expand_wildcard_test) | ||
| { | ||
| // Regression for #4924: concat with a fully-unconstrained (wildcard) dynamic dim parses. |
There was a problem hiding this comment.
| // Regression for #4924: concat with a fully-unconstrained (wildcard) dynamic dim parses. |
broadcast_with_dims (ONNX Expand) whose target shape is only known at runtime emits a fully-unconstrained dynamic dim {0, SIZE_MAX} on every axis. When such a tensor was concatenated with a tensor carrying a real dynamic range (e.g. a {1, 1000} batch dim), concat::normalize_compute_shape threw "CONCAT: all input dimensions should match in axis N" at parse time -- before split_single_dyn_dim / simplify_dyn_ops had a chance to constant-fold the Expand into a static multibroadcast -- crashing compile on otherwise-valid dynamic-shape models.
Fix: in the all-dynamic branch of concat, treat {0, SIZE_MAX} as a wildcard. On non-concat axes it matches any dim and adopts that dim's constraint; two genuinely-different concrete dynamic dims still throw. On the concat axis, if any input is unconstrained the summed dim is emitted as a wildcard rather than overflowing SIZE_MAX. After the program is specialised the Expand folds to a static multibroadcast and the exact shape is recovered.
Adds op_shape regression test test_dyn_concat_unconstrained. Reproduced the original crash and verified the fix end-to-end (Expand-on-dynamic + concat ONNX model compiles for the ref target); full test_op_shape_test (512 cases) passes.
Motivation
Technical Details
Changelog Category
Add a
CHANGELOG.mdentry for any option other thanNot Applicable