• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Tests for sparse ops."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21from absl.testing import parameterized
22import numpy as np
23
24from tensorflow.python.framework import constant_op
25from tensorflow.python.framework import dtypes
26from tensorflow.python.framework import ops
27from tensorflow.python.framework import sparse_tensor
28from tensorflow.python.framework import test_util
29# Need array_grad to register gradient for Identity.
30from tensorflow.python.ops import array_grad  # pylint: disable=unused-import
31from tensorflow.python.ops import array_ops
32from tensorflow.python.ops import gradient_checker_v2 as gradient_checker
33from tensorflow.python.ops import math_ops
34# Need sparse_grad to register gradient for SparseToDense.
35from tensorflow.python.ops import sparse_grad  # pylint: disable=unused-import
36from tensorflow.python.ops import sparse_ops
37from tensorflow.python.platform import googletest
38
39
40@test_util.run_all_in_graph_and_eager_modes
41class SparseOpsTest(test_util.TensorFlowTestCase, parameterized.TestCase):
42
43  def testSparseEye(self):
44    def test_one(n, m, as_tensors):
45      expected = np.eye(n, m)
46      if as_tensors:
47        m = constant_op.constant(m)
48        n = constant_op.constant(n)
49      s = sparse_ops.sparse_eye(n, m)
50      d = sparse_ops.sparse_to_dense(s.indices, s.dense_shape, s.values)
51      self.assertAllEqual(self.evaluate(d), expected)
52
53    for n in range(2, 10, 2):
54      for m in range(2, 10, 2):
55        # Test with n and m as both constants and tensors.
56        test_one(n, m, True)
57        test_one(n, m, False)
58
59  def testDenseFromConstantToSparse(self):
60    expected_constant = np.reshape(np.arange(24, dtype=np.int64), (3, 4, 2))
61    tensor = constant_op.constant(expected_constant)
62    sparse = sparse_ops.from_dense(tensor)
63    dense = sparse_ops.sparse_to_dense(sparse.indices, sparse.dense_shape,
64                                       sparse.values)
65    constant = self.evaluate(dense)
66    self.assertAllEqual(expected_constant, constant)
67
68  def testTransposePreservesShape(self):
69    with ops.Graph().as_default():
70      t = sparse_tensor.SparseTensor(indices=[[0, 0]],
71                                     values=[0.],
72                                     dense_shape=[3, 4])
73      self.assertTrue(t.shape.is_fully_defined)
74      transposed = sparse_ops.sparse_transpose(t)
75      self.assertAllEqual(transposed.shape, [4, 3])
76
77  def testSparseExpandDims(self):
78    for rank in range(1, 4):
79      # Create a dummy input. When rank=3, shape=[2, 4, 6].
80      shape = np.arange(1, rank + 1) * 2
81      before = np.arange(np.prod(shape)).reshape(shape)
82
83      # Make entries sparse.
84      before *= np.random.binomial(1, .2, before.shape)
85      dense_shape = before.shape
86      indices = np.array(np.where(before)).T
87      values = before[before != 0]
88
89      # Try every possible valid value of axis.
90      for axis in range(-rank - 1, rank):
91        expected_after = np.expand_dims(before, axis)
92
93        for axis_as_tensor in [False, True]:
94          dense_shape_t = constant_op.constant(dense_shape, dtype=dtypes.int64)
95          indices_t = constant_op.constant(indices)
96          values_t = constant_op.constant(values)
97          before_t = sparse_tensor.SparseTensor(
98              indices=indices_t, values=values_t, dense_shape=dense_shape_t)
99
100          if axis_as_tensor:
101            axis = constant_op.constant(axis)
102
103          s = sparse_ops.sparse_expand_dims(before_t, axis)
104          d = sparse_ops.sparse_to_dense(s.indices, s.dense_shape, s.values)
105          self.assertAllEqual(self.evaluate(d), expected_after)
106
107  @parameterized.parameters([
108      (math_ops.abs, [1.0, -1.0, 3.0, -4.0], [1.0, 1.0, 3.0, 4.0]),
109      (math_ops.negative, [1.0, -1.0, 3.0, -4.0], [-1.0, 1.0, -3.0, 4.0]),
110      (math_ops.sign, [3.0, -2.0, 0.0, -4.0], [1.0, -1.0, 0.0, -1.0]),
111      (math_ops.square, [1.0, -1.0, 3.0, -4.0], [1.0, 1.0, 9.0, 16.0]),
112  ])
113  def testUnarySparseDispatch(self, op, values, expected):
114    st = sparse_tensor.SparseTensor(
115        indices=[[0, 0], [0, 1], [2, 0], [2, 4]],
116        values=values,
117        dense_shape=[3, 6])
118    result = op(st)
119    result_value = self.evaluate(result)
120    self.assertAllEqual(result_value.indices, st.indices)
121    self.assertAllEqual(result_value.values, expected)
122    self.assertAllEqual(result_value.dense_shape, st.dense_shape)
123
124  def testSparseToDenseGradient(self):
125
126    def f(sparse_values, default_value):
127      st = sparse_tensor.SparseTensor(
128          indices=[[0, 3, 6], [1, 4, 7], [2, 5, 8]],
129          values=sparse_values,
130          dense_shape=[3, 6, 9])
131      return sparse_ops.sparse_tensor_to_dense(st, default_value)
132
133    grads = gradient_checker.compute_gradient(
134        f, [constant_op.constant([1.0, 2.0, 3.0]),
135            constant_op.constant(0.0)])
136    epsilon = 1e-4
137    self.assertLess(gradient_checker.max_error(*grads), epsilon)
138
139  def testSparseTensorToDenseString(self):
140    sp = sparse_tensor.SparseTensor(
141        indices=[[0, 0], [1, 2]], values=['a', 'b'], dense_shape=[2, 3])
142    dense = sparse_ops.sparse_tensor_to_dense(sp)
143    expected_dense = [[b'a', b'', b''], [b'', b'', b'b']]
144    result_dense = self.evaluate(dense)
145    self.assertAllEqual(expected_dense, result_dense)
146
147  def testDenseSparseTensorMatMul(self):
148
149    np.random.seed(42)
150    dense_numpy_array = np.random.rand(3, 3)
151    independent_dense_tf = constant_op.constant(
152        dense_numpy_array, dtype='float32')
153
154    sp = sparse_tensor.SparseTensor(
155        indices=[[0, 0], [1, 2]], values=[4., 8.], dense_shape=[3, 3])
156    dense_of_sparse = sparse_ops.sparse_to_dense(sp.indices, sp.shape,
157                                                 sp.values)
158
159    result = sparse_ops.sparse_tensor_dense_matmul(
160        independent_dense_tf, sp, adjoint_a=False, adjoint_b=False)
161    expected = math_ops.matmul(independent_dense_tf, dense_of_sparse)
162    self.assertAllEqual(expected, result)
163
164    result = sparse_ops.sparse_tensor_dense_matmul(
165        independent_dense_tf, sp, adjoint_a=False, adjoint_b=True)
166    expected = math_ops.matmul(independent_dense_tf,
167                               array_ops.transpose(dense_of_sparse))
168    self.assertAllEqual(expected, result)
169
170    result = sparse_ops.sparse_tensor_dense_matmul(
171        independent_dense_tf, sp, adjoint_a=True, adjoint_b=False)
172    expected = math_ops.matmul(
173        array_ops.transpose(independent_dense_tf), dense_of_sparse)
174    self.assertAllEqual(expected, result)
175
176    result = sparse_ops.sparse_tensor_dense_matmul(
177        independent_dense_tf, sp, adjoint_a=True, adjoint_b=True)
178    expected = math_ops.matmul(
179        array_ops.transpose(independent_dense_tf),
180        array_ops.transpose(dense_of_sparse))
181    self.assertAllEqual(expected, result)
182
183
184if __name__ == '__main__':
185  googletest.main()
186