siyiweigeHEW opened a new pull request, #20152:
URL: https://github.com/apache/tvm/pull/20152

   
   Fixes: #20150
   
   ## Summary
   
   The Relax ONNX frontend rejected legal **opset-18** Pad models using
   `mode="wrap"` (circular padding) or the optional `axes` input. Both are
   ONNX Pad-18 features, are accepted by `onnx.checker` / `onnx.reference` /
   onnxruntime, and `topi.nn.circular_pad` already implements circular
   padding — this is purely a frontend dispatch gap.
   
   ## Root cause
   
   Upstream #19827 added `Pad._impl_v19` with `wrap`/`axes` support, but
   `get_converter` dispatches on the highest `_impl_v{N}` with `N <= opset`,
   so that method is only reached for models with **opset >= 19**. A model
   with **opset 18** — the version that actually introduced `wrap` and
   `axes` — still resolves to `_impl_v11`, which:
   
   1. has a whitelist `["constant", "edge", "reflect"]`, so `mode="wrap"`
      raises `OpAttributeInvalid("Value wrap ... is invalid for operator 
Pad.")`;
   2. never reads `inputs[3]` (the `axes` input), so an axes model is padded
      on the full rank instead of the specified axes and fails with
      `ValueError("Input dimension and pad_before dismatch ...")`.
   
   ## Fix
   
   Add `Pad._impl_v18`, mirroring #19827's `_impl_v19` but for opset 18:
   expand the `axes` input into full-rank pads via
   `_get_known_tensor_rank` / `_normalize_constant_axes`, extend the mode
   whitelist to include `"wrap"`, and dispatch `wrap` to
   `topi.nn.circular_pad`:
   
   ```python
   @classmethod
   def _impl_v18(cls, bb, inputs, attr, params):
       # ONNX Pad-18 introduces mode="wrap" and the optional axes input ...
       ...
       axes_input = inputs[3] if len(inputs) > 3 else None
       if axes_input is not None:
           ...
           rank = _get_known_tensor_rank(inputs[0])
           axes = _normalize_constant_axes([int(a) for a in axes], rank, "Pad")
           full_before = [0] * rank
           full_after = [0] * rank
           for i, ax in enumerate(axes):
               full_before[ax] = pad_before[i]
               full_after[ax] = pad_after[i]
           pad_before, pad_after = full_before, full_after
   
       pad_mode = attr.get("mode", b"constant").decode("utf-8")
       if pad_mode not in ["constant", "edge", "reflect", "wrap"]:
           raise tvm.error.OpAttributeInvalid(...)
       ...
       elif pad_mode == "wrap":
           return bb.emit_te(topi.nn.circular_pad, inputs[0], pad_before, 
pad_after)
   ```
   
   `_impl_v2` (opset 2, pads as attribute) and `_impl_v11` (opset 11-17,
   neither `wrap` nor `axes` legal) are left untouched.
   
   ## Validation
   
   Differential test (Relax `from_onnx` + `relax.build` + `VirtualMachine`
   vs onnxruntime) over 81 legal Pad models: 3 input shapes × all modes ×
   positive/negative pads, plus `axes` cases. Verified on the familyfuzz
   locked build `262c6d2e0` via runtime monkey-patch
   (`results/.../onnx_Pad/verify_patch.py`, no source files modified).
   
   | Category | Cases | Before | After |
   |---|---|---|---|
   | `constant` / `edge` / `reflect` (opset 11/13) | 41 | match onnxrt | match 
onnxrt (no regression) |
   | `constant` / `edge` opset-18, no axes | 8 | match | match |
   | `wrap` positive pads (opset 18, 19) | 14 | **rejected** 
(`OpAttributeInvalid`) | match onnxrt, `max\|diff\| = 0` |
   | `constant` + `axes` (opset 18, incl. negative axis) | 5 | **rejected** 
(`ValueError`) | match onnxrt, `max\|diff\| = 0` |
   | negative pads (crop) | 8 | match | match |
   | **Total** | **81** | 59 match / **22 rejected** | **76 match / 0 
rejected** / 5 documented deviation |
   
   The 5 documented deviations are `wrap` with **negative pads**, where the
   implementations disagree: `onnx.reference` (`np.pad` mode `"wrap"`) errors
   out entirely, onnxruntime uses its own crop-window semantics, and
   `topi.nn.circular_pad` follows the ONNX mod formula
   (`out[i] = in[(i - pad_before) mod dim]`), matching upstream #19827's
   identical implementation. Positive-pad `wrap` (the actual use case)
   agrees exactly across onnxrt / onnx.reference / TVM.
   
   Run:
   
   ```bash
   /home/shenqingchao/miniconda3/envs/tvm23/bin/python3 \
     results/TVM/deepseek-v4-flash/prove_hum/onnx_Pad/verify_patch.py
   ```
   
   ## Files changed
   
   - `python/tvm/relax/frontend/onnx/onnx_frontend.py` — add `Pad._impl_v18`
     with `wrap`/`axes` support for opset 18 (same handling as #19827's
     `_impl_v19`), closing the opset-18 gap.
   


-- 
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