You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by GitBox <gi...@apache.org> on 2021/02/11 02:47:06 UTC

[GitHub] [tvm] codeislife99 opened a new pull request #7442: SparseFillEmptyRows Op

codeislife99 opened a new pull request #7442:
URL: https://github.com/apache/tvm/pull/7442


   -


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577141409



##########
File path: tests/python/relay/dyn/test_dynamic_op_level3.py
##########
@@ -202,5 +209,152 @@ def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_
     verify_sparse_to_dense(1, 3, None, [5], [0, 3, 0, 0, 0])  # default value not specified
 
 
+@pytest.mark.parametrize(
+    "sparse_indices, sparse_values, dense_shape, default_value",
+    [
+        (
+            np.array([[0, 1], [0, 3], [2, 0], [3, 1]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([5, 6], dtype=np.int64),
+            np.array([10], dtype=np.int64),
+        ),
+        (
+            np.array([[1, 1, 1], [1, 3, 1], [2, 0, 5], [3, 1, 6]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([7, 7, 7], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+        ),
+        (
+            np.array([[1], [2]], dtype=np.int64),
+            np.array([7, 8], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 1), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 3), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([9, 3, 7], dtype=np.int64),
+            np.array([100], dtype=np.int64),
+        ),
+    ],
+)
+@pytest.mark.parametrize("dtype", [np.int32, np.int64])
+@pytest.mark.parametrize("use_dyn", [True, False])
+def test_sparse_fill_empty_rows(
+    sparse_indices, sparse_values, dense_shape, default_value, dtype, use_dyn
+):
+    def ref_sparse_fill_empty_rows(
+        sparse_indices: np.ndarray,
+        sparse_values: np.ndarray,
+        dense_shape: np.ndarray,
+        default_value: np.ndarray,
+    ) -> None:
+        """
+        This function calculates the expected output of sparse_fill_empty_rows operator given the
+        inputs.
+        """
+
+        def check_add_rows(current_idx, limit_idx):
+            while current_idx < limit_idx:
+                new_sparse_indices.append([current_idx] + [0] * (num_cols - 1))
+                new_sparse_values.append(default_value[0])
+                empty_row_indicator[current_idx] = 1
+                current_idx += 1
+
+            return current_idx
+
+        current_idx = 0
+        new_sparse_indices = []
+        new_sparse_values = []
+        empty_row_indicator = [0 for _ in range(dense_shape[0])]
+        num_cols = sparse_indices.shape[1]
+        for sparse_row, sparse_value in zip(sparse_indices, sparse_values):
+            limit_idx = sparse_row[0]
+            current_idx = check_add_rows(current_idx, limit_idx)
+            new_sparse_indices.append(list(sparse_row))
+            new_sparse_values.append(sparse_value)
+            current_idx = limit_idx + 1
+
+        check_add_rows(current_idx, dense_shape[0])
+        return new_sparse_indices, new_sparse_values, empty_row_indicator
+
+    def verify_sparse_fill_empty_rows(
+        sparse_indices_np: np.ndarray,
+        sparse_values_np: np.ndarray,
+        dense_shape_np: np.ndarray,
+        default_value_np: np.ndarray,
+    ) -> None:
+        """
+        This function verifies the relay output of sparse_fill_empty_rows with its expected output.
+        """
+        if use_dyn:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                shape=[relay.Any(), relay.Any()],
+                dtype=str(sparse_indices_np.dtype),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                shape=[relay.Any()],
+                dtype=str(sparse_values_np.dtype),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                shape=[relay.Any()],
+                dtype=str(dense_shape_np.dtype),
+            )
+            default_value = relay.var(
+                "default_value",
+                shape=[relay.Any()],
+                dtype=str(default_value_np.dtype),
+            )
+        else:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                relay.TensorType(sparse_indices_np.shape, str(sparse_indices_np.dtype)),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                relay.TensorType(sparse_values_np.shape, str(sparse_values_np.dtype)),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                relay.TensorType(dense_shape_np.shape, str(dense_shape_np.dtype)),
+            )
+            default_value = relay.var(
+                "default_value",
+                relay.TensorType(default_value_np.shape, str(default_value_np.dtype)),
+            )
+        z = relay.sparse_fill_empty_rows(
+            sparse_indices, sparse_values, dense_shape, default_value
+        ).astuple()
+        func = relay.Function([sparse_indices, sparse_values, dense_shape, default_value], z)
+        ref_res = ref_sparse_fill_empty_rows(
+            sparse_indices_np,
+            sparse_values_np,
+            dense_shape_np,
+            default_value_np,
+        )
+        verify_func(

Review comment:
       I have set the output dtype to int64 irrespective of input . Is this an issue ? 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577121767



##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    Fill first column of the empty rows with default values for a sparse array.
+    It returns a TupleWrapper with 3 outputs
+    Parameters
+    ----------
+    sparse_indices : relay.Expr
+        A 2-D int64 tensor[N, ndims] of integers containing location of sparse values, where N is
+        the number of sparse values and n_dim is the number of dimensions of the dense_shape.
+        The inputs for this relay parameter must be in row-major order.

Review comment:
       Yes. Thank you. Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576425988



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)
+                for j in range(prev_index, index):
+                    new_sparse_indices[idx, 0] = int64(j)
+                    for k in range(1, int64(new_sparse_indices_shape[1])):
+                        new_sparse_indices[idx, k] = int64(0)
+                    empty_row_indicator[prev_index] = int64(1)
+                    new_sparse_values[idx] = default_value_
+                    idx += 1
+
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+
+        for i in range(
+            int64(sparse_indices[sparse_indices.shape[0] - 1, 0] + 1), int64(dense_shape[0])

Review comment:
       Because we must start filling the next row up until dense_shape[0] - 1, 
   For ex: 
   `Sparse_indices element -> [[0]]` and dense_shape element is `[2] `
   Then we have to fill in row "1" to make the final answer
   `new_sparse_indices -> [[0],[1]]  `




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576394924



##########
File path: tests/python/frontend/tensorflow/test_forward.py
##########
@@ -1812,6 +1812,109 @@ def test_forward_sparse_dense_matmul():
     )
 
 
+#######################################################################
+# SparseFillEmptyRows
+# ------------
+
+
+def _test_sparse_fill_empty_rows(indices_np, values_np, dense_shape_np, default_value_int, use_dyn):
+    with tf.Graph().as_default():
+        if use_dyn:
+            indices = tf.placeholder(shape=(None, None), dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=(None), dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=(None), dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+        else:
+            indices = tf.placeholder(shape=indices_np.shape, dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=values_np.shape, dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=dense_shape_np.shape, dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+
+        default_value = tf.placeholder(shape=(), dtype=values_np.dtype, name="default_value")
+        sp_input = tf.sparse.SparseTensor(indices=indices, values=values, dense_shape=dense_shape)
+        _ = tf.sparse.fill_empty_rows(sp_input, default_value, name="sparse_fill_empty_rows")
+        compare_tf_with_tvm(
+            [indices_np, values_np, dense_shape_np, default_value_int],
+            [indices.name, values.name, dense_shape.name, default_value.name],
+            [
+                "sparse_fill_empty_rows/SparseFillEmptyRows:0",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:1",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:2",
+            ],
+            mode="vm",
+        )
+
+
+@pytest.mark.parametrize(
+    "sparse_indices_np, sparse_values_np, dense_shape_np, default_value_int",

Review comment:
       Yes there is such a test. sparse_indices_np cannot be of size 0,0 it has to be 0,1 or 0,2 etc. And such tests are added below. Let me know if you meant something else. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577205883



##########
File path: tests/python/relay/dyn/test_dynamic_op_level3.py
##########
@@ -202,5 +209,152 @@ def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_
     verify_sparse_to_dense(1, 3, None, [5], [0, 3, 0, 0, 0])  # default value not specified
 
 
+@pytest.mark.parametrize(
+    "sparse_indices, sparse_values, dense_shape, default_value",
+    [
+        (
+            np.array([[0, 1], [0, 3], [2, 0], [3, 1]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([5, 6], dtype=np.int64),
+            np.array([10], dtype=np.int64),
+        ),
+        (
+            np.array([[1, 1, 1], [1, 3, 1], [2, 0, 5], [3, 1, 6]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([7, 7, 7], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+        ),
+        (
+            np.array([[1], [2]], dtype=np.int64),
+            np.array([7, 8], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 1), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 3), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([9, 3, 7], dtype=np.int64),
+            np.array([100], dtype=np.int64),
+        ),
+    ],
+)
+@pytest.mark.parametrize("dtype", [np.int32, np.int64])
+@pytest.mark.parametrize("use_dyn", [True, False])
+def test_sparse_fill_empty_rows(
+    sparse_indices, sparse_values, dense_shape, default_value, dtype, use_dyn
+):
+    def ref_sparse_fill_empty_rows(
+        sparse_indices: np.ndarray,
+        sparse_values: np.ndarray,
+        dense_shape: np.ndarray,
+        default_value: np.ndarray,
+    ) -> None:
+        """
+        This function calculates the expected output of sparse_fill_empty_rows operator given the
+        inputs.
+        """
+
+        def check_add_rows(current_idx, limit_idx):
+            while current_idx < limit_idx:
+                new_sparse_indices.append([current_idx] + [0] * (num_cols - 1))
+                new_sparse_values.append(default_value[0])
+                empty_row_indicator[current_idx] = 1
+                current_idx += 1
+
+            return current_idx
+
+        current_idx = 0
+        new_sparse_indices = []
+        new_sparse_values = []
+        empty_row_indicator = [0 for _ in range(dense_shape[0])]
+        num_cols = sparse_indices.shape[1]
+        for sparse_row, sparse_value in zip(sparse_indices, sparse_values):
+            limit_idx = sparse_row[0]
+            current_idx = check_add_rows(current_idx, limit_idx)
+            new_sparse_indices.append(list(sparse_row))
+            new_sparse_values.append(sparse_value)
+            current_idx = limit_idx + 1
+
+        check_add_rows(current_idx, dense_shape[0])
+        return new_sparse_indices, new_sparse_values, empty_row_indicator
+
+    def verify_sparse_fill_empty_rows(
+        sparse_indices_np: np.ndarray,
+        sparse_values_np: np.ndarray,
+        dense_shape_np: np.ndarray,
+        default_value_np: np.ndarray,
+    ) -> None:
+        """
+        This function verifies the relay output of sparse_fill_empty_rows with its expected output.
+        """
+        if use_dyn:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                shape=[relay.Any(), relay.Any()],
+                dtype=str(sparse_indices_np.dtype),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                shape=[relay.Any()],
+                dtype=str(sparse_values_np.dtype),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                shape=[relay.Any()],
+                dtype=str(dense_shape_np.dtype),
+            )
+            default_value = relay.var(
+                "default_value",
+                shape=[relay.Any()],
+                dtype=str(default_value_np.dtype),
+            )
+        else:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                relay.TensorType(sparse_indices_np.shape, str(sparse_indices_np.dtype)),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                relay.TensorType(sparse_values_np.shape, str(sparse_values_np.dtype)),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                relay.TensorType(dense_shape_np.shape, str(dense_shape_np.dtype)),
+            )
+            default_value = relay.var(
+                "default_value",
+                relay.TensorType(default_value_np.shape, str(default_value_np.dtype)),
+            )
+        z = relay.sparse_fill_empty_rows(
+            sparse_indices, sparse_values, dense_shape, default_value
+        ).astuple()
+        func = relay.Function([sparse_indices, sparse_values, dense_shape, default_value], z)
+        ref_res = ref_sparse_fill_empty_rows(
+            sparse_indices_np,
+            sparse_values_np,
+            dense_shape_np,
+            default_value_np,
+        )
+        verify_func(

Review comment:
       Done. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] tkonolige commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
tkonolige commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577138101



##########
File path: tests/python/relay/dyn/test_dynamic_op_level3.py
##########
@@ -202,5 +209,152 @@ def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_
     verify_sparse_to_dense(1, 3, None, [5], [0, 3, 0, 0, 0])  # default value not specified
 
 
+@pytest.mark.parametrize(
+    "sparse_indices, sparse_values, dense_shape, default_value",
+    [
+        (
+            np.array([[0, 1], [0, 3], [2, 0], [3, 1]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([5, 6], dtype=np.int64),
+            np.array([10], dtype=np.int64),
+        ),
+        (
+            np.array([[1, 1, 1], [1, 3, 1], [2, 0, 5], [3, 1, 6]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([7, 7, 7], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+        ),
+        (
+            np.array([[1], [2]], dtype=np.int64),
+            np.array([7, 8], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 1), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 3), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([9, 3, 7], dtype=np.int64),
+            np.array([100], dtype=np.int64),
+        ),
+    ],
+)
+@pytest.mark.parametrize("dtype", [np.int32, np.int64])
+@pytest.mark.parametrize("use_dyn", [True, False])
+def test_sparse_fill_empty_rows(
+    sparse_indices, sparse_values, dense_shape, default_value, dtype, use_dyn
+):
+    def ref_sparse_fill_empty_rows(
+        sparse_indices: np.ndarray,
+        sparse_values: np.ndarray,
+        dense_shape: np.ndarray,
+        default_value: np.ndarray,
+    ) -> None:
+        """
+        This function calculates the expected output of sparse_fill_empty_rows operator given the
+        inputs.
+        """
+
+        def check_add_rows(current_idx, limit_idx):
+            while current_idx < limit_idx:
+                new_sparse_indices.append([current_idx] + [0] * (num_cols - 1))
+                new_sparse_values.append(default_value[0])
+                empty_row_indicator[current_idx] = 1
+                current_idx += 1
+
+            return current_idx
+
+        current_idx = 0
+        new_sparse_indices = []
+        new_sparse_values = []
+        empty_row_indicator = [0 for _ in range(dense_shape[0])]
+        num_cols = sparse_indices.shape[1]
+        for sparse_row, sparse_value in zip(sparse_indices, sparse_values):
+            limit_idx = sparse_row[0]
+            current_idx = check_add_rows(current_idx, limit_idx)
+            new_sparse_indices.append(list(sparse_row))
+            new_sparse_values.append(sparse_value)
+            current_idx = limit_idx + 1
+
+        check_add_rows(current_idx, dense_shape[0])
+        return new_sparse_indices, new_sparse_values, empty_row_indicator
+
+    def verify_sparse_fill_empty_rows(
+        sparse_indices_np: np.ndarray,
+        sparse_values_np: np.ndarray,
+        dense_shape_np: np.ndarray,
+        default_value_np: np.ndarray,
+    ) -> None:
+        """
+        This function verifies the relay output of sparse_fill_empty_rows with its expected output.
+        """
+        if use_dyn:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                shape=[relay.Any(), relay.Any()],
+                dtype=str(sparse_indices_np.dtype),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                shape=[relay.Any()],
+                dtype=str(sparse_values_np.dtype),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                shape=[relay.Any()],
+                dtype=str(dense_shape_np.dtype),
+            )
+            default_value = relay.var(
+                "default_value",
+                shape=[relay.Any()],
+                dtype=str(default_value_np.dtype),
+            )
+        else:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                relay.TensorType(sparse_indices_np.shape, str(sparse_indices_np.dtype)),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                relay.TensorType(sparse_values_np.shape, str(sparse_values_np.dtype)),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                relay.TensorType(dense_shape_np.shape, str(dense_shape_np.dtype)),
+            )
+            default_value = relay.var(
+                "default_value",
+                relay.TensorType(default_value_np.shape, str(default_value_np.dtype)),
+            )
+        z = relay.sparse_fill_empty_rows(
+            sparse_indices, sparse_values, dense_shape, default_value
+        ).astuple()
+        func = relay.Function([sparse_indices, sparse_values, dense_shape, default_value], z)
+        ref_res = ref_sparse_fill_empty_rows(
+            sparse_indices_np,
+            sparse_values_np,
+            dense_shape_np,
+            default_value_np,
+        )
+        verify_func(

Review comment:
       Can you also verify that the output dtype is equal to the input dtype?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576394193



##########
File path: python/tvm/relay/op/_transform.py
##########
@@ -445,6 +465,44 @@ def argwhere_shape_func(attrs, inputs, out_ndims):
 _reg.register_shape_func("scatter_add", False, elemwise_shape_func)
 
 
+@script
+def _sparse_fill_empty_rows_shape_func(sparse_indices, dense_shape):
+
+    new_sparse_indices_shape = output_tensor((2,), "int64")
+    new_sparse_values_shape = output_tensor((1,), "int64")
+    empty_row_indicator_shape = output_tensor((1,), "int64")
+    num_dense_rows = int64(dense_shape[0])
+
+    if int64(sparse_indices.shape[0]) == int64(0):

Review comment:
       I have added test cases for fully dynamic input shapes and it works. Were you talking about something else ? 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] masahi merged pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
masahi merged pull request #7442:
URL: https://github.com/apache/tvm/pull/7442


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576393595



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name

Review comment:
       undefined-variable comes from the "int64" thing. 

##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,20 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    This op exactly follows the documentation here:
+    https://www.tensorflow.org/api_docs/python/tf/sparse/fill_empty_rows

Review comment:
       Sure. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576415157



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each dimension")
+    .add_argument("default_value", "Tensor",

Review comment:
       Resolving from offline conversation. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577125137



##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    Fill first column of the empty rows with default values for a sparse array.

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577123539



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,124 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, sparse_indices.dtype)
+    new_sparse_values = output_tensor(new_sparse_values_shape, sparse_values.dtype)
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    new_sparse_indices_row_id = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):  # Handle Empty Case
+        #  Fill all rows with default values
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        # Add rows with default value if first row id of sparse_indices is not a zero
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[new_sparse_indices_row_id, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[new_sparse_indices_row_id, k] = int64(0)
+
+            new_sparse_values[new_sparse_indices_row_id] = default_value_
+            empty_row_indicator[i] = int64(1)
+            new_sparse_indices_row_id += 1
+
+        # Iterate through sparse_indices and add rows if/when required
+        for i in range(0, int64(sparse_indices.shape[0])):
+            row_id = int64(sparse_indices[i, 0])
+            if i == 0:
+                # Add first row of input to output
+                new_sparse_indices[new_sparse_indices_row_id, 0] = row_id
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[new_sparse_indices_row_id, k] = int64(sparse_indices[i, k])
+                new_sparse_values[new_sparse_indices_row_id] = int64(sparse_values[i])
+                empty_row_indicator[row_id] = int64(0)
+                new_sparse_indices_row_id += 1
+            else:
+                prev_row_id = int64(sparse_indices[i - 1, 0] + 1)
+                # Since input is in row-major order, add rows between prev_row_id and row_id
+                for j in range(prev_row_id, row_id):
+                    new_sparse_indices[new_sparse_indices_row_id, 0] = int64(j)
+                    for k in range(1, int64(new_sparse_indices_shape[1])):
+                        new_sparse_indices[new_sparse_indices_row_id, k] = int64(0)
+                    empty_row_indicator[prev_row_id] = int64(1)
+                    new_sparse_values[new_sparse_indices_row_id] = default_value_
+                    new_sparse_indices_row_id += 1
+
+                # Add current row to output
+                new_sparse_indices[new_sparse_indices_row_id, 0] = row_id
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[new_sparse_indices_row_id, k] = int64(sparse_indices[i, k])
+                new_sparse_values[new_sparse_indices_row_id] = int64(sparse_values[i])
+                empty_row_indicator[row_id] = int64(0)
+                new_sparse_indices_row_id += 1

Review comment:
       Thank you . Done. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576429000



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index

Review comment:
       I have added some comments. Please let me know if that helps in understanding :) 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#issuecomment-780115708


   @tkonolige . Addressed all your comments and added test cases for int32. Please re-review. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#issuecomment-778547159


   @mbrookhart @tkonolige Please review this Op. it has been completely rewritten to address the issues in the previous PR and has also been tested with the customer model. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577124344



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,51 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5) << "SparseFillEmptyRowsRel expects 5 inputs but " << types.size()
+                             << "provided";
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(
+        R"code(Fill empty rows of a sparse tensor with a default value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values. COO Format")
+    .add_argument(
+        "sparse_values", "Tensor",
+        "A 1-D tensor[N] which supplies the values for each element in indices. COO Format")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each "
+                  "dimension. COO Format")
+    .add_argument(
+        "default_value", "Tensor",
+        "The value to fill for empty rows, with the same type as sparse_values. COO Format")

Review comment:
       Done.

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,51 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5) << "SparseFillEmptyRowsRel expects 5 inputs but " << types.size()
+                             << "provided";
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(
+        R"code(Fill empty rows of a sparse tensor with a default value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values. COO Format")
+    .add_argument(
+        "sparse_values", "Tensor",
+        "A 1-D tensor[N] which supplies the values for each element in indices. COO Format")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each "
+                  "dimension. COO Format")

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 edited a comment on pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 edited a comment on pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#issuecomment-778547159


   @mbrookhart @tkonolige Please review this Op. it has been completely rewritten to address the many issues in the previous PR and has also been tested with the customer model. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 edited a comment on pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 edited a comment on pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#issuecomment-778547159


   @mbrookhart @tkonolige Please review this Op. it has been completely rewritten to address the many issues in the previous PR #7126 and has also been tested with the customer model. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576428627



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",

Review comment:
       Done. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576413984



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)

Review comment:
       yes, I have specified this in the op documentation. That the indices should be in row-major order. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577141789



##########
File path: tests/python/relay/dyn/test_dynamic_op_level3.py
##########
@@ -202,5 +209,152 @@ def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_
     verify_sparse_to_dense(1, 3, None, [5], [0, 3, 0, 0, 0])  # default value not specified
 
 
+@pytest.mark.parametrize(
+    "sparse_indices, sparse_values, dense_shape, default_value",
+    [
+        (
+            np.array([[0, 1], [0, 3], [2, 0], [3, 1]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([5, 6], dtype=np.int64),
+            np.array([10], dtype=np.int64),
+        ),
+        (
+            np.array([[1, 1, 1], [1, 3, 1], [2, 0, 5], [3, 1, 6]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([7, 7, 7], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+        ),
+        (
+            np.array([[1], [2]], dtype=np.int64),
+            np.array([7, 8], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 1), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 3), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([9, 3, 7], dtype=np.int64),
+            np.array([100], dtype=np.int64),
+        ),
+    ],
+)
+@pytest.mark.parametrize("dtype", [np.int32, np.int64])
+@pytest.mark.parametrize("use_dyn", [True, False])
+def test_sparse_fill_empty_rows(
+    sparse_indices, sparse_values, dense_shape, default_value, dtype, use_dyn
+):
+    def ref_sparse_fill_empty_rows(
+        sparse_indices: np.ndarray,
+        sparse_values: np.ndarray,
+        dense_shape: np.ndarray,
+        default_value: np.ndarray,
+    ) -> None:
+        """
+        This function calculates the expected output of sparse_fill_empty_rows operator given the
+        inputs.
+        """
+
+        def check_add_rows(current_idx, limit_idx):
+            while current_idx < limit_idx:
+                new_sparse_indices.append([current_idx] + [0] * (num_cols - 1))
+                new_sparse_values.append(default_value[0])
+                empty_row_indicator[current_idx] = 1
+                current_idx += 1
+
+            return current_idx
+
+        current_idx = 0
+        new_sparse_indices = []
+        new_sparse_values = []
+        empty_row_indicator = [0 for _ in range(dense_shape[0])]
+        num_cols = sparse_indices.shape[1]
+        for sparse_row, sparse_value in zip(sparse_indices, sparse_values):
+            limit_idx = sparse_row[0]
+            current_idx = check_add_rows(current_idx, limit_idx)
+            new_sparse_indices.append(list(sparse_row))
+            new_sparse_values.append(sparse_value)
+            current_idx = limit_idx + 1
+
+        check_add_rows(current_idx, dense_shape[0])
+        return new_sparse_indices, new_sparse_values, empty_row_indicator
+
+    def verify_sparse_fill_empty_rows(
+        sparse_indices_np: np.ndarray,
+        sparse_values_np: np.ndarray,
+        dense_shape_np: np.ndarray,
+        default_value_np: np.ndarray,
+    ) -> None:
+        """
+        This function verifies the relay output of sparse_fill_empty_rows with its expected output.
+        """
+        if use_dyn:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                shape=[relay.Any(), relay.Any()],
+                dtype=str(sparse_indices_np.dtype),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                shape=[relay.Any()],
+                dtype=str(sparse_values_np.dtype),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                shape=[relay.Any()],
+                dtype=str(dense_shape_np.dtype),
+            )
+            default_value = relay.var(
+                "default_value",
+                shape=[relay.Any()],
+                dtype=str(default_value_np.dtype),
+            )
+        else:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                relay.TensorType(sparse_indices_np.shape, str(sparse_indices_np.dtype)),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                relay.TensorType(sparse_values_np.shape, str(sparse_values_np.dtype)),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                relay.TensorType(dense_shape_np.shape, str(dense_shape_np.dtype)),
+            )
+            default_value = relay.var(
+                "default_value",
+                relay.TensorType(default_value_np.shape, str(default_value_np.dtype)),
+            )
+        z = relay.sparse_fill_empty_rows(
+            sparse_indices, sparse_values, dense_shape, default_value
+        ).astuple()
+        func = relay.Function([sparse_indices, sparse_values, dense_shape, default_value], z)
+        ref_res = ref_sparse_fill_empty_rows(
+            sparse_indices_np,
+            sparse_values_np,
+            dense_shape_np,
+            default_value_np,
+        )
+        verify_func(

Review comment:
       I actually don't know how to store write int32 / int64 in a parametrized way in hybrid script. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577123376



##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576468584



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);

Review comment:
       Done.

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576396099



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",

Review comment:
       I just copied the terminology from TF documentation. What do you think would be a good name ? 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576395782



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each dimension")
+    .add_argument("default_value", "Tensor",

Review comment:
       Actually I don't know how to directly work with scalars ? So I made it a tensor of size [1]. I have seen other parts of the codebase use attrs etc. for scalar ints/floats but those dont apply here. Can you share with me an example snippet ?  




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] tkonolige commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
tkonolige commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576357085



##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,20 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    This op exactly follows the documentation here:
+    https://www.tensorflow.org/api_docs/python/tf/sparse/fill_empty_rows

Review comment:
       Instead of linking to the tensorflow documentation, could you write out what this op does. Please include parameters, return values, and examples.

##########
File path: python/tvm/relay/op/_transform.py
##########
@@ -445,6 +465,44 @@ def argwhere_shape_func(attrs, inputs, out_ndims):
 _reg.register_shape_func("scatter_add", False, elemwise_shape_func)
 
 
+@script
+def _sparse_fill_empty_rows_shape_func(sparse_indices, dense_shape):
+
+    new_sparse_indices_shape = output_tensor((2,), "int64")
+    new_sparse_values_shape = output_tensor((1,), "int64")
+    empty_row_indicator_shape = output_tensor((1,), "int64")
+    num_dense_rows = int64(dense_shape[0])
+
+    if int64(sparse_indices.shape[0]) == int64(0):

Review comment:
       This shape function does not support dynamic input shapes? Are you planning on using this op after any dynamic operations?

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)

Review comment:
       ```suggestion
       .describe(R"code(Fill empty rows of a sparse tensor with a default value.)code" TVM_ADD_FILELINE)
   ```
   

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",

Review comment:
       Dense shape is a bit of a misnomer. This is just the actual shape of the sparse tensor.

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name

Review comment:
       Where are you getting the undefined-variable warning? This seems like a dangerous lint to disable.

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index

Review comment:
       Also, this line can be removed if you start the loop below at 0.

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")

Review comment:
       Shouldn't the output type match the input type?

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);

Review comment:
       Could you put an error message on this.

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",

Review comment:
       Please document the format. Looking at the code, it seems as if the format is COO?

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)

Review comment:
       If you structure this loop as handling indices from `sparse_indices[i,0]` to `sparse_indices[i+1,0]`, you can simplify the code by removing some of these index copying blocks.

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index

Review comment:
       nit: you're using two variables called index (`idx` and `index`). Could you make the names a little more specific.

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each dimension")
+    .add_argument("default_value", "Tensor",

Review comment:
       Shouldn't this be a scalar?

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)
+                for j in range(prev_index, index):
+                    new_sparse_indices[idx, 0] = int64(j)
+                    for k in range(1, int64(new_sparse_indices_shape[1])):
+                        new_sparse_indices[idx, k] = int64(0)
+                    empty_row_indicator[prev_index] = int64(1)
+                    new_sparse_values[idx] = default_value_
+                    idx += 1
+
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+
+        for i in range(
+            int64(sparse_indices[sparse_indices.shape[0] - 1, 0] + 1), int64(dense_shape[0])

Review comment:
       Why is there a `+1` in this expression? If the sparse indices have size 1 and the dense shape is size 2, then we wouldn't add any elements.

##########
File path: tests/python/frontend/tensorflow/test_forward.py
##########
@@ -1812,6 +1812,109 @@ def test_forward_sparse_dense_matmul():
     )
 
 
+#######################################################################
+# SparseFillEmptyRows
+# ------------
+
+
+def _test_sparse_fill_empty_rows(indices_np, values_np, dense_shape_np, default_value_int, use_dyn):
+    with tf.Graph().as_default():
+        if use_dyn:
+            indices = tf.placeholder(shape=(None, None), dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=(None), dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=(None), dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+        else:
+            indices = tf.placeholder(shape=indices_np.shape, dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=values_np.shape, dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=dense_shape_np.shape, dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+
+        default_value = tf.placeholder(shape=(), dtype=values_np.dtype, name="default_value")
+        sp_input = tf.sparse.SparseTensor(indices=indices, values=values, dense_shape=dense_shape)
+        _ = tf.sparse.fill_empty_rows(sp_input, default_value, name="sparse_fill_empty_rows")
+        compare_tf_with_tvm(
+            [indices_np, values_np, dense_shape_np, default_value_int],
+            [indices.name, values.name, dense_shape.name, default_value.name],
+            [
+                "sparse_fill_empty_rows/SparseFillEmptyRows:0",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:1",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:2",
+            ],
+            mode="vm",
+        )
+
+
+@pytest.mark.parametrize(
+    "sparse_indices_np, sparse_values_np, dense_shape_np, default_value_int",
+    [
+        (
+            np.array([[1, 1], [0, 3], [0, 1], [2, 0], [3, 1]], dtype=np.int64),

Review comment:
       This doesn't appear to be in the correct format. Shouldn't the first column be in sorted order?

##########
File path: tests/python/frontend/tensorflow/test_forward.py
##########
@@ -1812,6 +1812,109 @@ def test_forward_sparse_dense_matmul():
     )
 
 
+#######################################################################
+# SparseFillEmptyRows
+# ------------
+
+
+def _test_sparse_fill_empty_rows(indices_np, values_np, dense_shape_np, default_value_int, use_dyn):
+    with tf.Graph().as_default():
+        if use_dyn:
+            indices = tf.placeholder(shape=(None, None), dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=(None), dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=(None), dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+        else:
+            indices = tf.placeholder(shape=indices_np.shape, dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=values_np.shape, dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=dense_shape_np.shape, dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+
+        default_value = tf.placeholder(shape=(), dtype=values_np.dtype, name="default_value")
+        sp_input = tf.sparse.SparseTensor(indices=indices, values=values, dense_shape=dense_shape)
+        _ = tf.sparse.fill_empty_rows(sp_input, default_value, name="sparse_fill_empty_rows")
+        compare_tf_with_tvm(
+            [indices_np, values_np, dense_shape_np, default_value_int],
+            [indices.name, values.name, dense_shape.name, default_value.name],
+            [
+                "sparse_fill_empty_rows/SparseFillEmptyRows:0",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:1",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:2",
+            ],
+            mode="vm",
+        )
+
+
+@pytest.mark.parametrize(
+    "sparse_indices_np, sparse_values_np, dense_shape_np, default_value_int",

Review comment:
       Could you add a test where the `sparse_indices_np` array is empty?

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)

Review comment:
       Also, it appears that you require the indices to be in sorted order?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] tkonolige commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
tkonolige commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577141818



##########
File path: tests/python/relay/dyn/test_dynamic_op_level3.py
##########
@@ -202,5 +209,152 @@ def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_
     verify_sparse_to_dense(1, 3, None, [5], [0, 3, 0, 0, 0])  # default value not specified
 
 
+@pytest.mark.parametrize(
+    "sparse_indices, sparse_values, dense_shape, default_value",
+    [
+        (
+            np.array([[0, 1], [0, 3], [2, 0], [3, 1]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([5, 6], dtype=np.int64),
+            np.array([10], dtype=np.int64),
+        ),
+        (
+            np.array([[1, 1, 1], [1, 3, 1], [2, 0, 5], [3, 1, 6]], dtype=np.int64),
+            np.array([1, 2, 3, 4], dtype=np.int64),
+            np.array([7, 7, 7], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+        ),
+        (
+            np.array([[1], [2]], dtype=np.int64),
+            np.array([7, 8], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 1), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([5], dtype=np.int64),
+            np.array([4], dtype=np.int64),
+        ),
+        (
+            np.ones((0, 3), dtype=np.int64),
+            np.array([], dtype=np.int64),
+            np.array([9, 3, 7], dtype=np.int64),
+            np.array([100], dtype=np.int64),
+        ),
+    ],
+)
+@pytest.mark.parametrize("dtype", [np.int32, np.int64])
+@pytest.mark.parametrize("use_dyn", [True, False])
+def test_sparse_fill_empty_rows(
+    sparse_indices, sparse_values, dense_shape, default_value, dtype, use_dyn
+):
+    def ref_sparse_fill_empty_rows(
+        sparse_indices: np.ndarray,
+        sparse_values: np.ndarray,
+        dense_shape: np.ndarray,
+        default_value: np.ndarray,
+    ) -> None:
+        """
+        This function calculates the expected output of sparse_fill_empty_rows operator given the
+        inputs.
+        """
+
+        def check_add_rows(current_idx, limit_idx):
+            while current_idx < limit_idx:
+                new_sparse_indices.append([current_idx] + [0] * (num_cols - 1))
+                new_sparse_values.append(default_value[0])
+                empty_row_indicator[current_idx] = 1
+                current_idx += 1
+
+            return current_idx
+
+        current_idx = 0
+        new_sparse_indices = []
+        new_sparse_values = []
+        empty_row_indicator = [0 for _ in range(dense_shape[0])]
+        num_cols = sparse_indices.shape[1]
+        for sparse_row, sparse_value in zip(sparse_indices, sparse_values):
+            limit_idx = sparse_row[0]
+            current_idx = check_add_rows(current_idx, limit_idx)
+            new_sparse_indices.append(list(sparse_row))
+            new_sparse_values.append(sparse_value)
+            current_idx = limit_idx + 1
+
+        check_add_rows(current_idx, dense_shape[0])
+        return new_sparse_indices, new_sparse_values, empty_row_indicator
+
+    def verify_sparse_fill_empty_rows(
+        sparse_indices_np: np.ndarray,
+        sparse_values_np: np.ndarray,
+        dense_shape_np: np.ndarray,
+        default_value_np: np.ndarray,
+    ) -> None:
+        """
+        This function verifies the relay output of sparse_fill_empty_rows with its expected output.
+        """
+        if use_dyn:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                shape=[relay.Any(), relay.Any()],
+                dtype=str(sparse_indices_np.dtype),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                shape=[relay.Any()],
+                dtype=str(sparse_values_np.dtype),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                shape=[relay.Any()],
+                dtype=str(dense_shape_np.dtype),
+            )
+            default_value = relay.var(
+                "default_value",
+                shape=[relay.Any()],
+                dtype=str(default_value_np.dtype),
+            )
+        else:
+            sparse_indices = relay.var(
+                "sparse_indices",
+                relay.TensorType(sparse_indices_np.shape, str(sparse_indices_np.dtype)),
+            )
+            sparse_values = relay.var(
+                "sparse_values",
+                relay.TensorType(sparse_values_np.shape, str(sparse_values_np.dtype)),
+            )
+            dense_shape = relay.var(
+                "dense_shape",
+                relay.TensorType(dense_shape_np.shape, str(dense_shape_np.dtype)),
+            )
+            default_value = relay.var(
+                "default_value",
+                relay.TensorType(default_value_np.shape, str(default_value_np.dtype)),
+            )
+        z = relay.sparse_fill_empty_rows(
+            sparse_indices, sparse_values, dense_shape, default_value
+        ).astuple()
+        func = relay.Function([sparse_indices, sparse_values, dense_shape, default_value], z)
+        ref_res = ref_sparse_fill_empty_rows(
+            sparse_indices_np,
+            sparse_values_np,
+            dense_shape_np,
+            default_value_np,
+        )
+        verify_func(

Review comment:
       The output type should match the input type.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576393005



##########
File path: tests/python/frontend/tensorflow/test_forward.py
##########
@@ -1812,6 +1812,109 @@ def test_forward_sparse_dense_matmul():
     )
 
 
+#######################################################################
+# SparseFillEmptyRows
+# ------------
+
+
+def _test_sparse_fill_empty_rows(indices_np, values_np, dense_shape_np, default_value_int, use_dyn):
+    with tf.Graph().as_default():
+        if use_dyn:
+            indices = tf.placeholder(shape=(None, None), dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=(None), dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=(None), dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+        else:
+            indices = tf.placeholder(shape=indices_np.shape, dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=values_np.shape, dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=dense_shape_np.shape, dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+
+        default_value = tf.placeholder(shape=(), dtype=values_np.dtype, name="default_value")
+        sp_input = tf.sparse.SparseTensor(indices=indices, values=values, dense_shape=dense_shape)
+        _ = tf.sparse.fill_empty_rows(sp_input, default_value, name="sparse_fill_empty_rows")
+        compare_tf_with_tvm(
+            [indices_np, values_np, dense_shape_np, default_value_int],
+            [indices.name, values.name, dense_shape.name, default_value.name],
+            [
+                "sparse_fill_empty_rows/SparseFillEmptyRows:0",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:1",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:2",
+            ],
+            mode="vm",
+        )
+
+
+@pytest.mark.parametrize(
+    "sparse_indices_np, sparse_values_np, dense_shape_np, default_value_int",
+    [
+        (
+            np.array([[1, 1], [0, 3], [0, 1], [2, 0], [3, 1]], dtype=np.int64),

Review comment:
       So if it is not in sorted order, the op is supposed to be sort it. I am doing this in the frontend code. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576426175



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)
+                for j in range(prev_index, index):
+                    new_sparse_indices[idx, 0] = int64(j)
+                    for k in range(1, int64(new_sparse_indices_shape[1])):
+                        new_sparse_indices[idx, k] = int64(0)
+                    empty_row_indicator[prev_index] = int64(1)
+                    new_sparse_values[idx] = default_value_
+                    idx += 1
+
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+
+        for i in range(
+            int64(sparse_indices[sparse_indices.shape[0] - 1, 0] + 1), int64(dense_shape[0])

Review comment:
       I have also added comments to shape func and this file so that should help. Let me know :) 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576413801



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576393102



##########
File path: tests/python/frontend/tensorflow/test_forward.py
##########
@@ -1812,6 +1812,109 @@ def test_forward_sparse_dense_matmul():
     )
 
 
+#######################################################################
+# SparseFillEmptyRows
+# ------------
+
+
+def _test_sparse_fill_empty_rows(indices_np, values_np, dense_shape_np, default_value_int, use_dyn):
+    with tf.Graph().as_default():
+        if use_dyn:
+            indices = tf.placeholder(shape=(None, None), dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=(None), dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=(None), dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+        else:
+            indices = tf.placeholder(shape=indices_np.shape, dtype=indices_np.dtype, name="indices")
+            values = tf.placeholder(shape=values_np.shape, dtype=values_np.dtype, name="values")
+            dense_shape = tf.placeholder(
+                shape=dense_shape_np.shape, dtype=dense_shape_np.dtype, name="dense_shape"
+            )
+
+        default_value = tf.placeholder(shape=(), dtype=values_np.dtype, name="default_value")
+        sp_input = tf.sparse.SparseTensor(indices=indices, values=values, dense_shape=dense_shape)
+        _ = tf.sparse.fill_empty_rows(sp_input, default_value, name="sparse_fill_empty_rows")
+        compare_tf_with_tvm(
+            [indices_np, values_np, dense_shape_np, default_value_int],
+            [indices.name, values.name, dense_shape.name, default_value.name],
+            [
+                "sparse_fill_empty_rows/SparseFillEmptyRows:0",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:1",
+                "sparse_fill_empty_rows/SparseFillEmptyRows:2",
+            ],
+            mode="vm",
+        )
+
+
+@pytest.mark.parametrize(
+    "sparse_indices_np, sparse_values_np, dense_shape_np, default_value_int",
+    [
+        (
+            np.array([[1, 1], [0, 3], [0, 1], [2, 0], [3, 1]], dtype=np.int64),

Review comment:
       So this is a valid test case. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r577126119



##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    Fill first column of the empty rows with default values for a sparse array.
+    It returns a TupleWrapper with 3 outputs
+    Parameters
+    ----------
+    sparse_indices : relay.Expr
+        A 2-D int64 tensor[N, ndims] of integers containing location of sparse values, where N is
+        the number of sparse values and n_dim is the number of dimensions of the dense_shape.
+        The inputs for this relay parameter must be in row-major order.
+    sparse_values : relay.Expr
+        A 1-D int64 tensor[N] containing the sparse values for the sparse indices.
+    dense_shape : relay.Expr
+        A 1-D int64 tensor[ndims] which contains shape of the dense output tensor.
+    default_value : relay.Expr
+        A 1-D tensor[1] containing the default value for the remaining locations.
+    Returns
+    -------
+    new_sparse_indices : relay.Expr
+        A 2-D int64 tensor[?, ndims] of integers containing location of new sparse
+        indices
+    new_sparse_values : relay.Expr
+        A 1-D int64 tensor[?] containing the sparse values for the sparse indices.
+    empty_row_indicator : relay.Expr
+        A 1-D int64 tensor[dense_shape[0]] filled with zeros and ones
+        indicating whether the particular row is empty or full respectively
+
+    Note:

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576428806



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#issuecomment-779454320


   @tkonolige  I have addressed all your comments and added the relevant documentation/changes wherever you asked. Please re-review. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576393426



##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")

Review comment:
       The input type is also "int64" but I see what you are saying, I will change it to parameterize it from the input type. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] tkonolige commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
tkonolige commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576945730



##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    Fill first column of the empty rows with default values for a sparse array.

Review comment:
       ```suggestion
       Fill rows in a sparse matrix that do no contain any values. Values are placed in the first column of empty rows.
   ```

##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):

Review comment:
       Please document that this takes a sparse matrix in coordinate (COO) format.

##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    Fill first column of the empty rows with default values for a sparse array.
+    It returns a TupleWrapper with 3 outputs
+    Parameters
+    ----------
+    sparse_indices : relay.Expr
+        A 2-D int64 tensor[N, ndims] of integers containing location of sparse values, where N is
+        the number of sparse values and n_dim is the number of dimensions of the dense_shape.
+        The inputs for this relay parameter must be in row-major order.
+    sparse_values : relay.Expr
+        A 1-D int64 tensor[N] containing the sparse values for the sparse indices.
+    dense_shape : relay.Expr
+        A 1-D int64 tensor[ndims] which contains shape of the dense output tensor.
+    default_value : relay.Expr
+        A 1-D tensor[1] containing the default value for the remaining locations.
+    Returns
+    -------
+    new_sparse_indices : relay.Expr
+        A 2-D int64 tensor[?, ndims] of integers containing location of new sparse
+        indices
+    new_sparse_values : relay.Expr
+        A 1-D int64 tensor[?] containing the sparse values for the sparse indices.
+    empty_row_indicator : relay.Expr
+        A 1-D int64 tensor[dense_shape[0]] filled with zeros and ones
+        indicating whether the particular row is empty or full respectively
+
+    Note:

Review comment:
       ```suggestion
       Note
       ----
   ```

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,51 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5) << "SparseFillEmptyRowsRel expects 5 inputs but " << types.size()
+                             << "provided";
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(
+        R"code(Fill empty rows of a sparse tensor with a default value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values. COO Format")
+    .add_argument(
+        "sparse_values", "Tensor",
+        "A 1-D tensor[N] which supplies the values for each element in indices. COO Format")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each "
+                  "dimension. COO Format")
+    .add_argument(
+        "default_value", "Tensor",
+        "The value to fill for empty rows, with the same type as sparse_values. COO Format")

Review comment:
       ```suggestion
           "The value to fill for empty rows, with the same type as sparse_values.")
   ```

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, "int64")
+    new_sparse_values = output_tensor(new_sparse_values_shape, "int64")
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    idx = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[idx, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[idx, k] = int64(0)
+
+            new_sparse_values[idx] = default_value_
+            empty_row_indicator[i] = int64(1)
+            idx += 1
+
+        for i in range(0, int64(sparse_indices.shape[0])):
+            index = int64(sparse_indices[i, 0])
+            if i == 0:
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+            else:
+                prev_index = int64(sparse_indices[i - 1, 0] + 1)
+                for j in range(prev_index, index):
+                    new_sparse_indices[idx, 0] = int64(j)
+                    for k in range(1, int64(new_sparse_indices_shape[1])):
+                        new_sparse_indices[idx, k] = int64(0)
+                    empty_row_indicator[prev_index] = int64(1)
+                    new_sparse_values[idx] = default_value_
+                    idx += 1
+
+                new_sparse_indices[idx, 0] = index
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[idx, k] = int64(sparse_indices[i, k])
+                new_sparse_values[idx] = int64(sparse_values[i])
+                empty_row_indicator[index] = int64(0)
+                idx += 1
+
+        for i in range(
+            int64(sparse_indices[sparse_indices.shape[0] - 1, 0] + 1), int64(dense_shape[0])

Review comment:
       Sorry, I got it in my head that we were in CSR format. This looks correct.

##########
File path: python/tvm/topi/sparse_fill_empty_rows.py
##########
@@ -0,0 +1,124 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHnew_sparse_indices WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=no-else-return, too-many-locals, too-many-arguments, too-many-branches
+# pylint: disable=undefined-variable, invalid-name
+"""SparseFillEmptyRows operator"""
+from ..te import hybrid
+
+
+@hybrid.script
+def _sparse_fill_empty_rows(
+    sparse_indices,
+    sparse_values,
+    dense_shape,
+    default_value,
+    new_sparse_indices_shape,
+    new_sparse_values_shape,
+    empty_row_indicator_shape,
+):
+    default_value_ = int64(default_value[0])
+    new_sparse_indices = output_tensor(new_sparse_indices_shape, sparse_indices.dtype)
+    new_sparse_values = output_tensor(new_sparse_values_shape, sparse_values.dtype)
+    empty_row_indicator = output_tensor(empty_row_indicator_shape, "int64")
+    new_sparse_indices_row_id = 0
+
+    if int64(sparse_indices.shape[0]) == int64(0):  # Handle Empty Case
+        #  Fill all rows with default values
+        for i in range(0, int64(new_sparse_indices_shape[0])):
+            new_sparse_indices[i, 0] = int64(i)
+            new_sparse_values[i] = default_value_
+            empty_row_indicator[i] = int64(1)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[i, k] = int64(0)
+
+        return (new_sparse_indices, new_sparse_values, empty_row_indicator)
+
+    else:
+        # Add rows with default value if first row id of sparse_indices is not a zero
+        for i in range(0, int64(sparse_indices[0, 0])):
+            new_sparse_indices[new_sparse_indices_row_id, 0] = int64(i)
+            for k in range(1, int64(new_sparse_indices_shape[1])):
+                new_sparse_indices[new_sparse_indices_row_id, k] = int64(0)
+
+            new_sparse_values[new_sparse_indices_row_id] = default_value_
+            empty_row_indicator[i] = int64(1)
+            new_sparse_indices_row_id += 1
+
+        # Iterate through sparse_indices and add rows if/when required
+        for i in range(0, int64(sparse_indices.shape[0])):
+            row_id = int64(sparse_indices[i, 0])
+            if i == 0:
+                # Add first row of input to output
+                new_sparse_indices[new_sparse_indices_row_id, 0] = row_id
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[new_sparse_indices_row_id, k] = int64(sparse_indices[i, k])
+                new_sparse_values[new_sparse_indices_row_id] = int64(sparse_values[i])
+                empty_row_indicator[row_id] = int64(0)
+                new_sparse_indices_row_id += 1
+            else:
+                prev_row_id = int64(sparse_indices[i - 1, 0] + 1)
+                # Since input is in row-major order, add rows between prev_row_id and row_id
+                for j in range(prev_row_id, row_id):
+                    new_sparse_indices[new_sparse_indices_row_id, 0] = int64(j)
+                    for k in range(1, int64(new_sparse_indices_shape[1])):
+                        new_sparse_indices[new_sparse_indices_row_id, k] = int64(0)
+                    empty_row_indicator[prev_row_id] = int64(1)
+                    new_sparse_values[new_sparse_indices_row_id] = default_value_
+                    new_sparse_indices_row_id += 1
+
+                # Add current row to output
+                new_sparse_indices[new_sparse_indices_row_id, 0] = row_id
+                for k in range(1, int64(new_sparse_indices_shape[1])):
+                    new_sparse_indices[new_sparse_indices_row_id, k] = int64(sparse_indices[i, k])
+                new_sparse_values[new_sparse_indices_row_id] = int64(sparse_values[i])
+                empty_row_indicator[row_id] = int64(0)
+                new_sparse_indices_row_id += 1

Review comment:
       You can simplify the logic here.
   ```suggestion
           # Iterate through sparse_indices and add rows if/when required
           for i in range(0, int64(sparse_indices.shape[0])):
               if i == 0:
                   prev_row_id = int64(0)
               else:
                   prev_row_id = int64(sparse_indices[i - 1, 0] + 1)
               row_id = int64(sparse_indices[i, 0])
               # Since input is in row-major order, add rows between prev_row_id and row_id
               for j in range(prev_row_id, row_id):
                   new_sparse_indices[new_sparse_indices_row_id, 0] = int64(j)
                   for k in range(1, int64(new_sparse_indices_shape[1])):
                       new_sparse_indices[new_sparse_indices_row_id, k] = int64(0)
                   empty_row_indicator[prev_row_id] = int64(1)
                   new_sparse_values[new_sparse_indices_row_id] = default_value_
                   new_sparse_indices_row_id += 1
   
               # Add current element to output
               new_sparse_indices[new_sparse_indices_row_id, 0] = row_id
               for k in range(1, int64(new_sparse_indices_shape[1])):
                   new_sparse_indices[new_sparse_indices_row_id, k] = int64(sparse_indices[i, k])
               new_sparse_values[new_sparse_indices_row_id] = int64(sparse_values[i])
               empty_row_indicator[row_id] = int64(0)
               new_sparse_indices_row_id += 1
   ```

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,51 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5) << "SparseFillEmptyRowsRel expects 5 inputs but " << types.size()
+                             << "provided";
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(
+        R"code(Fill empty rows of a sparse tensor with a default value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values. COO Format")
+    .add_argument(
+        "sparse_values", "Tensor",
+        "A 1-D tensor[N] which supplies the values for each element in indices. COO Format")
+    .add_argument("dense_shape", "Tensor",
+                  "A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the"
+                  "sparse tensor. Takes a list indicating the number of elements in each "
+                  "dimension. COO Format")

Review comment:
       ```suggestion
                     "dimension.")
   ```

##########
File path: python/tvm/relay/op/transform.py
##########
@@ -1322,6 +1322,71 @@ def adv_index(inputs):
     return _make.adv_index(Tuple(inputs))
 
 
+def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
+    """
+    Fill first column of the empty rows with default values for a sparse array.
+    It returns a TupleWrapper with 3 outputs
+    Parameters
+    ----------
+    sparse_indices : relay.Expr
+        A 2-D int64 tensor[N, ndims] of integers containing location of sparse values, where N is
+        the number of sparse values and n_dim is the number of dimensions of the dense_shape.
+        The inputs for this relay parameter must be in row-major order.

Review comment:
       Row major order means that the first dimension of the array indicates the row indices. I think what you want to say here is that `sparse_indices` must be sorted such that the first column is in ascending order.

##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",

Review comment:
       Sure, we can keep it.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [tvm] codeislife99 commented on a change in pull request #7442: SparseFillEmptyRows Op

Posted by GitBox <gi...@apache.org>.
codeislife99 commented on a change in pull request #7442:
URL: https://github.com/apache/tvm/pull/7442#discussion_r576454541



##########
File path: src/relay/op/tensor/transform.cc
##########
@@ -1584,6 +1584,47 @@ RELAY_REGISTER_OP("repeat")
     .set_attr<FTVMCompute>("FTVMCompute", RepeatCompute)
     .set_attr<TOpPattern>("TOpPattern", kBroadcast);
 
+bool SparseFillEmptyRowsRel(const Array<Type>& types, int num_inputs, const Attrs& attrs,
+                            const TypeReporter& reporter) {
+  // types: [sparse_indices, sparse_values, dense_shape, default_value, result]
+  ICHECK_EQ(types.size(), 5);
+  std::vector<Type> fields;
+  auto sparse_indices = types[0].as<TensorTypeNode>();
+  auto ndims = sparse_indices->shape[1];
+  fields.push_back(TensorType(Array<PrimExpr>{Any(), ndims}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  fields.push_back(TensorType(Array<PrimExpr>{Any()}, tvm::DataType::Int(64)));
+  reporter->Assign(types[types.size() - 1], TupleType(Array<Type>(fields)));
+  return true;
+}
+
+Expr MakeSparseFillEmptyRows(Expr sparse_indices, Expr sparse_values, Expr dense_shape,
+                             Expr default_value) {
+  static const Op& op = Op::Get("sparse_fill_empty_rows");
+  return Call(op, {sparse_indices, sparse_values, dense_shape, default_value}, Attrs(), {});
+}
+
+TVM_REGISTER_GLOBAL("relay.op._make.sparse_fill_empty_rows")
+    .set_body_typed(MakeSparseFillEmptyRows);
+
+RELAY_REGISTER_OP("sparse_fill_empty_rows")
+    .describe(R"code(Return representation of a sparse tensor with empty rows filled with default 
+    value.)code" TVM_ADD_FILELINE)
+    .set_num_inputs(4)
+    .add_argument("sparse_indices", "Tensor",
+                  "A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the"
+                  "elements in the sparse tensor that contain nonzero values")
+    .add_argument("sparse_values", "Tensor",
+                  "A 1-D tensor[N] which supplies the values for each element in indices")
+    .add_argument("dense_shape", "Tensor",

Review comment:
       Do you mind if we keep this name ? I would really appreciate it 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org