This is an automated email from the ASF dual-hosted git repository.
mshr-h 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 1fa4b8627d [Relax][ONNX] Add ReverseSequence operator support (#20924)
1fa4b8627d is described below
commit 1fa4b8627de240c59a2531bee7fb6a6f88981f59
Author: Anirudh Mittal <[email protected]>
AuthorDate: Sat Nov 7 23:31:25 2026 +0530
[Relax][ONNX] Add ReverseSequence operator support (#20924)
---
python/tvm/relax/frontend/onnx/onnx_frontend.py | 16 +++
tests/python/relax/test_frontend_onnx.py | 118 +++++++++++++++++++++++
tests/python/relax/test_frontend_onnx_backend.py | 1 +
3 files changed, 135 insertions(+)
diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py
b/python/tvm/relax/frontend/onnx/onnx_frontend.py
index 2f97ef839a..badb3f9975 100644
--- a/python/tvm/relax/frontend/onnx/onnx_frontend.py
+++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py
@@ -3466,6 +3466,21 @@ class Tile(OnnxOpConverter):
return bb.emit_te(topi.dyn_tile, data, output_shape_vars, reps_len)
+class ReverseSequence(OnnxOpConverter):
+ """Converts an onnx ReverseSequence node into an equivalent Relax
expression."""
+
+ @classmethod
+ def _impl_v10(cls, bb, inputs, attr, params):
+ data = inputs[0]
+ seq_lengths = inputs[1]
+ batch_axis = int(attr.get("batch_axis", 1))
+ seq_axis = int(attr.get("time_axis", 0))
+
+ return relax.op.reverse_sequence(
+ data, seq_lengths, seq_axis=seq_axis, batch_axis=batch_axis
+ )
+
+
class Expand(OnnxOpConverter):
"""Converts an onnx Expand node into an equivalent Relax expression."""
@@ -6256,6 +6271,7 @@ def _get_convert_map():
"Pad": Pad,
"Split": Split,
"Tile": Tile,
+ "ReverseSequence": ReverseSequence,
"AveragePool": AveragePool,
"MaxPool": MaxPool,
"LpPool": LpPool,
diff --git a/tests/python/relax/test_frontend_onnx.py
b/tests/python/relax/test_frontend_onnx.py
index d34d208b00..5e8c408d61 100644
--- a/tests/python/relax/test_frontend_onnx.py
+++ b/tests/python/relax/test_frontend_onnx.py
@@ -9386,6 +9386,124 @@ def test_tile_dynamic_repeats():
)
+def test_reverse_sequence_ir():
+ """Verify lowered Relax IR for the ReverseSequence ONNX operator."""
+
+ def make_reverse_sequence_model(x_shape, seq_lens_shape, time_axis=None,
batch_axis=None):
+ attrs = {}
+ if time_axis is not None:
+ attrs["time_axis"] = time_axis
+ if batch_axis is not None:
+ attrs["batch_axis"] = batch_axis
+ node = helper.make_node(
+ "ReverseSequence",
+ inputs=["x", "sequence_lens"],
+ outputs=["y"],
+ **attrs,
+ )
+ graph = helper.make_graph(
+ [node],
+ "reverse_sequence_test",
+ inputs=[
+ helper.make_tensor_value_info("x", TensorProto.FLOAT, x_shape),
+ helper.make_tensor_value_info("sequence_lens",
TensorProto.INT64, seq_lens_shape),
+ ],
+ outputs=[helper.make_tensor_value_info("y", TensorProto.FLOAT,
x_shape)],
+ )
+ return helper.make_model(graph, producer_name="reverse_sequence_test")
+
+ # Default time_axis=0, batch_axis=1
+ model = make_reverse_sequence_model([4, 3], [3])
+ tvm_model = from_onnx(model, opset=10, keep_params_in_input=True)
+
+ @I.ir_module
+ class ExpectedDefault:
+ @R.function
+ def main(
+ x: R.Tensor((4, 3), dtype="float32"),
+ sequence_lens: R.Tensor((3,), dtype="int64"),
+ ) -> R.Tensor((4, 3), dtype="float32"):
+ R.func_attr({"num_input": 2})
+ with R.dataflow():
+ gv: R.Tensor((4, 3), dtype="float32") = R.reverse_sequence(
+ x, sequence_lens, seq_axis=0, batch_axis=1
+ )
+ R.output(gv)
+ return gv
+
+ tvm_tirx.ir.assert_structural_equal(tvm_model, ExpectedDefault)
+
+ # Custom time_axis=1, batch_axis=0
+ model = make_reverse_sequence_model([2, 5], [2], time_axis=1, batch_axis=0)
+ tvm_model = from_onnx(model, opset=10, keep_params_in_input=True)
+
+ @I.ir_module
+ class ExpectedCustom:
+ @R.function
+ def main(
+ x: R.Tensor((2, 5), dtype="float32"),
+ sequence_lens: R.Tensor((2,), dtype="int64"),
+ ) -> R.Tensor((2, 5), dtype="float32"):
+ R.func_attr({"num_input": 2})
+ with R.dataflow():
+ gv: R.Tensor((2, 5), dtype="float32") = R.reverse_sequence(
+ x, sequence_lens, seq_axis=1, batch_axis=0
+ )
+ R.output(gv)
+ return gv
+
+ tvm_tirx.ir.assert_structural_equal(tvm_model, ExpectedCustom)
+
+
+def test_reverse_sequence():
+ """Verify numerical correctness of ReverseSequence against ONNX Runtime."""
+
+ def verify_reverse_sequence(x, sequence_lens, time_axis=None,
batch_axis=None, opset=10):
+ attrs = {}
+ if time_axis is not None:
+ attrs["time_axis"] = time_axis
+ if batch_axis is not None:
+ attrs["batch_axis"] = batch_axis
+ node = helper.make_node(
+ "ReverseSequence",
+ inputs=["x", "sequence_lens"],
+ outputs=["y"],
+ **attrs,
+ )
+ seq_lens_proto = TensorProto.INT64 if sequence_lens.dtype == np.int64
else TensorProto.INT32
+ graph = helper.make_graph(
+ [node],
+ "reverse_sequence_test",
+ inputs=[
+ helper.make_tensor_value_info("x", TensorProto.FLOAT,
list(x.shape)),
+ helper.make_tensor_value_info(
+ "sequence_lens", seq_lens_proto, list(sequence_lens.shape)
+ ),
+ ],
+ outputs=[helper.make_tensor_value_info("y", TensorProto.FLOAT,
list(x.shape))],
+ )
+ model = helper.make_model(graph, producer_name="reverse_sequence_test")
+ inputs = {"x": x, "sequence_lens": sequence_lens}
+ check_correctness(model, inputs=inputs, opset=opset)
+
+ # Example 1: 2D, time_axis=0, batch_axis=1
+ x = np.arange(16, dtype=np.float32).reshape(4, 4)
+ seq_lens = np.array([4, 3, 2, 1], dtype=np.int64)
+ verify_reverse_sequence(x, seq_lens, time_axis=0, batch_axis=1)
+
+ # Example 2: 2D, time_axis=1, batch_axis=0
+ seq_lens = np.array([1, 2, 3, 4], dtype=np.int64)
+ verify_reverse_sequence(x, seq_lens, time_axis=1, batch_axis=0)
+
+ # Default attributes (time_axis=0, batch_axis=1)
+ verify_reverse_sequence(x, np.array([4, 2, 1, 3], dtype=np.int64))
+
+ # 3D tensor
+ x_3d = np.arange(24, dtype=np.float32).reshape(2, 3, 4)
+ seq_lens_3d = np.array([3, 1], dtype=np.int64)
+ verify_reverse_sequence(x_3d, seq_lens_3d, time_axis=1, batch_axis=0)
+
+
def _generate_roi_cases():
return [
pytest.param(False, None, False, id="no_roi"),
diff --git a/tests/python/relax/test_frontend_onnx_backend.py
b/tests/python/relax/test_frontend_onnx_backend.py
index 6fa706c150..11ed7e1551 100644
--- a/tests/python/relax/test_frontend_onnx_backend.py
+++ b/tests/python/relax/test_frontend_onnx_backend.py
@@ -182,6 +182,7 @@ _INCLUDE_OPS = [
"or",
"reciprocal",
"relu",
+ "reversesequence",
"round",
"scatternd",
"sigmoid",