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