1 /*
2 * Copyright (C) 2021 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
18 #if __has_include("NeuralNetworks.h")
19 #include <NeuralNetworks.h>
20 #else // __has_include("NeuralNetworks.h")
21 #include <android/NeuralNetworks.h>
22 #endif // __has_include("NeuralNetworks.h")
23
24 #include <android-base/logging.h>
25 #include <jni.h>
26
27 #include <chrono>
28 #include <thread>
29
30 namespace {
31
32 using Matrix3x4 = float[3][4];
33 using InsufficientMatrixSize = float[2][3];
34
35 const int32_t kNoActivation = ANEURALNETWORKS_FUSED_NONE;
36 const uint32_t kDimensions[] = {3, 4};
37 const uint32_t kOperationInputs[] = {0, 1, 3};
38 const uint32_t kOperationOutputs[] = {2};
39 const uint32_t kModelInputs[] = {0, 1};
40 const uint32_t kModelOutputs[] = {2};
41 const uint32_t kDimensionsUnknown[] = {0, 0};
42
43 const ANeuralNetworksOperandType kMatrixType{
44 .type = ANEURALNETWORKS_TENSOR_FLOAT32,
45 .dimensionCount = std::size(kDimensions),
46 .dimensions = std::data(kDimensions),
47 .scale = 0.0f,
48 .zeroPoint = 0,
49 };
50 const ANeuralNetworksOperandType kScalarType{
51 .type = ANEURALNETWORKS_INT32,
52 .dimensionCount = 0,
53 .dimensions = nullptr,
54 .scale = 0.0f,
55 .zeroPoint = 0,
56 };
57 const ANeuralNetworksOperandType kMatrixUnknownDimensionsType{
58 .type = ANEURALNETWORKS_TENSOR_FLOAT32,
59 .dimensionCount = std::size(kDimensionsUnknown),
60 .dimensions = std::data(kDimensionsUnknown),
61 .scale = 0.0f,
62 .zeroPoint = 0,
63 };
64
65 const Matrix3x4 kMatrix1 = {{1.f, 2.f, 3.f, 4.f}, {5.f, 6.f, 7.f, 8.f}, {9.f, 10.f, 11.f, 12.f}};
66 const Matrix3x4 kMatrix2 = {{100.f, 200.f, 300.f, 400.f},
67 {500.f, 600.f, 700.f, 800.f},
68 {900.f, 1000.f, 1100.f, 1200.f}};
69
70 // Create a model that can add two tensors using a one node graph.
compilationSuccess()71 void compilationSuccess() {
72 // Create model
73 ANeuralNetworksModel* model = nullptr;
74 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_create(&model));
75 CHECK(model != nullptr);
76 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
77 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
78 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
79 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kScalarType));
80 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
81 ANeuralNetworksModel_setOperandValue(model, 3, &kNoActivation, sizeof(kNoActivation)));
82 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
83 ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD,
84 std::size(kOperationInputs),
85 std::data(kOperationInputs),
86 std::size(kOperationOutputs),
87 std::data(kOperationOutputs)));
88 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
89 ANeuralNetworksModel_identifyInputsAndOutputs(model, std::size(kModelInputs),
90 std::data(kModelInputs),
91 std::size(kModelOutputs),
92 std::data(kModelOutputs)));
93 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_finish(model));
94
95 // Create compilation
96 ANeuralNetworksCompilation* compilation = nullptr;
97 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_create(model, &compilation));
98 CHECK(compilation != nullptr);
99 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_finish(compilation));
100
101 // Cleanup
102 ANeuralNetworksCompilation_free(compilation);
103 ANeuralNetworksModel_free(model);
104 }
105
106 // Create a model that can add two tensors using a one node graph.
compilationFailure()107 void compilationFailure() {
108 // Create model
109 ANeuralNetworksModel* model = nullptr;
110 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_create(&model));
111 CHECK(model != nullptr);
112 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
113 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
114 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
115 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kScalarType));
116 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
117 ANeuralNetworksModel_setOperandValue(model, 3, &kNoActivation, sizeof(kNoActivation)));
118 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
119 ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD,
120 std::size(kOperationInputs),
121 std::data(kOperationInputs),
122 std::size(kOperationOutputs),
123 std::data(kOperationOutputs)));
124 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
125 ANeuralNetworksModel_identifyInputsAndOutputs(model, std::size(kModelInputs),
126 std::data(kModelInputs),
127 std::size(kModelOutputs),
128 std::data(kModelOutputs)));
129 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_finish(model));
130
131 // Create compilation
132 ANeuralNetworksCompilation* compilation = nullptr;
133 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_create(model, &compilation));
134 CHECK(compilation != nullptr);
135 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_finish(compilation));
136 // Second call to CompilationFinish fails.
137 CHECK_EQ(ANEURALNETWORKS_BAD_STATE, ANeuralNetworksCompilation_finish(compilation));
138
139 // Cleanup
140 ANeuralNetworksModel_free(model);
141 }
142
143 // Create a model that can add two tensors using a one node graph.
executionSuccess()144 void executionSuccess() {
145 // Create model
146 ANeuralNetworksModel* model = nullptr;
147 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_create(&model));
148 CHECK(model != nullptr);
149 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
150 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
151 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
152 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kScalarType));
153 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
154 ANeuralNetworksModel_setOperandValue(model, 3, &kNoActivation, sizeof(kNoActivation)));
155 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
156 ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD,
157 std::size(kOperationInputs),
158 std::data(kOperationInputs),
159 std::size(kOperationOutputs),
160 std::data(kOperationOutputs)));
161 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
162 ANeuralNetworksModel_identifyInputsAndOutputs(model, std::size(kModelInputs),
163 std::data(kModelInputs),
164 std::size(kModelOutputs),
165 std::data(kModelOutputs)));
166 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_finish(model));
167
168 // Create compilation
169 ANeuralNetworksCompilation* compilation = nullptr;
170 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_create(model, &compilation));
171 CHECK(compilation != nullptr);
172 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_finish(compilation));
173
174 // Create execution
175 Matrix3x4 output;
176 ANeuralNetworksExecution* execution = nullptr;
177 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksExecution_create(compilation, &execution));
178 CHECK(execution != nullptr);
179 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
180 ANeuralNetworksExecution_setInput(execution, 0, nullptr, &kMatrix1, sizeof(kMatrix1)));
181 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
182 ANeuralNetworksExecution_setInput(execution, 1, nullptr, &kMatrix2, sizeof(kMatrix2)));
183 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
184 ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &output, sizeof(output)));
185 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksExecution_compute(execution));
186
187 // Cleanup
188 ANeuralNetworksExecution_free(execution);
189 ANeuralNetworksCompilation_free(compilation);
190 ANeuralNetworksModel_free(model);
191 }
192
193 // Create a model that can add two tensors using a one node graph.
executionFailure()194 void executionFailure() {
195 // Create model
196 ANeuralNetworksModel* model = nullptr;
197 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_create(&model));
198 CHECK(model != nullptr);
199 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
200 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kMatrixType));
201 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
202 ANeuralNetworksModel_addOperand(model, &kMatrixUnknownDimensionsType));
203 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_addOperand(model, &kScalarType));
204 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
205 ANeuralNetworksModel_setOperandValue(model, 3, &kNoActivation, sizeof(kNoActivation)));
206 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
207 ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD,
208 std::size(kOperationInputs),
209 std::data(kOperationInputs),
210 std::size(kOperationOutputs),
211 std::data(kOperationOutputs)));
212 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
213 ANeuralNetworksModel_identifyInputsAndOutputs(model, std::size(kModelInputs),
214 std::data(kModelInputs),
215 std::size(kModelOutputs),
216 std::data(kModelOutputs)));
217 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksModel_finish(model));
218
219 // Create compilation
220 ANeuralNetworksCompilation* compilation = nullptr;
221 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_create(model, &compilation));
222 CHECK(compilation != nullptr);
223 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksCompilation_finish(compilation));
224
225 // Create execution
226 InsufficientMatrixSize output;
227 ANeuralNetworksExecution* execution = nullptr;
228 CHECK_EQ(ANEURALNETWORKS_NO_ERROR, ANeuralNetworksExecution_create(compilation, &execution));
229 CHECK(execution != nullptr);
230 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
231 ANeuralNetworksExecution_setInput(execution, 0, nullptr, &kMatrix1, sizeof(kMatrix1)));
232 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
233 ANeuralNetworksExecution_setInput(execution, 1, nullptr, &kMatrix2, sizeof(kMatrix2)));
234 // This will cause ANeuralNetworksExecution_compute to fail because the provided output buffer
235 // is too small.
236 CHECK_EQ(ANEURALNETWORKS_NO_ERROR,
237 ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &output, sizeof(output)));
238 CHECK_EQ(ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE, ANeuralNetworksExecution_compute(execution));
239
240 // Cleanup
241 ANeuralNetworksCompilation_free(compilation);
242 ANeuralNetworksModel_free(model);
243 }
244
245 } // namespace
246
247 extern "C" JNIEXPORT void JNICALL
Java_com_android_nn_stats_app_NnapiDeviceActivity_trigger_1libneuralnetworks_1atoms(JNIEnv *,jobject)248 Java_com_android_nn_stats_app_NnapiDeviceActivity_trigger_1libneuralnetworks_1atoms(
249 JNIEnv*, jobject /*this*/) {
250 compilationSuccess();
251 compilationFailure();
252 executionSuccess();
253 executionFailure();
254
255 // Sleep for a short period of time to make sure all the atoms have been sent.
256 std::this_thread::sleep_for(std::chrono::seconds(1));
257 }
258