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

tqchen 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 26041f8870 [Relax][Frontend] Support max/min in frontend op interface 
(#17782)
26041f8870 is described below

commit 26041f887006e74b7932fe0d2cd2ac57ddc36106
Author: Ruihang Lai <[email protected]>
AuthorDate: Wed Mar 26 19:06:58 2025 -0400

    [Relax][Frontend] Support max/min in frontend op interface (#17782)
    
    This PR adds the min/max reduction operators to the relax nn frontend
    operators, which were missing before this PR.
---
 python/tvm/relax/frontend/nn/op.py        | 68 +++++++++++++++++++++++++++++++
 tests/python/relax/test_frontend_nn_op.py | 48 ++++++++++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/python/tvm/relax/frontend/nn/op.py 
b/python/tvm/relax/frontend/nn/op.py
index 4c6d921db7..23045f7c4e 100644
--- a/python/tvm/relax/frontend/nn/op.py
+++ b/python/tvm/relax/frontend/nn/op.py
@@ -245,6 +245,74 @@ def sum(
     return wrap_nested(_op.sum(x._expr, axis, keepdims), name)
 
 
+def max(
+    x: Tensor,
+    axis: Optional[Union[int, List[int]]] = None,
+    keepdims: bool = False,
+    name: str = "max",
+) -> Tensor:
+    """Computes the max of tensor elements over given axes.
+
+    Parameters
+    ----------
+    x : Tensor
+        The input data tensor
+
+    axis : Optional[Union[int, List[int]]]
+        Axis or axes along which a max is performed.
+        The default, axis=None, will max all of the elements of the input 
tensor.
+        Negative indexing is supported.
+
+    keepdims : bool
+        If this is set to True, the axes which are reduced are left in the 
result as
+        dimensions with size one.
+        With this option, the result will broadcast correctly against the 
input tensor.
+
+    name : str
+        Name hint for this operation.
+
+    Returns
+    -------
+    result : Tensor
+        The computed result.
+    """
+    return wrap_nested(_op.max(x._expr, axis, keepdims), name)
+
+
+def min(
+    x: Tensor,
+    axis: Optional[Union[int, List[int]]] = None,
+    keepdims: bool = False,
+    name: str = "min",
+) -> Tensor:
+    """Computes the min of tensor elements over given axes.
+
+    Parameters
+    ----------
+    x : Tensor
+        The input data tensor
+
+    axis : Optional[Union[int, List[int]]]
+        Axis or axes along which a min is performed.
+        The default, axis=None, will min all of the elements of the input 
tensor.
+        Negative indexing is supported.
+
+    keepdims : bool
+        If this is set to True, the axes which are reduced are left in the 
result as
+        dimensions with size one.
+        With this option, the result will broadcast correctly against the 
input tensor.
+
+    name : str
+        Name hint for this operation.
+
+    Returns
+    -------
+    result : Tensor
+        The computed result.
+    """
+    return wrap_nested(_op.min(x._expr, axis, keepdims), name)
+
+
 def matmul(a: Tensor, b: Tensor, out_dtype: Optional[str] = None, name: str = 
"matmul") -> Tensor:
     """General matrix multiplication of two tensors, with broadcasting on 
batched dimensions.
 
diff --git a/tests/python/relax/test_frontend_nn_op.py 
b/tests/python/relax/test_frontend_nn_op.py
index 682f805026..6e63b0e4c0 100644
--- a/tests/python/relax/test_frontend_nn_op.py
+++ b/tests/python/relax/test_frontend_nn_op.py
@@ -128,6 +128,54 @@ def test_sum():
     tvm.ir.assert_structural_equal(irmodule["test"], test)
 
 
+def test_max():
+    class Model(Module):
+        def test(self, x: Tensor):
+            z0 = op.max(x, axis=[1, 2], keepdims=True)
+            return z0
+
+    # fmt: off
+    @R.function
+    def test(x: R.Tensor((3, 5, 2, 4), dtype="float32"), _io: R.Object) -> 
R.Tuple(R.Tensor((3, 1, 1, 4), dtype="float32"), R.Tuple(R.Object)):
+        R.func_attr({"num_input": 2})
+        with R.dataflow():
+            max: R.Tensor((3, 1, 1, 4), dtype="float32") = R.max(x, axis=[1, 
2], keepdims=True)
+            gv1: R.Tuple(R.Tensor((3, 1, 1, 4), dtype="float32"), 
R.Tuple(R.Object)) = max, (_io,)
+            R.output(gv1)
+        return gv1
+    # fmt: on
+
+    m = Model()
+    irmodule, _ = m.export_tvm(
+        spec={"test": {"x": spec.Tensor([3, 5, 2, 4], "float32")}}, debug=True
+    )
+    tvm.ir.assert_structural_equal(irmodule["test"], test)
+
+
+def test_min():
+    class Model(Module):
+        def test(self, x: Tensor):
+            z0 = op.min(x, axis=[1, 2], keepdims=True)
+            return z0
+
+    # fmt: off
+    @R.function
+    def test(x: R.Tensor((3, 5, 2, 4), dtype="float32"), _io: R.Object) -> 
R.Tuple(R.Tensor((3, 1, 1, 4), dtype="float32"), R.Tuple(R.Object)):
+        R.func_attr({"num_input": 2})
+        with R.dataflow():
+            min: R.Tensor((3, 1, 1, 4), dtype="float32") = R.min(x, axis=[1, 
2], keepdims=True)
+            gv1: R.Tuple(R.Tensor((3, 1, 1, 4), dtype="float32"), 
R.Tuple(R.Object)) = min, (_io,)
+            R.output(gv1)
+        return gv1
+    # fmt: on
+
+    m = Model()
+    irmodule, _ = m.export_tvm(
+        spec={"test": {"x": spec.Tensor([3, 5, 2, 4], "float32")}}, debug=True
+    )
+    tvm.ir.assert_structural_equal(irmodule["test"], test)
+
+
 def test_manipulate():
     class Model(Module):
         def test(self, x: Tensor):

Reply via email to