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 // Provides C++ classes to more easily use the Neural Networks API.
18 
19 #ifndef ANDROID_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H
20 #define ANDROID_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H
21 
22 #include "NeuralNetworks.h"
23 
24 #include <math.h>
25 #include <vector>
26 
27 namespace android {
28 namespace nn {
29 namespace wrapper {
30 
31 enum class Type {
32     FLOAT32 = ANEURALNETWORKS_FLOAT32,
33     INT32 = ANEURALNETWORKS_INT32,
34     UINT32 = ANEURALNETWORKS_UINT32,
35     TENSOR_FLOAT32 = ANEURALNETWORKS_TENSOR_FLOAT32,
36     TENSOR_INT32 = ANEURALNETWORKS_TENSOR_INT32,
37     TENSOR_QUANT8_ASYMM = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
38 };
39 
40 enum class ExecutePreference {
41     PREFER_LOW_POWER = ANEURALNETWORKS_PREFER_LOW_POWER,
42     PREFER_FAST_SINGLE_ANSWER = ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER,
43     PREFER_SUSTAINED_SPEED = ANEURALNETWORKS_PREFER_SUSTAINED_SPEED
44 };
45 
46 enum class Result {
47     NO_ERROR = ANEURALNETWORKS_NO_ERROR,
48     OUT_OF_MEMORY = ANEURALNETWORKS_OUT_OF_MEMORY,
49     INCOMPLETE = ANEURALNETWORKS_INCOMPLETE,
50     UNEXPECTED_NULL = ANEURALNETWORKS_UNEXPECTED_NULL,
51     BAD_DATA = ANEURALNETWORKS_BAD_DATA,
52     OP_FAILED = ANEURALNETWORKS_OP_FAILED,
53     UNMAPPABLE = ANEURALNETWORKS_UNMAPPABLE,
54     BAD_STATE = ANEURALNETWORKS_BAD_STATE,
55 };
56 
57 struct OperandType {
58     ANeuralNetworksOperandType operandType;
59     std::vector<uint32_t> dimensions;
60 
61     OperandType(Type type, std::vector<uint32_t> d, float scale = 0.0f, int32_t zeroPoint = 0)
dimensionsOperandType62             : dimensions(std::move(d)) {
63         operandType = {
64             .type = static_cast<int32_t>(type),
65             .dimensionCount = static_cast<uint32_t>(dimensions.size()),
66             .dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr,
67             .scale = scale,
68             .zeroPoint = zeroPoint,
69         };
70     }
71 };
72 
73 class Memory {
74 public:
Memory(size_t size,int protect,int fd,size_t offset)75     Memory(size_t size, int protect, int fd, size_t offset) {
76         mValid = ANeuralNetworksMemory_createFromFd(size, protect, fd, offset, &mMemory) ==
77                  ANEURALNETWORKS_NO_ERROR;
78     }
79 
~Memory()80     ~Memory() { ANeuralNetworksMemory_free(mMemory); }
81 
82     // Disallow copy semantics to ensure the runtime object can only be freed
83     // once. Copy semantics could be enabled if some sort of reference counting
84     // or deep-copy system for runtime objects is added later.
85     Memory(const Memory&) = delete;
86     Memory& operator=(const Memory&) = delete;
87 
88     // Move semantics to remove access to the runtime object from the wrapper
89     // object that is being moved. This ensures the runtime object will be
90     // freed only once.
Memory(Memory && other)91     Memory(Memory&& other) { *this = std::move(other); }
92     Memory& operator=(Memory&& other) {
93         if (this != &other) {
94             ANeuralNetworksMemory_free(mMemory);
95             mMemory = other.mMemory;
96             mValid = other.mValid;
97             other.mMemory = nullptr;
98             other.mValid = false;
99         }
100         return *this;
101     }
102 
get()103     ANeuralNetworksMemory* get() const { return mMemory; }
isValid()104     bool isValid() const { return mValid; }
105 
106 private:
107     ANeuralNetworksMemory* mMemory = nullptr;
108     bool mValid = true;
109 };
110 
111 class Model {
112 public:
Model()113     Model() {
114         // TODO handle the value returned by this call
115         ANeuralNetworksModel_create(&mModel);
116     }
~Model()117     ~Model() { ANeuralNetworksModel_free(mModel); }
118 
119     // Disallow copy semantics to ensure the runtime object can only be freed
120     // once. Copy semantics could be enabled if some sort of reference counting
121     // or deep-copy system for runtime objects is added later.
122     Model(const Model&) = delete;
123     Model& operator=(const Model&) = delete;
124 
125     // Move semantics to remove access to the runtime object from the wrapper
126     // object that is being moved. This ensures the runtime object will be
127     // freed only once.
Model(Model && other)128     Model(Model&& other) { *this = std::move(other); }
129     Model& operator=(Model&& other) {
130         if (this != &other) {
131             ANeuralNetworksModel_free(mModel);
132             mModel = other.mModel;
133             mNextOperandId = other.mNextOperandId;
134             mValid = other.mValid;
135             other.mModel = nullptr;
136             other.mNextOperandId = 0;
137             other.mValid = false;
138         }
139         return *this;
140     }
141 
finish()142     Result finish() {
143         if (mValid) {
144             auto result = static_cast<Result>(ANeuralNetworksModel_finish(mModel));
145             if (result != Result::NO_ERROR) {
146                 mValid = false;
147             }
148             return result;
149         } else {
150             return Result::BAD_STATE;
151         }
152     }
153 
addOperand(const OperandType * type)154     uint32_t addOperand(const OperandType* type) {
155         if (ANeuralNetworksModel_addOperand(mModel, &(type->operandType)) !=
156             ANEURALNETWORKS_NO_ERROR) {
157             mValid = false;
158         }
159         return mNextOperandId++;
160     }
161 
setOperandValue(uint32_t index,const void * buffer,size_t length)162     void setOperandValue(uint32_t index, const void* buffer, size_t length) {
163         if (ANeuralNetworksModel_setOperandValue(mModel, index, buffer, length) !=
164             ANEURALNETWORKS_NO_ERROR) {
165             mValid = false;
166         }
167     }
168 
setOperandValueFromMemory(uint32_t index,const Memory * memory,uint32_t offset,size_t length)169     void setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
170                                    size_t length) {
171         if (ANeuralNetworksModel_setOperandValueFromMemory(mModel, index, memory->get(), offset,
172                                                            length) != ANEURALNETWORKS_NO_ERROR) {
173             mValid = false;
174         }
175     }
176 
addOperation(ANeuralNetworksOperationType type,const std::vector<uint32_t> & inputs,const std::vector<uint32_t> & outputs)177     void addOperation(ANeuralNetworksOperationType type, const std::vector<uint32_t>& inputs,
178                       const std::vector<uint32_t>& outputs) {
179         if (ANeuralNetworksModel_addOperation(mModel, type, static_cast<uint32_t>(inputs.size()),
180                                               inputs.data(), static_cast<uint32_t>(outputs.size()),
181                                               outputs.data()) != ANEURALNETWORKS_NO_ERROR) {
182             mValid = false;
183         }
184     }
identifyInputsAndOutputs(const std::vector<uint32_t> & inputs,const std::vector<uint32_t> & outputs)185     void identifyInputsAndOutputs(const std::vector<uint32_t>& inputs,
186                                   const std::vector<uint32_t>& outputs) {
187         if (ANeuralNetworksModel_identifyInputsAndOutputs(
188                         mModel, static_cast<uint32_t>(inputs.size()), inputs.data(),
189                         static_cast<uint32_t>(outputs.size()),
190                         outputs.data()) != ANEURALNETWORKS_NO_ERROR) {
191             mValid = false;
192         }
193     }
194 
relaxComputationFloat32toFloat16(bool isRelax)195     void relaxComputationFloat32toFloat16(bool isRelax) {
196         if (ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, isRelax) ==
197                 ANEURALNETWORKS_NO_ERROR) {
198             mRelaxed = isRelax;
199         }
200     }
201 
getHandle()202     ANeuralNetworksModel* getHandle() const { return mModel; }
isValid()203     bool isValid() const { return mValid; }
isRelaxed()204     bool isRelaxed() const { return mRelaxed; }
205 
206 private:
207     ANeuralNetworksModel* mModel = nullptr;
208     // We keep track of the operand ID as a convenience to the caller.
209     uint32_t mNextOperandId = 0;
210     bool mValid = true;
211     bool mRelaxed = false;
212 };
213 
214 class Event {
215 public:
Event()216     Event() {}
~Event()217     ~Event() { ANeuralNetworksEvent_free(mEvent); }
218 
219     // Disallow copy semantics to ensure the runtime object can only be freed
220     // once. Copy semantics could be enabled if some sort of reference counting
221     // or deep-copy system for runtime objects is added later.
222     Event(const Event&) = delete;
223     Event& operator=(const Event&) = delete;
224 
225     // Move semantics to remove access to the runtime object from the wrapper
226     // object that is being moved. This ensures the runtime object will be
227     // freed only once.
Event(Event && other)228     Event(Event&& other) { *this = std::move(other); }
229     Event& operator=(Event&& other) {
230         if (this != &other) {
231             ANeuralNetworksEvent_free(mEvent);
232             mEvent = other.mEvent;
233             other.mEvent = nullptr;
234         }
235         return *this;
236     }
237 
wait()238     Result wait() { return static_cast<Result>(ANeuralNetworksEvent_wait(mEvent)); }
239 
240     // Only for use by Execution
set(ANeuralNetworksEvent * newEvent)241     void set(ANeuralNetworksEvent* newEvent) {
242         ANeuralNetworksEvent_free(mEvent);
243         mEvent = newEvent;
244     }
245 
246 private:
247     ANeuralNetworksEvent* mEvent = nullptr;
248 };
249 
250 class Compilation {
251 public:
Compilation(const Model * model)252     Compilation(const Model* model) {
253         int result = ANeuralNetworksCompilation_create(model->getHandle(), &mCompilation);
254         if (result != 0) {
255             // TODO Handle the error
256         }
257     }
258 
~Compilation()259     ~Compilation() { ANeuralNetworksCompilation_free(mCompilation); }
260 
261     // Disallow copy semantics to ensure the runtime object can only be freed
262     // once. Copy semantics could be enabled if some sort of reference counting
263     // or deep-copy system for runtime objects is added later.
264     Compilation(const Compilation&) = delete;
265     Compilation& operator=(const Compilation&) = delete;
266 
267     // Move semantics to remove access to the runtime object from the wrapper
268     // object that is being moved. This ensures the runtime object will be
269     // freed only once.
Compilation(Compilation && other)270     Compilation(Compilation&& other) { *this = std::move(other); }
271     Compilation& operator=(Compilation&& other) {
272         if (this != &other) {
273             ANeuralNetworksCompilation_free(mCompilation);
274             mCompilation = other.mCompilation;
275             other.mCompilation = nullptr;
276         }
277         return *this;
278     }
279 
setPreference(ExecutePreference preference)280     Result setPreference(ExecutePreference preference) {
281         return static_cast<Result>(ANeuralNetworksCompilation_setPreference(
282                     mCompilation, static_cast<int32_t>(preference)));
283     }
284 
finish()285     Result finish() { return static_cast<Result>(ANeuralNetworksCompilation_finish(mCompilation)); }
286 
getHandle()287     ANeuralNetworksCompilation* getHandle() const { return mCompilation; }
288 
289 private:
290     ANeuralNetworksCompilation* mCompilation = nullptr;
291 };
292 
293 class Execution {
294 public:
Execution(const Compilation * compilation)295     Execution(const Compilation* compilation) {
296         int result = ANeuralNetworksExecution_create(compilation->getHandle(), &mExecution);
297         if (result != 0) {
298             // TODO Handle the error
299         }
300     }
301 
~Execution()302     ~Execution() { ANeuralNetworksExecution_free(mExecution); }
303 
304     // Disallow copy semantics to ensure the runtime object can only be freed
305     // once. Copy semantics could be enabled if some sort of reference counting
306     // or deep-copy system for runtime objects is added later.
307     Execution(const Execution&) = delete;
308     Execution& operator=(const Execution&) = delete;
309 
310     // Move semantics to remove access to the runtime object from the wrapper
311     // object that is being moved. This ensures the runtime object will be
312     // freed only once.
Execution(Execution && other)313     Execution(Execution&& other) { *this = std::move(other); }
314     Execution& operator=(Execution&& other) {
315         if (this != &other) {
316             ANeuralNetworksExecution_free(mExecution);
317             mExecution = other.mExecution;
318             other.mExecution = nullptr;
319         }
320         return *this;
321     }
322 
323     Result setInput(uint32_t index, const void* buffer, size_t length,
324                     const ANeuralNetworksOperandType* type = nullptr) {
325         return static_cast<Result>(
326                     ANeuralNetworksExecution_setInput(mExecution, index, type, buffer, length));
327     }
328 
329     Result setInputFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
330                               uint32_t length, const ANeuralNetworksOperandType* type = nullptr) {
331         return static_cast<Result>(ANeuralNetworksExecution_setInputFromMemory(
332                     mExecution, index, type, memory->get(), offset, length));
333     }
334 
335     Result setOutput(uint32_t index, void* buffer, size_t length,
336                      const ANeuralNetworksOperandType* type = nullptr) {
337         return static_cast<Result>(
338                     ANeuralNetworksExecution_setOutput(mExecution, index, type, buffer, length));
339     }
340 
341     Result setOutputFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
342                                uint32_t length, const ANeuralNetworksOperandType* type = nullptr) {
343         return static_cast<Result>(ANeuralNetworksExecution_setOutputFromMemory(
344                     mExecution, index, type, memory->get(), offset, length));
345     }
346 
startCompute(Event * event)347     Result startCompute(Event* event) {
348         ANeuralNetworksEvent* ev = nullptr;
349         Result result = static_cast<Result>(ANeuralNetworksExecution_startCompute(mExecution, &ev));
350         event->set(ev);
351         return result;
352     }
353 
compute()354     Result compute() {
355         ANeuralNetworksEvent* event = nullptr;
356         Result result =
357                     static_cast<Result>(ANeuralNetworksExecution_startCompute(mExecution, &event));
358         if (result != Result::NO_ERROR) {
359             return result;
360         }
361         // TODO how to manage the lifetime of events when multiple waiters is not
362         // clear.
363         result = static_cast<Result>(ANeuralNetworksEvent_wait(event));
364         ANeuralNetworksEvent_free(event);
365         return result;
366     }
367 
368 private:
369     ANeuralNetworksExecution* mExecution = nullptr;
370 };
371 
372 }  // namespace wrapper
373 }  // namespace nn
374 }  // namespace android
375 
376 #endif  //  ANDROID_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H
377