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 "LSHProjection.h"
18
19 #include "NeuralNetworksWrapper.h"
20
21 #include <gmock/gmock.h>
22 #include <gmock/gmock-matchers.h>
23 #include <gtest/gtest.h>
24
25 using ::testing::FloatNear;
26 using ::testing::Matcher;
27
28 namespace android {
29 namespace nn {
30 namespace wrapper {
31
32 using ::testing::ElementsAre;
33
34 #define FOR_ALL_INPUT_AND_WEIGHT_TENSORS(ACTION) \
35 ACTION(Hash, float) \
36 ACTION(Input, int) \
37 ACTION(Weight, float)
38
39 // For all output and intermediate states
40 #define FOR_ALL_OUTPUT_TENSORS(ACTION) ACTION(Output, int)
41
42 class LSHProjectionOpModel {
43 public:
LSHProjectionOpModel(LSHProjectionType type,std::initializer_list<uint32_t> hash_shape,std::initializer_list<uint32_t> input_shape,std::initializer_list<uint32_t> weight_shape)44 LSHProjectionOpModel(LSHProjectionType type, std::initializer_list<uint32_t> hash_shape,
45 std::initializer_list<uint32_t> input_shape,
46 std::initializer_list<uint32_t> weight_shape)
47 : type_(type) {
48 std::vector<uint32_t> inputs;
49
50 OperandType HashTy(Type::TENSOR_FLOAT32, hash_shape);
51 inputs.push_back(model_.addOperand(&HashTy));
52 OperandType InputTy(Type::TENSOR_INT32, input_shape);
53 inputs.push_back(model_.addOperand(&InputTy));
54 OperandType WeightTy(Type::TENSOR_FLOAT32, weight_shape);
55 inputs.push_back(model_.addOperand(&WeightTy));
56
57 OperandType TypeParamTy(Type::INT32, {});
58 inputs.push_back(model_.addOperand(&TypeParamTy));
59
60 std::vector<uint32_t> outputs;
61
62 auto multiAll = [](const std::vector<uint32_t>& dims) -> uint32_t {
63 uint32_t sz = 1;
64 for (uint32_t d : dims) {
65 sz *= d;
66 }
67 return sz;
68 };
69
70 uint32_t outShapeDimension = 0;
71 if (type == LSHProjectionType_SPARSE || type == LSHProjectionType_SPARSE_DEPRECATED) {
72 auto it = hash_shape.begin();
73 Output_.insert(Output_.end(), *it, 0.f);
74 outShapeDimension = *it;
75 } else {
76 Output_.insert(Output_.end(), multiAll(hash_shape), 0.f);
77 outShapeDimension = multiAll(hash_shape);
78 }
79
80 OperandType OutputTy(Type::TENSOR_INT32, {outShapeDimension});
81 outputs.push_back(model_.addOperand(&OutputTy));
82
83 model_.addOperation(ANEURALNETWORKS_LSH_PROJECTION, inputs, outputs);
84 model_.identifyInputsAndOutputs(inputs, outputs);
85
86 model_.finish();
87 }
88
89 #define DefineSetter(X, T) \
90 void Set##X(const std::vector<T>& f) { X##_.insert(X##_.end(), f.begin(), f.end()); }
91
92 FOR_ALL_INPUT_AND_WEIGHT_TENSORS(DefineSetter);
93
94 #undef DefineSetter
95
GetOutput() const96 const std::vector<int>& GetOutput() const { return Output_; }
97
Invoke()98 void Invoke() {
99 ASSERT_TRUE(model_.isValid());
100
101 Compilation compilation(&model_);
102 compilation.finish();
103 Execution execution(&compilation);
104
105 #define SetInputOrWeight(X, T) \
106 ASSERT_EQ( \
107 execution.setInput(LSHProjection::k##X##Tensor, X##_.data(), sizeof(T) * X##_.size()), \
108 Result::NO_ERROR);
109
110 FOR_ALL_INPUT_AND_WEIGHT_TENSORS(SetInputOrWeight);
111
112 #undef SetInputOrWeight
113
114 #define SetOutput(X, T) \
115 ASSERT_EQ(execution.setOutput(LSHProjection::k##X##Tensor, X##_.data(), \
116 sizeof(T) * X##_.size()), \
117 Result::NO_ERROR);
118
119 FOR_ALL_OUTPUT_TENSORS(SetOutput);
120
121 #undef SetOutput
122
123 ASSERT_EQ(execution.setInput(LSHProjection::kTypeParam, &type_, sizeof(type_)),
124 Result::NO_ERROR);
125
126 ASSERT_EQ(execution.compute(), Result::NO_ERROR);
127 }
128
129 private:
130 Model model_;
131 LSHProjectionType type_;
132
133 std::vector<float> Hash_;
134 std::vector<int> Input_;
135 std::vector<float> Weight_;
136 std::vector<int> Output_;
137 }; // namespace wrapper
138
TEST(LSHProjectionOpTest2,DenseWithThreeInputs)139 TEST(LSHProjectionOpTest2, DenseWithThreeInputs) {
140 LSHProjectionOpModel m(LSHProjectionType_DENSE, {4, 2}, {3, 2}, {3});
141
142 m.SetInput({12345, 54321, 67890, 9876, -12345678, -87654321});
143 m.SetHash({0.123, 0.456, -0.321, -0.654, 1.234, 5.678, -4.321, -8.765});
144 m.SetWeight({0.12, 0.34, 0.56});
145
146 m.Invoke();
147
148 EXPECT_THAT(m.GetOutput(), ElementsAre(1, 1, 1, 0, 1, 1, 1, 0));
149 }
150
TEST(LSHProjectionOpTest2,SparseDeprecatedWithTwoInputs)151 TEST(LSHProjectionOpTest2, SparseDeprecatedWithTwoInputs) {
152 LSHProjectionOpModel m(LSHProjectionType_SPARSE_DEPRECATED, {4, 2}, {3, 2}, {0});
153
154 m.SetInput({12345, 54321, 67890, 9876, -12345678, -87654321});
155 m.SetHash({0.123, 0.456, -0.321, -0.654, 1.234, 5.678, -4.321, -8.765});
156
157 m.Invoke();
158
159 EXPECT_THAT(m.GetOutput(), ElementsAre(1, 2, 2, 0));
160 }
161
TEST(LSHProjectionOpTest2,SparseWithTwoInputs)162 TEST(LSHProjectionOpTest2, SparseWithTwoInputs) {
163 LSHProjectionOpModel m(LSHProjectionType_SPARSE, {4, 2}, {3, 2}, {0});
164
165 m.SetInput({12345, 54321, 67890, 9876, -12345678, -87654321});
166 m.SetHash({0.123, 0.456, -0.321, -0.654, 1.234, 5.678, -4.321, -8.765});
167
168 m.Invoke();
169
170 EXPECT_THAT(m.GetOutput(), ElementsAre(1, 6, 10, 12));
171 }
172
173 } // namespace wrapper
174 } // namespace nn
175 } // namespace android
176