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