siyiweigeHEW opened a new issue, #20150:
URL: https://github.com/apache/tvm/issues/20150

   
   ### Expected behavior
   
   A valid ONNX `Pad` model using the **opset-18+ features** — `mode="wrap"` 
(circular padding) and/or the optional `axes` input (pad only the specified 
dimensions) — should be imported successfully by 
`tvm.relax.frontend.onnx.from_onnx`. Both were added to the ONNX Pad spec in 
opset 18 (see [ONNX Pad 
spec](https://github.com/onnx/onnx/blob/main/docs/Operators.md#Pad)), are 
accepted by `onnx.checker`, and run correctly in onnxruntime and onnx.reference.
   
   ### Actual behavior
   
   `from_onnx` rejects both features:
   
   1. **`mode="wrap"`** raises `OpAttributeInvalid`:
      ```
      OpAttributeInvalid: Value wrap in attribute "mode" is invalid for 
operator Pad.
      ```
      The frontend's mode whitelist only accepts `["constant", "edge", 
"reflect"]` (raised at `python/tvm/relax/frontend/onnx/onnx_frontend.py:1919` 
in `Pad._impl_v2` and `:1949` in `Pad._impl_v11`, class at line 1899), so a 
legal circular-padding model is misdiagnosed as invalid.
   
   2. **`axes` input** is silently ignored — the pads are applied to the 
full-rank shape instead of the specified axes, so a model such as `X: (3, 4)`, 
`pads: [1, 2]`, `axes: [1]` (which pads only dim 1, output `(3, 7)`) fails with 
`ValueError: Input dimension and pad_before dismatch : 2 vs 1` because the 
frontend tries to apply the 1-axis pads to dim 0.
   
   Both failures are **frontend coverage gaps, not invalid models**: 
`onnx.checker` and `onnx.reference` accept them, onnxruntime runs them 
correctly, and `topi.nn.circular_pad` already implements circular padding — the 
frontend simply never dispatches to it.
   
   ### Environment
   
   - OS: Linux
   - TVM: v0.24.dev0 (main branch, commit `262c6d2e0`, built 2026-02-11)
   - Python: 3.11
   - onnx: 1.20.1
   - onnxruntime: 1.24.1
   
   ### Steps to reproduce
   
   ```python
   """Repro: valid ONNX Pad (opset 18) with mode="wrap" and/or the axes input is
   rejected by the TVM relax ONNX frontend, while onnxruntime runs it 
correctly."""
   import numpy as np
   import onnx, onnxruntime
   from onnx import helper, TensorProto
   from tvm.relax.frontend.onnx import from_onnx
   
   def make_v18_model(x_shape, pads, mode, axes=None):
       X = helper.make_tensor_value_info("X", TensorProto.FLOAT, list(x_shape))
       Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, None)
       inits = [helper.make_tensor("pads", TensorProto.INT64, [len(pads)], 
pads)]
       node_in = ["X", "pads"]
       if axes is not None:  # axes is positional input #3; provide 
constant_value too
           inits.append(helper.make_tensor("value", TensorProto.FLOAT, [], 
[0.0]))
           inits.append(helper.make_tensor("axes", TensorProto.INT64, 
[len(axes)], axes))
           node_in += ["value", "axes"]
       g = helper.make_graph([helper.make_node("Pad", node_in, ["Y"], 
mode=mode)], "g", [X], [Y], inits)
       m = helper.make_model(g, opset_imports=[helper.make_opsetid("", 18)])
       m.ir_version = 8
       return m
   
   x = np.arange(12, dtype="float32").reshape(3, 4)
   
   # (1) mode="wrap" (opset 18+ circular padding)
   m1 = make_v18_model((3, 4), [1, 1, 2, 2], "wrap")
   onnx.checker.check_model(m1)                                        # valid
   r = onnxruntime.InferenceSession(m1.SerializeToString()).run(None, {"X": 
x})[0]
   print("onnxruntime wrap ->", r.shape)                               # (6, 7)
   from_onnx(m1, shape_dict={"X": [3, 4]})                             # 
rejected
   
   # (2) axes input: pads only dim 1 of X(3,4)
   m2 = make_v18_model((3, 4), [1, 2], "constant", axes=[1])
   r = onnxruntime.InferenceSession(m2.SerializeToString()).run(None, {"X": 
x})[0]
   print("onnxruntime constant+axes ->", r.shape)                      # (3, 7)
   from_onnx(m2, shape_dict={"X": [3, 4]})                             # 
rejected
   ```
   
   Actual output:
   
   ```
   onnxruntime wrap -> (6, 7)
   Traceback (most recent call last):
     ...
     File "tvm/relax/frontend/onnx/onnx_frontend.py", line 1949, in _impl_v11
       raise tvm.error.OpAttributeInvalid(
   tvm.error.OpAttributeInvalid: Value wrap in attribute "mode" is invalid for 
operator Pad.
   onnxruntime constant+axes -> (3, 7)
   Traceback (most recent call last):
     ...
     File "tvm/relax/frontend/onnx/onnx_frontend.py", line 1954, in _impl_v11
       return bb.emit_te(topi.nn.pad, inputs[0], pad_before, pad_after, 
constant_value)
     File "tvm/topi/nn/pad.py", line 88, in pad
       raise ValueError(f"Input dimension and pad_before dismatch : {n} vs 
{len(pad_before)}")
   ValueError: Input dimension and pad_before dismatch : 2 vs 1
   ```
   
   ### Additional context
   
   - The ONNX Pad spec (opset 18+) defines `mode` ∈ {`constant`, `reflect`, 
`edge`, **`wrap`**}, where `wrap` pads by wrapping around (circular), and adds 
an optional `axes` input restricting which dimensions are padded. Opset ≤ 17 
has neither.
   - `topi.nn.circular_pad` already implements circular padding in TVM's core, 
so the capability exists — this is purely a frontend dispatch gap (mirroring 
the earlier `ConvTranspose3d` case where `topi.nn.conv3d_transpose` existed but 
`from_onnx` raised `NotImplementedError`).
   - A broader differential survey (3 input shapes × all modes × 
positive/negative pads, plus `axes` cases): of **81** legal Pad models accepted 
by onnxruntime / `onnx.checker`, TVM rejects **22** — `mode="wrap"` (19) and 
`constant` + `axes` (3). The 59 accepted models match onnxruntime exactly 
(`max|diff| = 0`), including **negative pads** (crop), so the gap is purely 
import coverage. Dynamic `pads` (graph input) are also rejected (`"Dynamic pads 
are not supported yet."`), an unrelated known limitation.
   - A fix was upstreamed in apache/tvm [#19827 "[Relax][Frontend][ONNX] Add 
support for Pad mode=wrap for opset 
19"](https://github.com/apache/tvm/pull/19827) (merged 2026-07-09): it 
dispatches `mode="wrap"` to `topi.nn.circular_pad` and expands the `axes` input 
into full-rank pads. Its motivating case is exactly the `OpAttributeInvalid: 
Value wrap ... is invalid` failure described here — confirming the bug is real. 
This report covers the unfixed behavior in the v0.24.dev0 build at commit 
`262c6d2e0`, which predates that fix.
   
   ### Triage
   
   * needs-triage
   * bug
   * relax
   * frontend/onnx
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to