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

tlopex 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 8f328e802c [Relax][ONNX] Add CastLike support and dynamic-k Trilu to 
expand backend coverage (#19898)
8f328e802c is described below

commit 8f328e802cfe5e41fcc8f5c17e7582b1c28bfce4
Author: Hongyi Wu <[email protected]>
AuthorDate: Fri Sep 11 12:43:31 2026 +0800

    [Relax][ONNX] Add CastLike support and dynamic-k Trilu to expand backend 
coverage (#19898)
    
    ## Summary
    
    This PR adds `CastLike` support and dynamic-`k` support for `Trilu` in
    the Relax
    ONNX frontend, then adds `relu`, `tril`, and `triu` to the official ONNX
    backend
    test allowlist.
    
    ### Goal
    
    Increase the Relax ONNX frontend's coverage in the official ONNX Backend
    Test
    Suite by enabling operators that already have hand-written frontend
    tests but
    still fail some official node-level tests.
    
    ### What changed
    
    - Added a `CastLike` converter.
    - Removed the constant-`k` restriction from the `Trilu` converter.
    - Added `relu`, `tril`, and `triu` to `_INCLUDE_OPS`.
    - Added `_EXCLUDE_PATTERNS` to filter out a few model-level tests whose
    names
    collide with the node-level include patterns.
    
    ### Result
    
    ```text
    # Before
    388 passed, 3142 skipped
    
    # After
    451 passed, 3377 skipped
    ```
    
    This PR directly addresses part of #19505.
    
    ## Design
    
    ### CastLike support
    
    ONNX `CastLike` (opset 15+) takes two inputs: the data to cast and a
    tensor
    whose dtype determines the output dtype. The opset-18 expanded form of
    `Relu`
    decomposes the operator into a subgraph that uses `CastLike`, so
    importing any
    opset-18 `Relu` model previously failed with:
    
    ```text
    OpNotImplemented: The following operators are not supported for frontend 
ONNX: CastLike
    ```
    
    The new `CastLike` converter reads the dtype of the second input and
    emits
    `relax.op.astype(data, target_dtype)`. It handles both constant and
    dynamic
    target tensors because the dtype is taken from the input's type
    information.
    
    ### Trilu dynamic `k`
    
    The existing `Trilu` converter only accepted a constant `k` diagonal
    offset and
    raised `ValueError` for any dynamic / graph-input `k`. Several official
    ONNX
    node tests (`test_tril_neg`, `test_triu_zero`, etc.) supply `k` as a
    graph
    input, so those tests could not pass.
    
    The converter now branches:
    
    - If `k` is a constant or omitted, use the optimized `relax.op.tril` /
      `relax.op.triu` paths.
    - If `k` is dynamic, construct the lower/upper-triangular mask
    explicitly:
      1. Build row and column index tensors with `relax.op.arange`.
      2. Compute `col_index - row_index`.
      3. Compare against the dynamic scalar `k`.
    4. Broadcast the mask to the input shape and use `relax.op.where` to
    zero
         the excluded elements.
    
    ## Updated Allowlist
    
    | Operator | Added to `_INCLUDE_OPS` | Tests gained |
    |---|---|---|
    | `relu` | yes | 2 |
    | `tril` | yes | 18 |
    | `triu` | yes | 18 |
    
    Total backend suite progress: **388 passed → 451 passed** (all CPU; CUDA
    tests
    are registered but skipped because the backend adapter only supports
    CPU).
    
    ## Safety Checks
    
    - `CastLike` returns `relax.op.astype(data, target_dtype)` where
      `target_dtype` is the dtype of the second input.
    - Constant / omitted `k` in `Trilu` keeps the existing optimized
      `relax.op.tril` / `relax.op.triu` lowering.
    - Dynamic `k` in `Trilu` is implemented without calling `relax.op.tril`
    /
      `triu` with a non-constant diagonal offset.
    - `_INCLUDE_OPS` remains the gate for which backend tests run; a small
    `_EXCLUDE_PATTERNS` list filters model-level name collisions so the
    suite
      stays green without limiting the registered test classes.
    
    ## Out of Scope / Non-Goals
    
    - This PR does not address the other candidate operators that still fail
    node
    tests (`cast`, `equal`, `gather`, `reshape`, `shape`, `reduce_*`). Those
    will
      be handled in follow-up PRs.
    - This PR does not change the frontend's handling of `Relu` itself; it
    only
      unblocks the expanded form by adding `CastLike`.
    - This PR does not add CUDA support to the backend test adapter.
    
    ## Tests
    
    | Test | Coverage |
    |---|---|
    | `test_castlike_ir` | New `CastLike` converter, structural IR check |
    | `test_trilu` / `test_trilu_with_const_k` | Existing Trilu coverage,
    unchanged |
    | `test_trilu_dynamic_k_ir` | New parametrized structural IR test for
    dynamic `k` (`upper=True/False`) |
    | `test_frontend_onnx_backend.py` | Official ONNX node tests for `relu`,
    `tril`, `triu` |
    
    Local validation:
    
    ```bash
    python -m pytest tests/python/relax/test_frontend_onnx.py::test_castlike_ir 
-xvs
    python -m pytest tests/python/relax/test_frontend_onnx.py -k "trilu" -xvs
    python -m pytest tests/python/relax/test_frontend_onnx_backend.py -q
    python -m ruff format --check \
      python/tvm/relax/frontend/onnx/onnx_frontend.py \
      tests/python/relax/test_frontend_onnx.py \
      tests/python/relax/test_frontend_onnx_backend.py
    python -m ruff check \
      python/tvm/relax/frontend/onnx/onnx_frontend.py \
      tests/python/relax/test_frontend_onnx.py \
      tests/python/relax/test_frontend_onnx_backend.py
    ```
    
    Result:
    
    ```text
    test_castlike_ir: passed
    test_frontend_onnx.py -k "trilu": 10 passed
    test_frontend_onnx_backend.py -q: 450 passed, 3080 skipped
    ruff format --check: 3 files already formatted
    ruff check: All checks passed
    ```
    
    ## References
    
    - Relates to [#19505](https://github.com/apache/tvm/issues/19505):
    `[Relax][ONNX]
      Use ONNX Backend Tests to improve frontend coverage`.
---
 python/tvm/relax/frontend/onnx/onnx_frontend.py  |  59 +++++--
 tests/python/relax/test_frontend_onnx.py         | 200 ++++++++++++++++++++++-
 tests/python/relax/test_frontend_onnx_backend.py |  28 +++-
 3 files changed, 270 insertions(+), 17 deletions(-)

diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py 
b/python/tvm/relax/frontend/onnx/onnx_frontend.py
index 861dc840f5..62cde1ee12 100644
--- a/python/tvm/relax/frontend/onnx/onnx_frontend.py
+++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py
@@ -1263,6 +1263,7 @@ class Cast(OnnxOpConverter):
             if all([isinstance(x, tirx.IntImm) for x in shape]):
                 shape = [int(x) for x in shape]
                 return relax.const(shape, to_type)
+            inputs = [bb.normalize(relax.op.shape_to_tensor(shape))]
         if isinstance(inputs[0], relax.Constant):
             output = inputs[0].data.numpy().astype(to_type)
             return relax.const(output, to_type)
@@ -1330,6 +1331,24 @@ class Cast(OnnxOpConverter):
         return relax.op.astype(inputs[0], to_type)
 
 
+class CastLike(OnnxOpConverter):
+    """Convert an onnx CastLike node into an equivalent Relax expression."""
+
+    @classmethod
+    def _impl_v15(cls, bb, inputs, attr, params):
+        data = inputs[0]
+        target = inputs[1]
+        if isinstance(target, relax.ShapeExpr):
+            target_dtype = "int64"
+        else:
+            target_dtype = getattr(getattr(target, "ty", None), "dtype", None) 
or getattr(
+                target, "dtype", None
+            )
+        if target_dtype is None:
+            raise ValueError(f"CastLike: unable to determine dtype from target 
{target}")
+        return Cast._impl_v13(bb, [data], {"to": str(target_dtype)}, params)
+
+
 def _normalize_negative_indices(bb, indices, axis_extent):
     """Map negative indices to idx + axis_extent; skip unsigned indices. 
axis_extent
     must be broadcastable against indices and share its dtype.
@@ -1858,21 +1877,39 @@ class Trilu(OnnxOpConverter):
     def _impl_v14(cls, bb, inputs, attr, params):
         upper = attr.get("upper", True)
         x = inputs[0]
-        k = inputs[1] if len(inputs) > 1 else 0
+        k = inputs[1] if len(inputs) > 1 else None
 
-        if len(inputs) > 1:
-            k = get_constant(inputs[1], params)
-            if isinstance(k, relax.Constant):
-                k = int(k.data.numpy().item())
-            else:
-                raise ValueError("Currently only support constant k for Trilu 
op.")
-        else:
+        if k is None:
             k = 0
+        else:
+            k = get_constant(k, params)
+        if isinstance(k, relax.Constant):
+            k = int(k.data.numpy().item())
+        if isinstance(k, tirx.IntImm):
+            k = int(k)
+        if isinstance(k, int):
+            if upper:
+                return relax.op.triu(x, k)
+            return relax.op.tril(x, k)
 
+        # Dynamic k: build the mask explicitly so it works with any scalar k.
+        shape = x.ty.shape
+        m, n = shape[-2], shape[-1]
+        row_idx = relax.op.reshape(relax.op.arange(0, m, dtype="int64"), (m, 
1))
+        col_idx = relax.op.reshape(relax.op.arange(0, n, dtype="int64"), (1, 
n))
+        diff = relax.op.subtract(col_idx, row_idx)
+        if tvm.ir.is_prim_expr(k):
+            shape_value = k if str(k.ty) == "int64" else k.astype("int64")
+            k_int64 = 
bb.normalize(relax.op.shape_to_tensor(relax.ShapeExpr([shape_value])))
+            k_int64 = bb.normalize(relax.op.squeeze(k_int64, axis=[0]))
+        else:
+            k_int64 = relax.op.astype(k, "int64")
         if upper:
-            return relax.op.triu(x, k)
+            mask = relax.op.greater_equal(diff, k_int64)
         else:
-            return relax.op.tril(x, k)
+            mask = relax.op.less_equal(diff, k_int64)
+        mask = relax.op.broadcast_to(mask, shape)
+        return relax.op.where(mask, x, relax.const(0, x.ty.dtype.dtype))
 
 
 class Relu(OnnxOpConverter):
@@ -6084,6 +6121,7 @@ def _get_convert_map():
         "Max": Max,
         "Mean": Mean,
         "Cast": Cast,
+        "CastLike": CastLike,
         "Gemm": Gemm,
         "MatMul": MatMul,
         "MatMulInteger": MatMulInteger,
@@ -6440,6 +6478,7 @@ class ONNXGraphImporter:
                 "Equal",
                 "Where",
                 "Cast",
+                "CastLike",
                 "Squeeze",
             ]
             return_tuple_ops = [
diff --git a/tests/python/relax/test_frontend_onnx.py 
b/tests/python/relax/test_frontend_onnx.py
index e2de5e02a3..92f62d32f6 100644
--- a/tests/python/relax/test_frontend_onnx.py
+++ b/tests/python/relax/test_frontend_onnx.py
@@ -1773,6 +1773,93 @@ def test_cast_nan_inf_to_int8():
     np.testing.assert_array_equal(out_np, expected)
 
 
+def test_castlike_ir():
+    castlike_node = helper.make_node("CastLike", ["a", "b"], ["c"])
+    graph = helper.make_graph(
+        [castlike_node],
+        "castlike_test",
+        inputs=[
+            helper.make_tensor_value_info("a", TensorProto.INT32, [1, 32]),
+            helper.make_tensor_value_info("b", TensorProto.FLOAT, [1]),
+        ],
+        outputs=[helper.make_tensor_value_info("c", TensorProto.FLOAT, [1, 
32])],
+    )
+    model = helper.make_model(graph, producer_name="castlike_test")
+    tvm_model = from_onnx(model, opset=15, keep_params_in_input=True)
+
+    @I.ir_module
+    class Expected:
+        @R.function
+        def main(
+            a: R.Tensor((1, 32), dtype="int32"),
+            b: R.Tensor((1,), dtype="float32"),
+        ) -> R.Tensor((1, 32), dtype="float32"):
+            R.func_attr({"num_input": 2})
+            with R.dataflow():
+                gv: R.Tensor((1, 32), dtype="float32") = R.astype(a, "float32")
+                R.output(gv)
+            return gv
+
+    tvm.ir.assert_structural_equal(tvm_model, Expected)
+
+
+def test_castlike_nan_inf_to_int8():
+    vals = np.array([np.nan, np.inf, -np.inf, 1.9, -1.9], dtype=np.float32)
+    castlike_node = helper.make_node("CastLike", ["data", "like"], ["output"])
+    graph = helper.make_graph(
+        [castlike_node],
+        "castlike_nan_inf_test",
+        inputs=[
+            helper.make_tensor_value_info("data", TensorProto.FLOAT, 
list(vals.shape)),
+            helper.make_tensor_value_info("like", TensorProto.INT8, [1]),
+        ],
+        outputs=[helper.make_tensor_value_info("output", TensorProto.INT8, 
list(vals.shape))],
+    )
+    model = helper.make_model(graph, producer_name="castlike_nan_inf_test")
+    inputs = {"data": vals, "like": np.array([0], dtype=np.int8)}
+    check_correctness(model, inputs=inputs, opset=15, check_dtypes=True)
+
+
[email protected]("symbolic", [False, True], ids=["static", "symbolic"])
+def test_castlike_shape_expr_data(symbolic: bool):
+    shape_node = helper.make_node("Shape", ["data"], ["data_shape"])
+    castlike_node = helper.make_node("CastLike", ["data_shape", "like"], 
["output"])
+    data_shape = ["n", 3] if symbolic else [2, 3]
+    graph = helper.make_graph(
+        [shape_node, castlike_node],
+        "castlike_shape_data_test",
+        inputs=[
+            helper.make_tensor_value_info("data", TensorProto.FLOAT, 
data_shape),
+            helper.make_tensor_value_info("like", TensorProto.FLOAT, [1]),
+        ],
+        outputs=[helper.make_tensor_value_info("output", TensorProto.FLOAT, 
[2])],
+    )
+    model = helper.make_model(graph, producer_name="castlike_shape_data_test")
+    inputs = None
+    if symbolic:
+        inputs = {
+            "data": np.ones((2, 3), dtype="float32"),
+            "like": np.zeros((1,), dtype="float32"),
+        }
+    check_correctness(model, inputs=inputs, opset=15, check_dtypes=True)
+
+
+def test_castlike_shape_expr_target():
+    shape_node = helper.make_node("Shape", ["like"], ["like_shape"])
+    castlike_node = helper.make_node("CastLike", ["data", "like_shape"], 
["output"])
+    graph = helper.make_graph(
+        [shape_node, castlike_node],
+        "castlike_shape_target_test",
+        inputs=[
+            helper.make_tensor_value_info("data", TensorProto.FLOAT, [3]),
+            helper.make_tensor_value_info("like", TensorProto.FLOAT, [2, 3]),
+        ],
+        outputs=[helper.make_tensor_value_info("output", TensorProto.INT64, 
[3])],
+    )
+    model = helper.make_model(graph, 
producer_name="castlike_shape_target_test")
+    check_correctness(model, opset=15, check_dtypes=True)
+
+
 def test_gather():
     def _verify_gather(data_shape, indices, out_shape, expected, axis=0):
         gather_node = helper.make_node("Gather", ["data", "indices"], ["y"], 
axis=axis)
@@ -4176,7 +4263,7 @@ def test_shape_start_end_scalar():
 
 def test_trilu():
     def verify_trilu(upper: bool):
-        node = helper.make_node("Trilu", ["x"], ["y"], upper=upper)
+        node = helper.make_node("Trilu", ["x", ""], ["y"], upper=upper)
         graph = helper.make_graph(
             [node],
             "trilu_test",
@@ -4212,6 +4299,117 @@ def test_trilu_with_const_k(k_value: int):
     check_correctness(model)
 
 
+def test_trilu_initializer_with_params_in_input():
+    k_value = np.array(1, dtype="int64")
+    graph = helper.make_graph(
+        [helper.make_node("Trilu", inputs=["x", "k"], outputs=["y"])],
+        "trilu_initializer_test",
+        inputs=[helper.make_tensor_value_info("x", TensorProto.FLOAT, [2, 3])],
+        initializer=[numpy_helper.from_array(k_value, name="k")],
+        outputs=[helper.make_tensor_value_info("y", TensorProto.FLOAT, [2, 
3])],
+    )
+    model = helper.make_model(
+        graph,
+        producer_name="trilu_initializer_test",
+        opset_imports=[helper.make_opsetid("", 14)],
+    )
+
+    tvm_model = from_onnx(model, opset=14, keep_params_in_input=True)
+    assert len(tvm_model["main"].attrs["params"]) == 1
+    
np.testing.assert_array_equal(tvm_model["main"].attrs["params"][0].numpy(), 
k_value)
+    tvm_model["main"] = tvm_model["main"].without_attr("params")
+
+    @I.ir_module
+    class Expected:
+        @R.function
+        def main(
+            x: R.Tensor((2, 3), dtype="float32"),
+            k: R.Tensor((), dtype="int64"),
+        ) -> R.Tensor((2, 3), dtype="float32"):
+            R.func_attr({"num_input": 1})
+            with R.dataflow():
+                gv: R.Tensor((2, 3), dtype="float32") = R.triu(x, 1)
+                R.output(gv)
+            return gv
+
+    tvm.ir.assert_structural_equal(tvm_model, Expected)
+
+
[email protected]("upper", [True, False])
+def test_trilu_dynamic_k_ir(upper: bool):
+    if upper:
+        nodes = [helper.make_node("Trilu", inputs=["x", "k"], outputs=["y"], 
upper=True)]
+        inputs = [
+            helper.make_tensor_value_info("x", TensorProto.FLOAT, [2, 3]),
+            helper.make_tensor_value_info("k", TensorProto.INT64, []),
+        ]
+        outputs = [helper.make_tensor_value_info("y", TensorProto.FLOAT, [2, 
3])]
+    else:
+        index = numpy_helper.from_array(np.array(0, dtype="int64"), 
name="index")
+        nodes = [
+            helper.make_node("Shape", ["x"], ["x_shape"]),
+            helper.make_node("Constant", [], ["index"], value=index),
+            helper.make_node("Gather", ["x_shape", "index"], ["k"]),
+            helper.make_node("Trilu", ["x", "k"], ["y"], upper=False),
+        ]
+        inputs = [helper.make_tensor_value_info("x", TensorProto.FLOAT, ["m", 
3])]
+        outputs = [helper.make_tensor_value_info("y", TensorProto.FLOAT, ["m", 
3])]
+
+    graph = helper.make_graph(nodes, "trilu_dynamic_k_graph", inputs, outputs)
+    model = helper.make_model(graph, producer_name="trilu_dynamic_k_graph")
+    tvm_model = from_onnx(model, opset=14, keep_params_in_input=True)
+
+    if upper:
+
+        @I.ir_module
+        class ExpectedTriu:
+            @R.function
+            def main(
+                x: R.Tensor((2, 3), dtype="float32"),
+                k: R.Tensor((), dtype="int64"),
+            ) -> R.Tensor((2, 3), dtype="float32"):
+                R.func_attr({"num_input": 2})
+                with R.dataflow():
+                    lv: R.Tensor((3,), dtype="int64") = R.arange(0, 3, 1, 
dtype="int64")
+                    lv1: R.Tensor((1, 3), dtype="int64") = R.reshape(lv, 
R.shape([1, 3]))
+                    lv2: R.Tensor((2,), dtype="int64") = R.arange(0, 2, 1, 
dtype="int64")
+                    lv3: R.Tensor((2, 1), dtype="int64") = R.reshape(lv2, 
R.shape([2, 1]))
+                    lv4: R.Tensor((2, 3), dtype="int64") = R.subtract(lv1, lv3)
+                    lv5: R.Tensor((), dtype="int64") = R.astype(k, 
dtype="int64")
+                    lv6: R.Tensor((2, 3), dtype="bool") = R.greater_equal(lv4, 
lv5)
+                    lv7: R.Tensor((2, 3), dtype="bool") = R.broadcast_to(lv6, 
R.shape([2, 3]))
+                    gv: R.Tensor((2, 3), dtype="float32") = R.where(lv7, x, 
R.const(0.0, "float32"))
+                    R.output(gv)
+                return gv
+
+        expected = ExpectedTriu
+    else:
+
+        @I.ir_module
+        class ExpectedTril:
+            @R.function
+            def main(x: R.Tensor(("m", 3), dtype="float32")) -> R.Tensor(("m", 
3), dtype="float32"):
+                R.func_attr({"num_input": 1})
+                m = T.int64()
+                with R.dataflow():
+                    lv: R.Tensor((1,), dtype="int64") = 
R.shape_to_tensor(R.shape([m]))
+                    lv1: R.Tensor((3,), dtype="int64") = R.arange(0, 3, 1, 
dtype="int64")
+                    lv2: R.Tensor((1, 3), dtype="int64") = R.reshape(lv1, 
R.shape([1, 3]))
+                    lv3: R.Tensor((m,), dtype="int64") = R.arange(0, m, 1, 
dtype="int64")
+                    lv4: R.Tensor((m, 1), dtype="int64") = R.reshape(lv3, 
R.shape([m, 1]))
+                    lv5: R.Tensor((m, 3), dtype="int64") = R.subtract(lv2, lv4)
+                    lv6: R.Tensor((), dtype="int64") = R.squeeze(lv, axis=[0])
+                    lv7: R.Tensor((m, 3), dtype="bool") = R.less_equal(lv5, 
lv6)
+                    lv8: R.Tensor((m, 3), dtype="bool") = R.broadcast_to(lv7, 
R.shape([m, 3]))
+                    gv: R.Tensor((m, 3), dtype="float32") = R.where(lv8, x, 
R.const(0.0, "float32"))
+                    R.output(gv)
+                return gv
+
+        expected = ExpectedTril
+
+    tvm.ir.assert_structural_equal(tvm_model, expected)
+
+
 def test_selu():
     model = make_unary_model("Selu", [2, 3])
     tvm_model = from_onnx(model, keep_params_in_input=True)
diff --git a/tests/python/relax/test_frontend_onnx_backend.py 
b/tests/python/relax/test_frontend_onnx_backend.py
index e2d08040bd..45386fe57c 100644
--- a/tests/python/relax/test_frontend_onnx_backend.py
+++ b/tests/python/relax/test_frontend_onnx_backend.py
@@ -19,13 +19,13 @@
 ONNX Backend Tests
 ===================
 Systematically verify the Relax ONNX importer using the official ONNX
-Backend Test Suite (node-level tests only).  Each test loads a small
-ONNX model with protobuf reference inputs/outputs and checks that the
-Relax-imported model produces numerically correct results.
+Backend Test Suite.  Each test loads a small ONNX model with protobuf
+reference inputs/outputs and checks that the Relax-imported model
+produces numerically correct results.
 
-Only ``onnx.backend.test.data.node`` tests are registered here; real,
-simple, and PyTorch model tests are out of scope for importer-level
-semantic verification.
+Currently ``_INCLUDE_OPS`` selects node-level operator tests.  Other
+test classes (real/simple/PyTorch models) remain available in
+``backend_test.test_cases`` and can be enabled explicitly in the future.
 
 """
 
@@ -168,6 +168,7 @@ _INCLUDE_OPS = [
     "less",
     "less_equal",
     "lrn",
+    "logsoftmax",
     "matmul",
     "matmulinteger",
     "mean",
@@ -179,6 +180,7 @@ _INCLUDE_OPS = [
     "not",
     "or",
     "reciprocal",
+    "relu",
     "round",
     "scatternd",
     "sigmoid",
@@ -187,6 +189,7 @@ _INCLUDE_OPS = [
     "sinh",
     "size",
     "slice",
+    "softmax",
     "spacetodepth",
     "sqrt",
     "squeeze",
@@ -196,6 +199,8 @@ _INCLUDE_OPS = [
     "tanh",
     "tile",
     "transpose",
+    "tril",
+    "triu",
     "unique",
     "unsqueeze",
     "where",
@@ -234,4 +239,15 @@ class 
_AllowlistedBackendTest(onnx.backend.test.BackendTest):
 
 backend_test = _AllowlistedBackendTest(TVMRelaxBackend, __name__)
 
+# A small number of model-level tests (e.g. from PyTorch converted models)
+# have names that collide with the node-level include patterns above.  The
+# current adapter is focused on node-level protobuf test cases, so exclude
+# those known collisions explicitly rather than limiting the test classes.
+_EXCLUDE_PATTERNS = [
+    r"^test_softmax_functional_dim3_cpu$",
+    r"^test_softmax_lastdim_cpu$",
+]
+for _pattern in _EXCLUDE_PATTERNS:
+    backend_test.exclude(_pattern)
+
 globals().update(backend_test.test_cases)

Reply via email to