concat: treat fully-unconstrained dynamic dim as a wildcard#4924
concat: treat fully-unconstrained dynamic dim as a wildcard#4924chun-wan wants to merge 1 commit 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.63% 92.72% +0.10%
===========================================
Files 587 592 +5
Lines 30469 31261 +792
===========================================
+ Hits 28222 28986 +764
- Misses 2247 2275 +28
🚀 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. |
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.
| // 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
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