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

ruihangl 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 75a6b308c6 [Relax][Frontend][TFLite] Fix and test `MATRIX_DIAG`, 
`MATRIX_SET_DIAG`, `SPARSE_TO_DENSE` (#19408)
75a6b308c6 is described below

commit 75a6b308c6ff1c7838e88b56fbcc0bd8d9f3328b
Author: HoYi <[email protected]>
AuthorDate: Fri Apr 17 00:13:10 2026 +0800

    [Relax][Frontend][TFLite] Fix and test `MATRIX_DIAG`, `MATRIX_SET_DIAG`, 
`SPARSE_TO_DENSE` (#19408)
    
    This PR partially implements test coverage requested in issue #18971 for
    Relax TFLite frontend operator tests.
    
    ## Bug Fix
    
    The TFLite frontend converters for `MATRIX_DIAG`, `MATRIX_SET_DIAG`, and
    `SPARSE_TO_DENSE` were broken due to calling non-existent Relax ops:
    
    - `relax.op.matrix_set_diag` - never registered in Relax
    - `relax.op.sparse_to_dense` - never registered in Relax
    
    These ops only exist as TOPI packed functions (`topi.matrix_set_diag`,
    `topi.sparse_to_dense`).
    
    **Fix:** Replace direct op calls with `call_dps_packed` to invoke the
    TOPI packed functions:
    - `convert_matrix_diag`: zeros + call_dps_packed("topi.matrix_set_diag",
    ...)
    - `convert_matrix_set_diag`: call_dps_packed("topi.matrix_set_diag",
    ...)
    - `convert_sparse_to_dense`: call_dps_packed("topi.sparse_to_dense",
    ...)
    
    Refs: #18971
---
 .../tvm/relax/frontend/tflite/tflite_frontend.py   |  51 ++++++---
 tests/python/relax/test_frontend_tflite.py         | 114 +++++++++++++++++++++
 2 files changed, 153 insertions(+), 12 deletions(-)

diff --git a/python/tvm/relax/frontend/tflite/tflite_frontend.py 
b/python/tvm/relax/frontend/tflite/tflite_frontend.py
index ce74f707cf..22f45e8a5a 100644
--- a/python/tvm/relax/frontend/tflite/tflite_frontend.py
+++ b/python/tvm/relax/frontend/tflite/tflite_frontend.py
@@ -3015,11 +3015,20 @@ class OperatorConverter:
             t_type = t.tensor.Type()
             assert t_type in (TensorType.INT32, TensorType.INT64)
 
-        out = relax.op.sparse_to_dense(
-            self.get_tensor_expr(indices),
-            list(self.get_tensor_value(output_shape)),
-            self.get_tensor_expr(values),
-            self.get_tensor_expr(default_value),
+        output_tensors = self.get_output_tensors(op)
+        output_tensor = output_tensors[0]
+        output_shape_val = to_int_list(self.get_tensor_shape(output_tensor))
+        output_dtype = self.get_tensor_type_str(output_tensor.tensor.Type())
+
+        indices_expr = self.get_tensor_expr(indices)
+        values_expr = self.get_tensor_expr(values)
+        default_value_expr = self.get_tensor_expr(default_value)
+        output_shape_expr = 
relax.const(list(self.get_tensor_value(output_shape)), "int32")
+
+        out = relax.op.call_dps_packed(
+            "topi.sparse_to_dense",
+            (indices_expr, output_shape_expr, values_expr, default_value_expr),
+            out_sinfo=relax.TensorStructInfo(output_shape_val, output_dtype),
         )
 
         return out
@@ -3700,7 +3709,18 @@ class OperatorConverter:
         input_expr = self.get_tensor_expr(input_tensors[0])
         diagonal_expr = self.get_tensor_expr(input_tensors[1])
 
-        out = relax.op.matrix_set_diag(input_expr, diagonal_expr)
+        output_tensors = self.get_output_tensors(op)
+        output_tensor = output_tensors[0]
+        output_shape = to_int_list(self.get_tensor_shape(output_tensor))
+        output_dtype = self.get_tensor_type_str(output_tensor.tensor.Type())
+
+        # topi.matrix_set_diag(input, diagonal, k1, k2, 
super_diag_right_align, sub_diag_right_align)
+        # TFLite MATRIX_SET_DIAG only sets the main diagonal, so k1=0, k2=0
+        out = relax.op.call_dps_packed(
+            "topi.matrix_set_diag",
+            (input_expr, diagonal_expr, relax.const(0), relax.const(0), 
relax.const(False), relax.const(False)),
+            out_sinfo=relax.TensorStructInfo(output_shape, output_dtype),
+        )
         return out
 
     def convert_matrix_diag(self, op):
@@ -3718,14 +3738,21 @@ class OperatorConverter:
                     scale and zero points to be equal"
             )
 
-        shape = to_int_list(self.get_tensor_shape(diagonal))
-        shape = np.append(shape, shape[-1])
-        dtype = self.get_tensor_type_str(diagonal.tensor.Type())
+        output_tensors = self.get_output_tensors(op)
+        output_tensor = output_tensors[0]
+        output_shape = to_int_list(self.get_tensor_shape(output_tensor))
+        output_dtype = self.get_tensor_type_str(output_tensor.tensor.Type())
 
-        input_expr = relax.op.zeros(tuple(shape), dtype)
         diagonal_expr = self.get_tensor_expr(diagonal)
-
-        out = relax.op.matrix_set_diag(input_expr, diagonal_expr)
+        zeros_expr = relax.op.zeros(output_shape, output_dtype)
+
+        # topi.matrix_set_diag(input, diagonal, k1, k2, 
super_diag_right_align, sub_diag_right_align)
+        # TFLite MATRIX_DIAG only sets the main diagonal, so k1=0, k2=0
+        out = relax.op.call_dps_packed(
+            "topi.matrix_set_diag",
+            (zeros_expr, diagonal_expr, relax.const(0), relax.const(0), 
relax.const(False), relax.const(False)),
+            out_sinfo=relax.TensorStructInfo(output_shape, output_dtype),
+        )
         return out
 
     def convert_densify(self, op):
diff --git a/tests/python/relax/test_frontend_tflite.py 
b/tests/python/relax/test_frontend_tflite.py
index a116daebb1..de26bae25e 100644
--- a/tests/python/relax/test_frontend_tflite.py
+++ b/tests/python/relax/test_frontend_tflite.py
@@ -2292,5 +2292,119 @@ def test_prelu(shared_axes):
     tvm.ir.assert_structural_equal(mod, Expected)
 
 
+def test_matrix_diag():
+    """Test TFLite MATRIX_DIAG operator."""
+
+    class MatrixDiag(tf.Module):
+        @tf.function(input_signature=[tf.TensorSpec(shape=(3,), 
dtype=tf.float32)])
+        def func(self, diagonal):
+            return tf.raw_ops.MatrixDiag(diagonal=diagonal)
+
+    @I.ir_module
+    class Expected:
+        @R.function
+        def main(diagonal: R.Tensor((3,), dtype="float32")) -> R.Tensor((3, 
3), dtype="float32"):
+            R.func_attr({"num_input": 1})
+            with R.dataflow():
+                lv: R.Tensor((3, 3), dtype="float32") = R.zeros(R.shape([3, 
3]), dtype="float32")
+                gv = R.call_dps_packed(
+                    "topi.matrix_set_diag",
+                    (
+                        lv,
+                        diagonal,
+                        R.const(0, "int32"),
+                        R.const(0, "int32"),
+                        R.const(False, "bool"),
+                        R.const(False, "bool"),
+                    ),
+                    out_sinfo=R.Tensor((3, 3), dtype="float32"),
+                )
+                R.output(gv)
+            return gv
+
+    verify(MatrixDiag, Expected)
+
+
+def test_matrix_set_diag():
+    """Test TFLite MATRIX_SET_DIAG operator."""
+
+    class MatrixSetDiag(tf.Module):
+        @tf.function(
+            input_signature=[
+                tf.TensorSpec(shape=(3, 3), dtype=tf.float32),
+                tf.TensorSpec(shape=(3,), dtype=tf.float32),
+            ]
+        )
+        def func(self, input, diagonal):
+            return tf.raw_ops.MatrixSetDiag(input=input, diagonal=diagonal)
+
+    @I.ir_module
+    class Expected:
+        @R.function
+        def main(
+            input: R.Tensor((3, 3), dtype="float32"),
+            diagonal: R.Tensor((3,), dtype="float32"),
+        ) -> R.Tensor((3, 3), dtype="float32"):
+            R.func_attr({"num_input": 2})
+            with R.dataflow():
+                gv = R.call_dps_packed(
+                    "topi.matrix_set_diag",
+                    (
+                        input,
+                        diagonal,
+                        R.const(0, "int32"),
+                        R.const(0, "int32"),
+                        R.const(False, "bool"),
+                        R.const(False, "bool"),
+                    ),
+                    out_sinfo=R.Tensor((3, 3), dtype="float32"),
+                )
+                R.output(gv)
+            return gv
+
+    verify(MatrixSetDiag, Expected)
+
+
+def test_sparse_to_dense():
+    """Test TFLite SPARSE_TO_DENSE operator."""
+
+    class SparseToDense(tf.Module):
+        @tf.function(
+            input_signature=[
+                tf.TensorSpec(shape=(2,), dtype=tf.int32),
+                tf.TensorSpec(shape=(2,), dtype=tf.float32),
+                tf.TensorSpec(shape=(), dtype=tf.float32),
+            ]
+        )
+        def func(self, indices, values, default_value):
+            # output_shape is provided as a constant, not an input
+            return tf.raw_ops.SparseToDense(
+                sparse_indices=indices,
+                output_shape=tf.constant([3], dtype=tf.int32),
+                sparse_values=values,
+                default_value=default_value,
+            )
+
+    @I.ir_module
+    class Expected:
+        @R.function
+        def main(
+            indices: R.Tensor((2,), dtype="int32"),
+            values: R.Tensor((2,), dtype="float32"),
+            default_value: R.Tensor((), dtype="float32"),
+        ) -> R.Tensor((3,), dtype="float32"):
+            R.func_attr({"num_input": 3})
+            with R.dataflow():
+                gv = R.call_dps_packed(
+                    "topi.sparse_to_dense",
+                    (indices, R.const([3], "int32"), values, default_value),
+                    out_sinfo=R.Tensor((3,), dtype="float32"),
+                )
+                R.output(gv)
+            return gv
+
+    verify(SparseToDense, Expected)
+
+
 if __name__ == "__main__":
     pytest.main(["-s", __file__])

Reply via email to