1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19
20 #include <vector>
21
22 #include "NeuralNetworksWrapper.h"
23 #include "SVDF.h"
24
25 using ::testing::FloatNear;
26 using ::testing::Matcher;
27
28 namespace android {
29 namespace nn {
30 namespace wrapper {
31
32 namespace {
33
ArrayFloatNear(const std::vector<float> & values,float max_abs_error=1.e-6)34 std::vector<Matcher<float>> ArrayFloatNear(const std::vector<float>& values,
35 float max_abs_error = 1.e-6) {
36 std::vector<Matcher<float>> matchers;
37 matchers.reserve(values.size());
38 for (const float& v : values) {
39 matchers.emplace_back(FloatNear(v, max_abs_error));
40 }
41 return matchers;
42 }
43
44 } // namespace
45
46 using ::testing::ElementsAreArray;
47
48 static float svdf_input[] = {
49 0.12609188, -0.46347019, -0.89598465, 0.12609188, -0.46347019, -0.89598465,
50
51 0.14278367, -1.64410412, -0.75222826, 0.14278367, -1.64410412, -0.75222826,
52
53 0.49837467, 0.19278903, 0.26584083, 0.49837467, 0.19278903, 0.26584083,
54
55 -0.11186574, 0.13164264, -0.05349274, -0.11186574, 0.13164264, -0.05349274,
56
57 -0.68892461, 0.37783599, 0.18263303, -0.68892461, 0.37783599, 0.18263303,
58
59 -0.81299269, -0.86831826, 1.43940818, -0.81299269, -0.86831826, 1.43940818,
60
61 -1.45006323, -0.82251364, -1.69082689, -1.45006323, -0.82251364, -1.69082689,
62
63 0.03966608, -0.24936394, -0.77526885, 0.03966608, -0.24936394, -0.77526885,
64
65 0.11771342, -0.23761693, -0.65898693, 0.11771342, -0.23761693, -0.65898693,
66
67 -0.89477462, 1.67204106, -0.53235275, -0.89477462, 1.67204106, -0.53235275};
68
69 static float svdf_input_rank2[] = {
70 0.12609188, -0.46347019, -0.89598465, 0.35867718, 0.36897406, 0.73463392,
71
72 0.14278367, -1.64410412, -0.75222826, -0.57290924, 0.12729003, 0.7567004,
73
74 0.49837467, 0.19278903, 0.26584083, 0.17660543, 0.52949083, -0.77931279,
75
76 -0.11186574, 0.13164264, -0.05349274, -0.72674477, -0.5683046, 0.55900657,
77
78 -0.68892461, 0.37783599, 0.18263303, -0.63690937, 0.44483393, -0.71817774,
79
80 -0.81299269, -0.86831826, 1.43940818, -0.95760226, 1.82078898, 0.71135032,
81
82 -1.45006323, -0.82251364, -1.69082689, -1.65087092, -1.89238167, 1.54172635,
83
84 0.03966608, -0.24936394, -0.77526885, 2.06740379, -1.51439476, 1.43768692,
85
86 0.11771342, -0.23761693, -0.65898693, 0.31088525, -1.55601168, -0.87661445,
87
88 -0.89477462, 1.67204106, -0.53235275, -0.6230064, 0.29819036, 1.06939757,
89 };
90
91 static float svdf_golden_output[] = {0.014899, -0.0517661, -0.143725, -0.00271883,
92 0.014899, -0.0517661, -0.143725, -0.00271883,
93
94 0.068281, -0.162217, -0.152268, 0.00323521,
95 0.068281, -0.162217, -0.152268, 0.00323521,
96
97 -0.0317821, -0.0333089, 0.0609602, 0.0333759,
98 -0.0317821, -0.0333089, 0.0609602, 0.0333759,
99
100 -0.00623099, -0.077701, -0.391193, -0.0136691,
101 -0.00623099, -0.077701, -0.391193, -0.0136691,
102
103 0.201551, -0.164607, -0.179462, -0.0592739,
104 0.201551, -0.164607, -0.179462, -0.0592739,
105
106 0.0886511, -0.0875401, -0.269283, 0.0281379,
107 0.0886511, -0.0875401, -0.269283, 0.0281379,
108
109 -0.201174, -0.586145, -0.628624, -0.0330412,
110 -0.201174, -0.586145, -0.628624, -0.0330412,
111
112 -0.0839096, -0.299329, 0.108746, 0.109808,
113 -0.0839096, -0.299329, 0.108746, 0.109808,
114
115 0.419114, -0.237824, -0.422627, 0.175115,
116 0.419114, -0.237824, -0.422627, 0.175115,
117
118 0.36726, -0.522303, -0.456502, -0.175475,
119 0.36726, -0.522303, -0.456502, -0.175475};
120
121 static float svdf_golden_output_rank_2[] = {
122 -0.09623547, -0.10193135, 0.11083051, -0.0347917,
123 0.1141196, 0.12965347, -0.12652366, 0.01007236,
124
125 -0.16396809, -0.21247184, 0.11259045, -0.04156673,
126 0.10132131, -0.06143532, -0.00924693, 0.10084561,
127
128 0.01257364, 0.0506071, -0.19287863, -0.07162561,
129 -0.02033747, 0.22673416, 0.15487903, 0.02525555,
130
131 -0.1411963, -0.37054959, 0.01774767, 0.05867489,
132 0.09607603, -0.0141301, -0.08995658, 0.12867066,
133
134 -0.27142537, -0.16955489, 0.18521598, -0.12528358,
135 0.00331409, 0.11167502, 0.02218599, -0.07309391,
136
137 0.09593632, -0.28361851, -0.0773851, 0.17199151,
138 -0.00075242, 0.33691186, -0.1536046, 0.16572715,
139
140 -0.27916506, -0.27626723, 0.42615682, 0.3225764,
141 -0.37472126, -0.55655634, -0.05013514, 0.289112,
142
143 -0.24418658, 0.07540751, -0.1940318, -0.08911639,
144 0.00732617, 0.46737891, 0.26449674, 0.24888524,
145
146 -0.17225097, -0.54660404, -0.38795233, 0.08389944,
147 0.07736043, -0.28260678, 0.15666828, 1.14949894,
148
149 -0.57454878, -0.64704704, 0.73235172, -0.34616736,
150 0.21120001, -0.22927976, 0.02455296, -0.35906726,
151 };
152
153 #define FOR_ALL_INPUT_AND_WEIGHT_TENSORS(ACTION) \
154 ACTION(Input) \
155 ACTION(WeightsFeature) \
156 ACTION(WeightsTime) \
157 ACTION(Bias) \
158 ACTION(StateIn)
159
160 // For all output and intermediate states
161 #define FOR_ALL_OUTPUT_TENSORS(ACTION) \
162 ACTION(StateOut) \
163 ACTION(Output)
164
165 // Derived class of SingleOpModel, which is used to test SVDF TFLite op.
166 class SVDFOpModel {
167 public:
SVDFOpModel(uint32_t batches,uint32_t units,uint32_t input_size,uint32_t memory_size,uint32_t rank)168 SVDFOpModel(uint32_t batches, uint32_t units, uint32_t input_size, uint32_t memory_size,
169 uint32_t rank)
170 : batches_(batches),
171 units_(units),
172 input_size_(input_size),
173 memory_size_(memory_size),
174 rank_(rank) {
175 std::vector<std::vector<uint32_t>> input_shapes{
176 {batches_, input_size_}, // Input tensor
177 {units_ * rank_, input_size_}, // weights_feature tensor
178 {units_ * rank_, memory_size_}, // weights_time tensor
179 {units_}, // bias tensor
180 {batches_, memory_size * units_ * rank_}, // state in tensor
181 };
182 std::vector<uint32_t> inputs;
183 auto it = input_shapes.begin();
184
185 // Input and weights
186 #define AddInput(X) \
187 OperandType X##OpndTy(Type::TENSOR_FLOAT32, *it++); \
188 inputs.push_back(model_.addOperand(&X##OpndTy));
189
190 FOR_ALL_INPUT_AND_WEIGHT_TENSORS(AddInput);
191
192 #undef AddInput
193
194 // Parameters
195 OperandType RankParamTy(Type::INT32, {});
196 inputs.push_back(model_.addOperand(&RankParamTy));
197 OperandType ActivationParamTy(Type::INT32, {});
198 inputs.push_back(model_.addOperand(&ActivationParamTy));
199
200 // Output and other intermediate state
201 std::vector<std::vector<uint32_t>> output_shapes{{batches_, memory_size_ * units_ * rank_},
202 {batches_, units_}};
203 std::vector<uint32_t> outputs;
204
205 auto it2 = output_shapes.begin();
206
207 #define AddOutput(X) \
208 OperandType X##OpndTy(Type::TENSOR_FLOAT32, *it2++); \
209 outputs.push_back(model_.addOperand(&X##OpndTy));
210
211 FOR_ALL_OUTPUT_TENSORS(AddOutput);
212
213 #undef AddOutput
214
215 Input_.insert(Input_.end(), batches_ * input_size_, 0.f);
216 StateIn_.insert(StateIn_.end(), batches_ * units_ * rank_ * memory_size_, 0.f);
217
218 auto multiAll = [](const std::vector<uint32_t>& dims) -> uint32_t {
219 uint32_t sz = 1;
220 for (uint32_t d : dims) {
221 sz *= d;
222 }
223 return sz;
224 };
225
226 it2 = output_shapes.begin();
227
228 #define ReserveOutput(X) X##_.insert(X##_.end(), multiAll(*it2++), 0.f);
229
230 FOR_ALL_OUTPUT_TENSORS(ReserveOutput);
231
232 model_.addOperation(ANEURALNETWORKS_SVDF, inputs, outputs);
233 model_.identifyInputsAndOutputs(inputs, outputs);
234
235 model_.finish();
236 }
237
Invoke()238 void Invoke() {
239 ASSERT_TRUE(model_.isValid());
240
241 Compilation compilation(&model_);
242 compilation.finish();
243 Execution execution(&compilation);
244
245 StateIn_.swap(StateOut_);
246
247 #define SetInputOrWeight(X) \
248 ASSERT_EQ(execution.setInput(SVDF::k##X##Tensor, X##_.data(), sizeof(float) * X##_.size()), \
249 Result::NO_ERROR);
250
251 FOR_ALL_INPUT_AND_WEIGHT_TENSORS(SetInputOrWeight);
252
253 #undef SetInputOrWeight
254
255 #define SetOutput(X) \
256 EXPECT_TRUE(X##_.data() != nullptr); \
257 ASSERT_EQ(execution.setOutput(SVDF::k##X##Tensor, X##_.data(), sizeof(float) * X##_.size()), \
258 Result::NO_ERROR);
259
260 FOR_ALL_OUTPUT_TENSORS(SetOutput);
261
262 #undef SetOutput
263
264 ASSERT_EQ(execution.setInput(SVDF::kRankParam, &rank_, sizeof(rank_)), Result::NO_ERROR);
265
266 int activation = ActivationFn::kActivationNone;
267 ASSERT_EQ(execution.setInput(SVDF::kActivationParam, &activation, sizeof(activation)),
268 Result::NO_ERROR);
269
270 ASSERT_EQ(execution.compute(), Result::NO_ERROR);
271 }
272
273 #define DefineSetter(X) \
274 void Set##X(const std::vector<float>& f) { X##_.insert(X##_.end(), f.begin(), f.end()); }
275
276 FOR_ALL_INPUT_AND_WEIGHT_TENSORS(DefineSetter);
277
278 #undef DefineSetter
279
SetInput(int offset,float * begin,float * end)280 void SetInput(int offset, float* begin, float* end) {
281 for (; begin != end; begin++, offset++) {
282 Input_[offset] = *begin;
283 }
284 }
285
286 // Resets the state of SVDF op by filling it with 0's.
ResetState()287 void ResetState() {
288 std::fill(StateIn_.begin(), StateIn_.end(), 0.f);
289 std::fill(StateOut_.begin(), StateOut_.end(), 0.f);
290 }
291
292 // Extracts the output tensor from the SVDF op.
GetOutput() const293 const std::vector<float>& GetOutput() const { return Output_; }
294
input_size() const295 int input_size() const { return input_size_; }
num_units() const296 int num_units() const { return units_; }
num_batches() const297 int num_batches() const { return batches_; }
298
299 private:
300 Model model_;
301
302 const uint32_t batches_;
303 const uint32_t units_;
304 const uint32_t input_size_;
305 const uint32_t memory_size_;
306 const uint32_t rank_;
307
308 #define DefineTensor(X) std::vector<float> X##_;
309
310 FOR_ALL_INPUT_AND_WEIGHT_TENSORS(DefineTensor);
311 FOR_ALL_OUTPUT_TENSORS(DefineTensor);
312
313 #undef DefineTensor
314 };
315
TEST(SVDFOpTest,BlackBoxTest)316 TEST(SVDFOpTest, BlackBoxTest) {
317 SVDFOpModel svdf(/*batches=*/2, /*units=*/4, /*input_size=*/3,
318 /*memory_size=*/10, /*rank=*/1);
319 svdf.SetWeightsFeature({-0.31930989, -0.36118156, 0.0079667, 0.37613347, 0.22197971, 0.12416199,
320 0.27901134, 0.27557442, 0.3905206, -0.36137494, -0.06634006,
321 -0.10640851});
322
323 svdf.SetWeightsTime({-0.31930989, 0.37613347, 0.27901134, -0.36137494, -0.36118156,
324 0.22197971, 0.27557442, -0.06634006, 0.0079667, 0.12416199,
325
326 0.3905206, -0.10640851, -0.0976817, 0.15294972, 0.39635518,
327 -0.02702999, 0.39296314, 0.15785322, 0.21931258, 0.31053296,
328
329 -0.36916667, 0.38031587, -0.21580373, 0.27072677, 0.23622236,
330 0.34936687, 0.18174365, 0.35907319, -0.17493086, 0.324846,
331
332 -0.10781813, 0.27201805, 0.14324132, -0.23681851, -0.27115166,
333 -0.01580888, -0.14943552, 0.15465137, 0.09784451, -0.0337657});
334
335 svdf.SetBias({});
336
337 svdf.ResetState();
338 const int svdf_num_batches = svdf.num_batches();
339 const int svdf_input_size = svdf.input_size();
340 const int svdf_num_units = svdf.num_units();
341 const int input_sequence_size =
342 sizeof(svdf_input) / sizeof(float) / (svdf_input_size * svdf_num_batches);
343 // Going over each input batch, setting the input tensor, invoking the SVDF op
344 // and checking the output with the expected golden values.
345 for (int i = 0; i < input_sequence_size; i++) {
346 float* batch_start = svdf_input + i * svdf_input_size * svdf_num_batches;
347 float* batch_end = batch_start + svdf_input_size * svdf_num_batches;
348 svdf.SetInput(0, batch_start, batch_end);
349
350 svdf.Invoke();
351
352 float* golden_start = svdf_golden_output + i * svdf_num_units * svdf_num_batches;
353 float* golden_end = golden_start + svdf_num_units * svdf_num_batches;
354 std::vector<float> expected;
355 expected.insert(expected.end(), golden_start, golden_end);
356
357 EXPECT_THAT(svdf.GetOutput(), ElementsAreArray(ArrayFloatNear(expected)));
358 }
359 }
360
TEST(SVDFOpTest,BlackBoxTestRank2)361 TEST(SVDFOpTest, BlackBoxTestRank2) {
362 SVDFOpModel svdf(/*batches=*/2, /*units=*/4, /*input_size=*/3,
363 /*memory_size=*/10, /*rank=*/2);
364 svdf.SetWeightsFeature({-0.31930989, 0.0079667, 0.39296314, 0.37613347, 0.12416199,
365 0.15785322, 0.27901134, 0.3905206, 0.21931258, -0.36137494,
366 -0.10640851, 0.31053296, -0.36118156, -0.0976817, -0.36916667,
367 0.22197971, 0.15294972, 0.38031587, 0.27557442, 0.39635518,
368 -0.21580373, -0.06634006, -0.02702999, 0.27072677});
369
370 svdf.SetWeightsTime({-0.31930989, 0.37613347, 0.27901134, -0.36137494, -0.36118156,
371 0.22197971, 0.27557442, -0.06634006, 0.0079667, 0.12416199,
372
373 0.3905206, -0.10640851, -0.0976817, 0.15294972, 0.39635518,
374 -0.02702999, 0.39296314, 0.15785322, 0.21931258, 0.31053296,
375
376 -0.36916667, 0.38031587, -0.21580373, 0.27072677, 0.23622236,
377 0.34936687, 0.18174365, 0.35907319, -0.17493086, 0.324846,
378
379 -0.10781813, 0.27201805, 0.14324132, -0.23681851, -0.27115166,
380 -0.01580888, -0.14943552, 0.15465137, 0.09784451, -0.0337657,
381
382 -0.14884081, 0.19931212, -0.36002168, 0.34663299, -0.11405486,
383 0.12672701, 0.39463779, -0.07886535, -0.06384811, 0.08249187,
384
385 -0.26816407, -0.19905911, 0.29211238, 0.31264046, -0.28664589,
386 0.05698794, 0.11613581, 0.14078894, 0.02187902, -0.21781836,
387
388 -0.15567942, 0.08693647, -0.38256618, 0.36580828, -0.22922277,
389 -0.0226903, 0.12878349, -0.28122205, -0.10850525, -0.11955214,
390
391 0.27179423, -0.04710215, 0.31069002, 0.22672787, 0.09580326,
392 0.08682203, 0.1258215, 0.1851041, 0.29228821, 0.12366763});
393
394 svdf.SetBias({});
395
396 svdf.ResetState();
397 const int svdf_num_batches = svdf.num_batches();
398 const int svdf_input_size = svdf.input_size();
399 const int svdf_num_units = svdf.num_units();
400 const int input_sequence_size =
401 sizeof(svdf_input_rank2) / sizeof(float) / (svdf_input_size * svdf_num_batches);
402 // Going over each input batch, setting the input tensor, invoking the SVDF op
403 // and checking the output with the expected golden values.
404 for (int i = 0; i < input_sequence_size; i++) {
405 float* batch_start = svdf_input_rank2 + i * svdf_input_size * svdf_num_batches;
406 float* batch_end = batch_start + svdf_input_size * svdf_num_batches;
407 svdf.SetInput(0, batch_start, batch_end);
408
409 svdf.Invoke();
410
411 float* golden_start = svdf_golden_output_rank_2 + i * svdf_num_units * svdf_num_batches;
412 float* golden_end = golden_start + svdf_num_units * svdf_num_batches;
413 std::vector<float> expected;
414 expected.insert(expected.end(), golden_start, golden_end);
415
416 EXPECT_THAT(svdf.GetOutput(), ElementsAreArray(ArrayFloatNear(expected)));
417 }
418 }
419
420 } // namespace wrapper
421 } // namespace nn
422 } // namespace android
423