Imported Upstream version 1.12.0
[platform/core/ml/nnfw.git] / runtime / onert / test / core / interp / ExecManager.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 <gtest/gtest.h>
18
19 #include <memory>
20
21 #include "ir/Graph.h"
22 #include "interp/InterpExecutor.h"
23 #include "exec/Execution.h"
24 #include "ir/operation/BinaryArithmetic.h"
25
26 namespace
27 {
28
29 using namespace onert::ir;
30 using InterpExecutor = onert::interp::InterpExecutor;
31 using Execution = onert::exec::Execution;
32 using ExecutorMap = onert::exec::ExecutorMap;
33
34 class InterpExecutorTest : public ::testing::Test
35 {
36 protected:
37   virtual void SetUp() {}
38   void CreateSimpleModel()
39   {
40     // Model: one elementwise add operation
41     // model input: lhs, rhs
42     // model output: add result
43     // lhs, rhs, result shape: {1, 2, 2, 1}
44     // activation: none (constant)
45     _graph = std::make_unique<Graph>();
46
47     // Add operands
48
49     Shape shape{1, 2, 2, 1};
50     TypeInfo type{DataType::INT32};
51     Shape shape_scalar(0);
52     TypeInfo type_scalar{DataType::INT32};
53
54     auto operand_lhs = _graph->addOperand(shape, type);
55     auto operand_rhs = _graph->addOperand(shape, type);
56     auto operand_result = _graph->addOperand(shape, type);
57
58     // Add operations
59
60     operation::BinaryArithmetic::Param param;
61     param.arithmetic_type = operation::BinaryArithmetic::ArithmeticType::ADD;
62     param.activation = Activation::NONE;
63     auto input_set = OperandIndexSequence{operand_lhs, operand_rhs};
64     auto output_set = OperandIndexSequence{operand_result};
65     _graph->addOperation(
66       std::make_unique<operation::BinaryArithmetic>(input_set, output_set, param));
67
68     // Identify model inputs and outputs
69
70     _graph->getInputs().append(operand_lhs);
71     _graph->getInputs().append(operand_rhs);
72     _graph->getOutputs().append(operand_result);
73
74     _graph->finishBuilding();
75
76     auto subgs = std::make_shared<onert::ir::Subgraphs>();
77     subgs->push(onert::ir::SubgraphIndex{0}, _graph);
78     _graph->setSubgraphs(subgs);
79
80     _executors = std::make_shared<ExecutorMap>();
81     _executors->insert(
82       std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
83   }
84
85   void CreateTwoStepModel()
86   {
87     // Model: two elementwise add operation
88     // model input: lhs, rhs1
89     // model output: second add result (result2)
90     // constant: rhs2
91     // result1 <= (lhs + rhs)
92     // result2 <= (result1 + rhs2)
93     // lhs, rhs1, rh2, result1, result2 shape: {1, 2, 2, 1}
94     // activation: none (constant)
95     _graph = std::make_unique<Graph>();
96
97     // 1st add operands (result1 <= lhs + rhs1)
98
99     Shape shape{1, 2, 2, 1};
100     TypeInfo type{DataType::INT32};
101     Shape shape_scalar(0);
102     TypeInfo type_scalar{DataType::INT32};
103
104     static int32_t rhs2_data[4] = {3, 1, -1, 5};
105
106     auto operand_lhs = _graph->addOperand(shape, type);
107     auto operand_rhs1 = _graph->addOperand(shape, type);
108     auto operand_result1 = _graph->addOperand(shape, type);
109     auto operand_rhs2 = _graph->addOperand(shape, type);
110     auto operand_result2 = _graph->addOperand(shape, type);
111     _graph->operands()
112       .at(operand_rhs2)
113       .data(std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(&rhs2_data), 16));
114
115     // 2nd add operations (result2 <= result1 + rhs2)
116
117     operation::BinaryArithmetic::Param param1;
118     param1.arithmetic_type = operation::BinaryArithmetic::ArithmeticType::ADD;
119     param1.activation = Activation::NONE;
120     auto input_set1 = OperandIndexSequence{operand_lhs, operand_rhs1};
121     auto output_set1 = OperandIndexSequence{operand_result1};
122     _graph->addOperation(
123       std::make_unique<operation::BinaryArithmetic>(input_set1, output_set1, param1));
124
125     operation::BinaryArithmetic::Param param2;
126     param2.arithmetic_type = operation::BinaryArithmetic::ArithmeticType::ADD;
127     param2.activation = Activation::NONE;
128     auto input_set2 = OperandIndexSequence{operand_result1, operand_rhs2};
129     auto output_set2 = OperandIndexSequence{operand_result2};
130     _graph->addOperation(
131       std::make_unique<operation::BinaryArithmetic>(input_set2, output_set2, param2));
132
133     // Identify model inputs and outputs
134
135     _graph->getInputs().append(operand_lhs);
136     _graph->getInputs().append(operand_rhs1);
137     _graph->getOutputs().append(operand_result2);
138
139     _graph->finishBuilding();
140
141     auto subgs = std::make_shared<onert::ir::Subgraphs>();
142     subgs->push(onert::ir::SubgraphIndex{0}, _graph);
143     _graph->setSubgraphs(subgs);
144
145     _executors = std::make_shared<ExecutorMap>();
146     _executors->insert(
147       std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
148   }
149
150   void CreateUnspecifiedDimensionsModel()
151   {
152     // Model: one elementwise add operation
153     // model input: lhs, rhs
154     // model output: add result
155     // lhs, rhs, result shape: {1, unknown, 2, 1}
156     // activation: none (constant)
157     _graph = std::make_unique<Graph>();
158
159     // Add operands
160
161     Shape shape{1, 0, 2, 1};
162     TypeInfo type{DataType::INT32};
163     Shape shape_scalar(0);
164     TypeInfo type_scalar{DataType::INT32};
165
166     auto operand_lhs = _graph->addOperand(shape, type);
167     auto operand_rhs = _graph->addOperand(shape, type);
168
169     auto operand_activation = _graph->addOperand(shape_scalar, type_scalar);
170     _graph->operands()
171       .at(operand_activation)
172       .data(std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(&_activation_value), 4));
173
174     auto operand_result = _graph->addOperand(shape, type);
175
176     // Add operations
177
178     operation::BinaryArithmetic::Param param;
179     param.arithmetic_type = operation::BinaryArithmetic::ArithmeticType::ADD;
180     param.activation = Activation::NONE;
181     auto input_set = OperandIndexSequence{operand_lhs, operand_rhs};
182     auto output_set = OperandIndexSequence{operand_result};
183     _graph->addOperation(
184       std::make_unique<operation::BinaryArithmetic>(input_set, output_set, param));
185
186     // Identify model inputs and outputs
187
188     _graph->getInputs().append(operand_lhs);
189     _graph->getInputs().append(operand_rhs);
190     _graph->getOutputs().append(operand_result);
191
192     _graph->finishBuilding();
193
194     auto subgs = std::make_shared<onert::ir::Subgraphs>();
195     subgs->push(onert::ir::SubgraphIndex{0}, _graph);
196     _graph->setSubgraphs(subgs);
197
198     _executors = std::make_shared<ExecutorMap>();
199     _executors->insert(
200       std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
201   }
202
203   void createExecution() { _execution = std::make_unique<Execution>(_executors); }
204
205   virtual void TearDown() { _executors = nullptr; }
206
207   std::shared_ptr<Graph> _graph{nullptr};
208   std::shared_ptr<ExecutorMap> _executors{nullptr};
209   std::unique_ptr<Execution> _execution{nullptr};
210   const int32_t _activation_value{0};
211 };
212
213 TEST_F(InterpExecutorTest, create_empty)
214 {
215   Graph graph;
216   graph.finishBuilding();
217   auto executor = std::make_unique<InterpExecutor>(graph);
218   ASSERT_NE(executor, nullptr);
219 }
220
221 TEST_F(InterpExecutorTest, create_simple)
222 {
223   CreateSimpleModel();
224   ASSERT_NE(_executors, nullptr);
225   ASSERT_NE(_executors->at(onert::ir::SubgraphIndex{0}), nullptr);
226 }
227
228 TEST_F(InterpExecutorTest, neg_setInput)
229 {
230   CreateSimpleModel();
231   createExecution();
232
233   auto input1 = IOIndex{0};
234   const int32_t input1_buffer[4] = {1, 0, -1, -2};
235
236   EXPECT_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 4),
237                std::runtime_error);
238   EXPECT_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 12),
239                std::runtime_error);
240   EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
241 }
242
243 TEST_F(InterpExecutorTest, neg_setOutput)
244 {
245   CreateSimpleModel();
246   createExecution();
247
248   auto output = IOIndex{0};
249   auto output_idx = _graph->getOutputs().at(output);
250
251   int32_t output_buffer[4] = {};
252
253   EXPECT_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 4),
254                std::runtime_error);
255   EXPECT_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 12),
256                std::runtime_error);
257   EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
258 }
259
260 TEST_F(InterpExecutorTest, neg_setInputForUnspecifiedDimensions)
261 {
262   CreateUnspecifiedDimensionsModel();
263   createExecution();
264
265   auto input1 = IOIndex{0};
266   const int32_t input1_buffer[4] = {1, 0, -1, -2};
267
268   TypeInfo operand_type{DataType::INT32};
269   Shape operand_shape{1, 2, 2, 1};
270
271   EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape,
272                                     reinterpret_cast<const void *>(input1_buffer), 4),
273                std::runtime_error);
274   EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape,
275                                     reinterpret_cast<const void *>(input1_buffer), 12),
276                std::runtime_error);
277   EXPECT_NO_THROW(_execution->setInput(input1, operand_type, operand_shape,
278                                        reinterpret_cast<const void *>(input1_buffer), 16));
279 }
280
281 TEST_F(InterpExecutorTest, neg_setOutputForUnspecifiedDimensions)
282 {
283   CreateUnspecifiedDimensionsModel();
284   createExecution();
285
286   auto output = IOIndex{0};
287   auto output_idx = _graph->getOutputs().at(output);
288
289   TypeInfo operand_type{DataType::INT32};
290   Shape operand_shape{1, 2, 2, 1};
291
292   int32_t output_buffer[4] = {};
293
294   EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape,
295                                      reinterpret_cast<void *>(output_buffer), 4),
296                std::runtime_error);
297   EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape,
298                                      reinterpret_cast<void *>(output_buffer), 12),
299                std::runtime_error);
300   EXPECT_NO_THROW(_execution->setOutput(output, operand_type, operand_shape,
301                                         reinterpret_cast<void *>(output_buffer), 16));
302 }
303
304 TEST_F(InterpExecutorTest, execute)
305 {
306   CreateSimpleModel();
307   createExecution();
308
309   auto input1 = IOIndex{0};
310   auto input2 = IOIndex{1};
311   auto input1_idx = _graph->getInputs().at(input1);
312   auto input2_idx = _graph->getInputs().at(input2);
313
314   const int32_t input1_buffer[4] = {1, 0, -1, -2};
315   const int32_t input2_buffer[4] = {1, -3, 2, -4};
316
317   auto output = IOIndex{0};
318   auto output_idx = _graph->getOutputs().at(output);
319
320   int32_t output_buffer[4] = {};
321
322   EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
323   EXPECT_NO_THROW(_execution->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16));
324   EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
325   EXPECT_NO_THROW(_execution->execute());
326   EXPECT_EQ(output_buffer[0], 2);
327   EXPECT_EQ(output_buffer[1], -3);
328   EXPECT_EQ(output_buffer[2], 1);
329   EXPECT_EQ(output_buffer[3], -6);
330 }
331
332 TEST_F(InterpExecutorTest, executeTwoStep)
333 {
334   CreateTwoStepModel();
335   createExecution();
336
337   auto input1 = IOIndex{0};
338   auto input2 = IOIndex{1};
339   auto input1_idx = _graph->getInputs().at(input1);
340   auto input2_idx = _graph->getInputs().at(input2);
341
342   const int32_t input1_buffer[4] = {1, 0, -1, -2};
343   const int32_t input2_buffer[4] = {1, -3, 2, -4};
344
345   auto output = IOIndex{0};
346   auto output_idx = _graph->getOutputs().at(output);
347
348   int32_t output_buffer[4] = {};
349
350   EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
351   EXPECT_NO_THROW(_execution->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16));
352   EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
353   EXPECT_NO_THROW(_execution->execute());
354   EXPECT_EQ(output_buffer[0], 5);
355   EXPECT_EQ(output_buffer[1], -2);
356   EXPECT_EQ(output_buffer[2], 0);
357   EXPECT_EQ(output_buffer[3], -1);
358 }
359
360 } // namespace