Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SparseFillEmptyRows Op #7442

Merged
merged 32 commits into from
Feb 17, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Resolve input and output dtype compat
Ubuntu committed Feb 16, 2021

Verified

This commit was signed with the committer’s verified signature. The key has expired.
aitbw Angel Perez
commit c43cda2d474d7f72a1274e688f1154095bcdd787
4 changes: 1 addition & 3 deletions python/tvm/relay/frontend/tensorflow.py
Original file line number Diff line number Diff line change
@@ -1013,9 +1013,7 @@ def _impl(inputs, attr, params, mod):
)

return _expr.TupleWrapper(
_expr.Tuple(
[new_sparse_indices, new_sparse_values, _op.cast(empty_row_indicator, dtype="bool")]
),
_expr.Tuple([new_sparse_indices, new_sparse_values, empty_row_indicator]),
3,
)

21 changes: 13 additions & 8 deletions python/tvm/relay/op/transform.py
Original file line number Diff line number Diff line change
@@ -1330,24 +1330,24 @@ def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_v
Parameters
----------
sparse_indices : relay.Expr
A 2-D int64 tensor[N, ndims] of integers containing location of sparse values, where N is
A 2-D 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 first column of this relay parameter must be sorted in ascending order.
sparse_values : relay.Expr
A 1-D int64 tensor[N] containing the sparse values for the sparse indices.
A 1-D 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.
A 1-D 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
A 2-D tensor[?, ndims] of integers containing location of new sparse
indices. The first column outputs must be sorted in ascending order.
new_sparse_values : relay.Expr
A 1-D int64 tensor[?] containing the sparse values for the sparse indices.
A 1-D 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
A 1-D tensor[dense_shape[0]] filled with zeros and ones
indicating whether the particular row is empty or full respectively

Note
@@ -1380,13 +1380,18 @@ def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_v
[2, 0],
[3, 1],
[4, 0]]
empty_row_indicator = [0, 1, 0, 0, 1]
empty_row_indicator = [False, True, False, False, True]
new_sparse_values = [1, 2, 10, 3, 4, 10]

"""
return TupleWrapper(
new_sparse_indices, new_sparse_values, empty_row_indicator = TupleWrapper(
_make.sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value), 3
)
new_sparse_indices = cast_like(new_sparse_indices, sparse_indices)
new_sparse_values = cast_like(new_sparse_values, sparse_values)
empty_row_indicator = cast(empty_row_indicator, "bool")

return Tuple((new_sparse_indices, new_sparse_values, empty_row_indicator))


def cumsum(data, axis=None, dtype=None, exclusive=None):
2 changes: 1 addition & 1 deletion python/tvm/topi/sparse_fill_empty_rows.py
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ def _sparse_fill_empty_rows(

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])):
for i in range(0, new_sparse_indices_shape[0]):
new_sparse_indices[i, 0] = int64(i)
new_sparse_values[i] = default_value_
empty_row_indicator[i] = int64(1)
65 changes: 0 additions & 65 deletions python/tvm/topi/transform.py
Original file line number Diff line number Diff line change
@@ -931,68 +931,3 @@ def adv_index(data, indices):
Output tensor
"""
return cpp.adv_index(data, indices)


def sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value):
"""
Fill rows in a sparse matrix that do no contain any values. Values are placed in the first
column of empty rows. The sparse array is in COO format.
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 first column of this relay parameter must be sorted in ascending 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. The first column outputs must be sorted in ascending order.
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
----
This op exactly follows the documentation here:
https://www.tensorflow.org/api_docs/python/tf/sparse/fill_empty_rows
There are two exceptions:
1. Input Sparse Indices are expected to be in row-major order.
2. Empty Row Indicator has int64 output type with 1(for True) and 0(for False).

Examples
-------
.. code-block:: python
sparse_indices = [[0, 1],
[0, 3],
[2, 0],
[3, 1]]
sparse_values = [1, 2, 3, 4]
default_value = [10]
dense_shape = [5, 6]
new_sparse_indices, empty_row_indicator, new_sparse_values, slice_element_index =
relay.sparse_fill_empty_rows(
sparse_indices,
sparse_values,
default_value,
dense_shape)
new_sparse_indices = [[0, 1],
[0, 3],
[1, 0],
[2, 0],
[3, 1],
[4, 0]]
empty_row_indicator = [0, 1, 0, 0, 1]
new_sparse_values = [1, 2, 10, 3, 4, 10]

"""
return cpp.sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value)
20 changes: 14 additions & 6 deletions tests/python/relay/dyn/test_dynamic_op_level3.py
Original file line number Diff line number Diff line change
@@ -244,7 +244,7 @@ def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_
),
],
)
@pytest.mark.parametrize("dtype", [np.int32, np.int64])
@pytest.mark.parametrize("dtype", [np.int64, np.int32])
@pytest.mark.parametrize("use_dyn", [True, False])
def test_sparse_fill_empty_rows(
sparse_indices, sparse_values, dense_shape, default_value, dtype, use_dyn
@@ -264,15 +264,15 @@ 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
empty_row_indicator[current_idx] = True
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])]
empty_row_indicator = [False 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]
@@ -331,16 +331,24 @@ def verify_sparse_fill_empty_rows(
"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()
z = relay.sparse_fill_empty_rows(sparse_indices, sparse_values, dense_shape, default_value)
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,
)
(
new_sparse_indices_infer_type,
new_sparse_values_infer_type,
empty_row_indicator_infer_type,
) = run_infer_type(z)

assert new_sparse_indices_infer_type.checked_type.dtype == sparse_indices_np.dtype
assert new_sparse_values_infer_type.checked_type.dtype == sparse_indices_np.dtype
assert empty_row_indicator_infer_type.checked_type.dtype == "bool"

verify_func(
codeislife99 marked this conversation as resolved.
Show resolved Hide resolved
func,
[sparse_indices_np, sparse_values_np, dense_shape_np, default_value_np],