1 /* Copyright 2016 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
16 #include "tensorflow/core/framework/allocator.h"
17 #include "tensorflow/core/framework/fake_input.h"
18 #include "tensorflow/core/framework/node_def_builder.h"
19 #include "tensorflow/core/framework/op_kernel.h"
20 #include "tensorflow/core/framework/tensor.h"
21 #include "tensorflow/core/framework/tensor_testutil.h"
22 #include "tensorflow/core/framework/types.h"
23 #include "tensorflow/core/kernels/ops_testutil.h"
24 #include "tensorflow/core/lib/core/status_test_util.h"
25 #include "tensorflow/core/platform/test.h"
26
27 namespace tensorflow {
28
29 namespace {
30
31 class SparseAddOpTest : public OpsTestBase {
32 protected:
33 template <typename T>
MakeOp()34 void MakeOp() {
35 DataType value_type = tensorflow::DataTypeToEnum<T>::value;
36 DataType thresh_type = value_type;
37 if (std::is_same<T, std::complex<float>>::value) {
38 thresh_type = DT_FLOAT;
39 } else if (std::is_same<T, std::complex<double>>::value) {
40 thresh_type = DT_DOUBLE;
41 }
42
43 TF_ASSERT_OK(NodeDefBuilder("sparseadd", "SparseAdd")
44 .Input(FakeInput(DT_INT64))
45 .Input(FakeInput(value_type))
46 .Input(FakeInput(DT_INT64))
47 .Input(FakeInput(DT_INT64))
48 .Input(FakeInput(value_type))
49 .Input(FakeInput(DT_INT64))
50 .Input(FakeInput(thresh_type))
51 .Attr("Treal", thresh_type)
52 .Finalize(node_def()));
53 TF_ASSERT_OK(InitOp());
54 }
55 };
56
TEST_F(SparseAddOpTest,TwoD_AddSparseTensorWithSelf)57 TEST_F(SparseAddOpTest, TwoD_AddSparseTensorWithSelf) {
58 MakeOp<float>();
59
60 // [ 1]
61 // [2 ]
62 // [3 4]
63
64 const auto indices_shape = TensorShape({4, 2});
65 std::initializer_list<int64> in{0, 1, 1, 0, 2, 0, 2, 1};
66 const gtl::ArraySlice<int64> indices(in);
67 std::initializer_list<int64> sh{3, 2};
68 const gtl::ArraySlice<int64> shape(sh);
69
70 #define ADD_TENSOR_INPUT() \
71 AddInputFromArray<int64>(indices_shape, indices); \
72 AddInputFromArray<float>(TensorShape({4}), {1, 2, 3, 4}); \
73 AddInputFromArray<int64>(TensorShape({2}), shape);
74
75 ADD_TENSOR_INPUT();
76 ADD_TENSOR_INPUT();
77 AddInputFromArray<float>(TensorShape({}), {0.0});
78 #undef ADD_TENSOR_INPUT
79
80 TF_ASSERT_OK(RunOpKernel());
81
82 Tensor expected_indices(allocator(), DT_INT64, indices_shape);
83 test::FillValues<int64>(&expected_indices, indices);
84 test::ExpectTensorEqual<int64>(expected_indices, *GetOutput(0));
85
86 Tensor expected_values(allocator(), DT_FLOAT, {4});
87 test::FillValues<float>(&expected_values, {2, 4, 6, 8});
88 test::ExpectTensorEqual<float>(expected_values, *GetOutput(1));
89
90 Tensor expected_shape(allocator(), DT_INT64,
91 {static_cast<int64>(shape.size())});
92 test::FillValues<int64>(&expected_shape, shape);
93 test::ExpectTensorEqual<int64>(expected_shape, *GetOutput(2));
94 }
95
96 // [ 1] [5 ] [5 1]
97 // [2 ] + [ 6] == [2 6]
98 // [3 4] [ ] [3 4]
99 #define RUN_TEST(VALTYPE) \
100 TEST_F(SparseAddOpTest, TwoD_AddSparseTensorsWithDiffIndices_##VALTYPE) { \
101 MakeOp<VALTYPE>(); \
102 DataType val_dtype = tensorflow::DataTypeToEnum<VALTYPE>::value; \
103 \
104 const auto indices_shape = TensorShape({4, 2}); \
105 std::initializer_list<int64> in{0, 1, 1, 0, 2, 0, 2, 1}; \
106 const gtl::ArraySlice<int64> indices(in); \
107 std::initializer_list<int64> sh{3, 2}; \
108 const gtl::ArraySlice<int64> shape(sh); \
109 \
110 AddInputFromArray<int64>(indices_shape, indices); \
111 AddInputFromArray<VALTYPE>(TensorShape({4}), {1, 2, 3, 4}); \
112 AddInputFromArray<int64>(TensorShape({2}), shape); \
113 \
114 AddInputFromArray<int64>(TensorShape({2, 2}), {0, 0, 1, 1}); \
115 AddInputFromArray<VALTYPE>(TensorShape({2}), {5, 6}); \
116 AddInputFromArray<int64>(TensorShape({2}), shape); \
117 \
118 if (val_dtype == DT_COMPLEX64) { \
119 AddInputFromArray<float>(TensorShape({}), {0}); \
120 } else if (val_dtype == DT_COMPLEX128) { \
121 AddInputFromArray<double>(TensorShape({}), {0}); \
122 } else { \
123 AddInputFromArray<VALTYPE>(TensorShape({}), {0}); \
124 } \
125 \
126 TF_ASSERT_OK(RunOpKernel()); \
127 \
128 const int expected_nnz = 6; \
129 Tensor expected_indices(allocator(), DT_INT64, \
130 TensorShape({expected_nnz, 2})); \
131 test::FillValues<int64>(&expected_indices, \
132 {0, 0, 0, 1, 1, 0, 1, 1, 2, 0, 2, 1}); \
133 test::ExpectTensorEqual<int64>(expected_indices, *GetOutput(0)); \
134 \
135 Tensor expected_values(allocator(), val_dtype, {expected_nnz}); \
136 test::FillValues<VALTYPE>(&expected_values, {5, 1, 2, 6, 3, 4}); \
137 test::ExpectTensorEqual<VALTYPE>(expected_values, *GetOutput(1)); \
138 \
139 Tensor expected_shape(allocator(), DT_INT64, \
140 {static_cast<int64>(shape.size())}); \
141 test::FillValues<int64>(&expected_shape, shape); \
142 test::ExpectTensorEqual<int64>(expected_shape, *GetOutput(2)); \
143 }
144
145 RUN_TEST(int64);
146 RUN_TEST(float);
147 RUN_TEST(double);
148 RUN_TEST(complex64);
149 RUN_TEST(complex128);
150 #undef RUN_TEST
151
152 // Adding
153 // [ 1]
154 // [2 ]
155 // [3 4]
156 // to its cwise negation.
157 #define RUN_TEST(VALTYPE, THRESH) \
158 TEST_F(SparseAddOpTest, TwoD_SmallValuesShouldVanish_##VALTYPE) { \
159 MakeOp<VALTYPE>(); \
160 DataType val_dtype = tensorflow::DataTypeToEnum<VALTYPE>::value; \
161 const auto indices_shape = TensorShape({4, 2}); \
162 std::initializer_list<int64> in{0, 1, 1, 0, 2, 0, 2, 1}; \
163 const gtl::ArraySlice<int64> indices(in); \
164 std::initializer_list<int64> sh{3, 2}; \
165 const gtl::ArraySlice<int64> shape(sh); \
166 \
167 auto AddSparseTensor = [indices, indices_shape, shape, \
168 this](bool negate) { \
169 AddInputFromArray<int64>(indices_shape, indices); \
170 if (!negate) { \
171 AddInputFromArray<VALTYPE>(TensorShape({4}), {1, 2, 3, 4}); \
172 } else { \
173 AddInputFromArray<VALTYPE>(TensorShape({4}), {-1, -2, -3, -4}); \
174 } \
175 AddInputFromArray<int64>(TensorShape({2}), shape); \
176 }; \
177 AddSparseTensor(false); \
178 AddSparseTensor(true); \
179 if (val_dtype == DT_COMPLEX64) { \
180 AddInputFromArray<float>(TensorShape({}), {THRESH}); \
181 } else if (val_dtype == DT_COMPLEX128) { \
182 AddInputFromArray<double>(TensorShape({}), {THRESH}); \
183 } else { \
184 AddInputFromArray<VALTYPE>(TensorShape({}), {THRESH}); \
185 } \
186 \
187 TF_ASSERT_OK(RunOpKernel()); \
188 \
189 Tensor expected_indices(allocator(), DT_INT64, TensorShape({0, 2})); \
190 test::ExpectTensorEqual<int64>(expected_indices, *GetOutput(0)); \
191 \
192 Tensor expected_values(allocator(), val_dtype, TensorShape({0})); \
193 test::ExpectTensorEqual<VALTYPE>(expected_values, *GetOutput(1)); \
194 \
195 Tensor expected_shape(allocator(), DT_INT64, \
196 {static_cast<int64>(shape.size())}); \
197 test::FillValues<int64>(&expected_shape, shape); \
198 test::ExpectTensorEqual<int64>(expected_shape, *GetOutput(2)); \
199 }
200
201 RUN_TEST(int64, 1);
202 RUN_TEST(float, 1e-3f);
203 RUN_TEST(double, 1e-3f);
204 RUN_TEST(complex64, 1e-3f);
205 RUN_TEST(complex128, 1e-3f);
206 #undef RUN_TEST
207
208 } // namespace
209
210 } // namespace tensorflow
211