The GitHub Actions job "Lint" on tvm.git/main has succeeded. Run started by GitHub user tlopex (triggered by tlopex).
Head commit for run: bd2df9f605cd86d6eba7b1f51ab3d06216cfcf21 / HuEnwei <[email protected]> [Fix][Relax][Frontend][ONNX] Support Pad mode="wrap" and axes input for op… (#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. --------- Co-authored-by: FFChopon <[email protected]> Co-authored-by: Claude <[email protected]> Report URL: https://github.com/apache/tvm/actions/runs/33289991591 With regards, GitHub Actions via GitBox --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
