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