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