6114b74b059609734fc8077737ac119e0a7eb4ad
[platform/core/ml/nnfw.git] / runtime / onert / frontend / nnapi / wrapper / ANeuralNetworksExecution.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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 "ANeuralNetworksExecution.h"
18 #include "NNAPIConvert.h"
19 #include "util/logging.h"
20
21 const onert::ir::OperandIndex ANeuralNetworksExecution::getInputOperandIndex(int32_t index) noexcept
22 {
23   if (index < 0)
24   {
25     // Negative index: return invalid index
26     return onert::ir::OperandIndex{};
27   }
28
29   uint32_t cast_index = static_cast<uint32_t>(index);
30   if (cast_index >= _execution->primary_subgraph().getInputs().size())
31   {
32     // Return invalid index
33     return onert::ir::OperandIndex{};
34   }
35
36   onert::ir::IOIndex input_index{cast_index};
37   const auto operand_index = _execution->primary_subgraph().getInputs().at(input_index);
38   return operand_index;
39 }
40
41 const onert::ir::OperandIndex
42 ANeuralNetworksExecution::getOutputOperandIndex(int32_t index) noexcept
43 {
44   if (index < 0)
45   {
46     // Negative index: return invalid index
47     return onert::ir::OperandIndex{};
48   }
49
50   uint32_t cast_index = static_cast<uint32_t>(index);
51   if (cast_index >= _execution->primary_subgraph().getOutputs().size())
52   {
53     // Return invalid index
54     return onert::ir::OperandIndex{};
55   }
56
57   onert::ir::IOIndex output_index{cast_index};
58   const auto operand_index = _execution->primary_subgraph().getOutputs().at(output_index);
59   return operand_index;
60 }
61
62 bool ANeuralNetworksExecution::compareDataType(const ANeuralNetworksOperandType *type,
63                                                const onert::ir::OperandIndex index) noexcept
64 {
65   try
66   {
67     const auto operand_type = _execution->primary_subgraph().operands().at(index).typeInfo();
68     const auto typeInfo = NNAPIConvert::getTypeInfo(type);
69
70     if (operand_type != typeInfo)
71     {
72       // Data type mismatch
73       return false;
74     }
75   }
76   catch (const std::exception &e)
77   {
78     VERBOSE(EXCEPTION) << e.what() << std::endl;
79
80     return false;
81   }
82
83   return true;
84 }
85
86 bool ANeuralNetworksExecution::compareShape(const ANeuralNetworksOperandType *type,
87                                             const onert::ir::OperandIndex index) noexcept
88 {
89   // Passed shape should be specified
90   if (hasUnspecifiedDims(index))
91   {
92     return false;
93   }
94
95   const auto &operand_shape = _execution->primary_subgraph().operands().at(index).shape();
96   const auto &shape_from_type = NNAPIConvert::getShape(type);
97
98   return operand_shape == shape_from_type;
99 }
100
101 bool ANeuralNetworksExecution::IsOptionalInput(const onert::ir::OperandIndex index) noexcept
102 {
103   const auto &operand_shape = _execution->primary_subgraph().operands().at(index).shape();
104   for (int32_t i = 0; i < operand_shape.rank(); ++i)
105   {
106     if (operand_shape.dim(i) != 0)
107       return false;
108   }
109   return true;
110 }
111
112 bool ANeuralNetworksExecution::hasUnspecifiedDims(const onert::ir::OperandIndex index) noexcept
113 {
114   const auto operand_shape = _execution->primary_subgraph().operands().at(index).shape();
115
116   return operand_shape.hasUnspecifiedDims();
117 }
118
119 size_t ANeuralNetworksExecution::getOperandSize(const onert::ir::OperandIndex index) noexcept
120 {
121   try
122   {
123     return _execution->primary_subgraph().operands().at(index).operandSize();
124   }
125   catch (const std::exception &e)
126   {
127     VERBOSE(EXCEPTION) << e.what() << std::endl;
128
129     return 0;
130   }
131 }
132
133 bool ANeuralNetworksExecution::setInput(uint32_t index, const ANeuralNetworksOperandType *type,
134                                         const void *buffer, size_t length) noexcept
135 {
136   try
137   {
138     onert::ir::IOIndex input_index{index};
139     const auto operand_index = getInputOperandIndex(index);
140
141     const auto type_info = _execution->primary_subgraph().operands().at(operand_index).typeInfo();
142     const auto shape = (type != nullptr)
143                            ? NNAPIConvert::getShape(type)
144                            : _execution->primary_subgraph().operands().at(operand_index).shape();
145
146     // NOTE The nnapi does not provide setting io_layout and not support changing layout. In other
147     // words, we can assume that io_layout from nnapi always is the same as layout of the used
148     // model.
149     // TODO Set layout of model
150     _execution->setInput(input_index, type_info, shape, buffer, length, onert::ir::Layout::NHWC);
151   }
152   catch (const std::exception &e)
153   {
154     VERBOSE(EXCEPTION) << e.what() << std::endl;
155
156     return false;
157   }
158
159   return true;
160 }
161
162 bool ANeuralNetworksExecution::setOptionalInput(uint32_t index,
163                                                 const ANeuralNetworksOperandType *type,
164                                                 const void *buffer, size_t length) noexcept
165 {
166   assert(type == nullptr);
167   assert(buffer == nullptr);
168   assert(length == 0);
169   try
170   {
171     onert::ir::IOIndex input_index{index};
172     const auto operand_index = getInputOperandIndex(index);
173
174     const auto type_info = _execution->primary_subgraph().operands().at(operand_index).typeInfo();
175     const auto shape = (type != nullptr)
176                            ? NNAPIConvert::getShape(type)
177                            : _execution->primary_subgraph().operands().at(operand_index).shape();
178
179     // ANeuralNetworksExecution::setInput() uses only shape information
180     ANeuralNetworksOperandType optional_input_type;
181     optional_input_type.dimensionCount = shape.rank();
182     std::vector<uint32_t> dims(optional_input_type.dimensionCount);
183     for (uint32_t i = 0; i < optional_input_type.dimensionCount; ++i)
184     {
185       dims.at(i) = shape.dim(i);
186     }
187     optional_input_type.dimensions = dims.data();
188
189     return setInput(index, &optional_input_type, buffer, length);
190   }
191   catch (const std::exception &e)
192   {
193     VERBOSE(EXCEPTION) << e.what() << std::endl;
194
195     return false;
196   }
197
198   return true;
199 }
200
201 bool ANeuralNetworksExecution::setOutput(uint32_t index, const ANeuralNetworksOperandType *type,
202                                          void *buffer, size_t length) noexcept
203 {
204   try
205   {
206     onert::ir::IOIndex output_index{index};
207     const auto operand_index = getOutputOperandIndex(index);
208
209     const auto type_info = _execution->primary_subgraph().operands().at(operand_index).typeInfo();
210     const auto shape = (type != nullptr)
211                            ? NNAPIConvert::getShape(type)
212                            : _execution->primary_subgraph().operands().at(operand_index).shape();
213
214     // NOTE The nnapi does not provide setting io_layout and not support changing layout. In other
215     // words, we can assume that io_layout from nnapi always is the same as layout of the used
216     // model.
217     // TODO Set layout of model
218     _execution->setOutput(output_index, type_info, shape, buffer, length, onert::ir::Layout::NHWC);
219   }
220   catch (const std::exception &e)
221   {
222     VERBOSE(EXCEPTION) << e.what() << std::endl;
223
224     return false;
225   }
226
227   return true;
228 }
229
230 bool ANeuralNetworksExecution::startExecute(void) noexcept
231 {
232   try
233   {
234     _execution->startExecute();
235   }
236   catch (const std::exception &e)
237   {
238     VERBOSE(EXCEPTION) << e.what() << std::endl;
239
240     return false;
241   }
242
243   return true;
244 }
245
246 bool ANeuralNetworksExecution::execute(void) noexcept
247 {
248   try
249   {
250     _execution->execute();
251   }
252   catch (const std::exception &e)
253   {
254     VERBOSE(EXCEPTION) << e.what() << std::endl;
255
256     return false;
257   }
258
259   return true;
260 }
261
262 const std::shared_ptr<onert::exec::Execution> ANeuralNetworksExecution::instance(void) noexcept
263 {
264   return _execution;
265 }
266
267 bool ANeuralNetworksExecution::getOutputOperandRank(uint32_t index, uint32_t *rank) noexcept
268 {
269   try
270   {
271     onert::ir::IOIndex output_index{index};
272
273     // Check execution is finished
274     if (!_execution->isFinished())
275     {
276       return false;
277     }
278
279     const auto shape = _execution->getOutputShape(output_index);
280     if (shape.hasUnspecifiedDims())
281     {
282       throw std::runtime_error{"Internal error: Output tensor has unspecified dims"};
283     }
284
285     *rank = shape.rank();
286   }
287   catch (const std::exception &e)
288   {
289     VERBOSE(EXCEPTION) << e.what() << std::endl;
290
291     return false;
292   }
293
294   return true;
295 }
296
297 bool ANeuralNetworksExecution::getOutputOperandDimensions(uint32_t index, uint32_t *dimensions)
298 {
299   try
300   {
301     onert::ir::IOIndex output_index{index};
302
303     // Check execution is finished
304     if (!_execution->isFinished())
305     {
306       return false;
307     }
308
309     const auto shape = _execution->getOutputShape(output_index);
310     if (shape.hasUnspecifiedDims())
311     {
312       throw std::runtime_error{"Internal error: Output tensor has unspecified dims"};
313     }
314
315     for (int i = 0; i < shape.rank(); i++)
316     {
317       auto dim = shape.dim(i);
318
319       if (dim <= 0)
320       {
321         throw std::runtime_error{"Invalid dimension value"};
322       }
323
324       dimensions[i] = static_cast<uint32_t>(dim);
325     }
326   }
327   catch (const std::exception &e)
328   {
329     VERBOSE(EXCEPTION) << e.what() << std::endl;
330
331     return false;
332   }
333
334   return true;
335 }