This is an automated email from the ASF dual-hosted git repository.

tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new af4c3f4d50 [Fix][Relax][ONNX] Preserve rank-expanding Expand (#19992)
af4c3f4d50 is described below

commit af4c3f4d505d27aae8cdf208e2304d67d20ac2cf
Author: Vic Wen <[email protected]>
AuthorDate: Tue Jul 14 02:42:15 2026 +0800

    [Fix][Relax][ONNX] Preserve rank-expanding Expand (#19992)
    
    ### What changed
    
    Record the original input rank before `Expand` left-pads the input shape
    for broadcast validation. The no-op fast path now returns the input
    unchanged only when both the padded shape and the original rank match
    the target.
    
    A regression test covers expanding `[1]` to `[1, 1]` when the target is
    represented as a Relax `ShapeExpr`.
    
    ### Why
    
    ONNX `Expand` right-aligns dimensions and may increase tensor rank by
    adding leading dimensions. Previously, a rank-expanding broadcast could
    look like a no-op after the frontend padded the input shape, causing it
    to return the original lower-rank tensor. Downstream operators could
    then receive inconsistent ranks.
    
    This fixes the focused bug tracked in #19991 and is part of the
    investigation and fixes for #19971. It does not close #19971 because the
    attached model exposes additional independent importer issues after this
    `Concat` failure is resolved.
    
    Fixes #19991
    Part of #19971
    
    ### Validation
    
    - `python -m pytest
    tests/python/relax/test_frontend_onnx.py::test_expand -q`
    - A/B checked the model attached to #19971: the base revision reproduces
    `Concat expects all input tensors to have same ndim`, while this change
    advances beyond that `Concat`.
    
    Signed-off-by: viiccwen <[email protected]>
---
 python/tvm/relax/frontend/onnx/onnx_frontend.py |  3 ++-
 tests/python/relax/test_frontend_onnx.py        | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py 
b/python/tvm/relax/frontend/onnx/onnx_frontend.py
index d340c696b4..70e90d3731 100644
--- a/python/tvm/relax/frontend/onnx/onnx_frontend.py
+++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py
@@ -2875,6 +2875,7 @@ class Expand(OnnxOpConverter):
         shape = inputs[1]
         if isinstance(shape, relax.ShapeExpr):
             data_shape = list(data.ty.shape)
+            data_ndim = len(data_shape)
             target_shape = list(shape.values)
             original_data_shape = [
                 dim.value if hasattr(dim, "value") else str(dim) for dim in 
data_shape
@@ -2918,7 +2919,7 @@ class Expand(OnnxOpConverter):
                                 f"the same value or one of them to be 1."
                             )
                         # For dynamic shapes, let broadcast_to handle it
-            if target_shape == data_shape:
+            if target_shape == data_shape and len(target_shape) == data_ndim:
                 return data
             return relax.op.broadcast_to(data, relax.ShapeExpr(target_shape))
 
diff --git a/tests/python/relax/test_frontend_onnx.py 
b/tests/python/relax/test_frontend_onnx.py
index 126c909983..c1bf8802f0 100644
--- a/tests/python/relax/test_frontend_onnx.py
+++ b/tests/python/relax/test_frontend_onnx.py
@@ -5601,6 +5601,19 @@ def test_expand():
                 R.output(gv)
             return gv
 
+    @I.ir_module
+    class ExpectedHigherRankSamePaddedShape:
+        @R.function
+        def main(
+            in_: R.Tensor((1,), dtype="float32"),
+            in_2: R.Tensor((1, 1), dtype="float32"),
+        ) -> R.Tensor((1, 1), dtype="float32"):
+            R.func_attr({"num_input": 2})
+            with R.dataflow():
+                gv: R.Tensor((1, 1), dtype="float32") = R.broadcast_to(in_, 
R.shape([1, 1]))
+                R.output(gv)
+            return gv
+
     _assert_expand_ir("expand_with_dim_unchanged_test", [3, 1], [3, 4], [3, 
4], ExpectedSameRank)
     _assert_expand_ir("expand_with_diff_dim", [3, 1], [1, 3, 4], [1, 3, 4], 
ExpectedHigherRank)
     _assert_expand_ir(
@@ -5609,6 +5622,12 @@ def test_expand():
     _assert_expand_dynamic_shapeexpr_ir(
         "expand_with_dynamic_dim", [1, 32, 32], ["batch", 32, 32], 
ExpectedDynamicShape
     )
+    _assert_expand_dynamic_shapeexpr_ir(
+        "expand_with_higher_rank_same_padded_shape",
+        [1],
+        [1, 1],
+        ExpectedHigherRankSamePaddedShape,
+    )
 
 
 def test_expand_incompatible_broadcasting():

Reply via email to