0c7b1b762fe7c80d00993d009e0ed57992348ae4
[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(
173             std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(&_activation_value), 4));
174
175     auto operand_result = _graph->addOperand(shape, type);
176
177     // Add operations
178
179     operation::BinaryArithmetic::Param param;
180     param.arithmetic_type = operation::BinaryArithmetic::ArithmeticType::ADD;
181     param.activation = Activation::NONE;
182     auto input_set = OperandIndexSequence{operand_lhs, operand_rhs};
183     auto output_set = OperandIndexSequence{operand_result};
184     _graph->addOperation(
185         std::make_unique<operation::BinaryArithmetic>(input_set, output_set, param));
186
187     // Identify model inputs and outputs
188
189     _graph->getInputs().append(operand_lhs);
190     _graph->getInputs().append(operand_rhs);
191     _graph->getOutputs().append(operand_result);
192
193     _graph->finishBuilding();
194
195     auto subgs = std::make_shared<onert::ir::Subgraphs>();
196     subgs->push(onert::ir::SubgraphIndex{0}, _graph);
197     _graph->setSubgraphs(subgs);
198
199     _executors = std::make_shared<ExecutorMap>();
200     _executors->insert(
201         std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
202   }
203
204   void createExecution() { _execution = std::make_unique<Execution>(_executors); }
205
206   virtual void TearDown() { _executors = nullptr; }
207
208   std::shared_ptr<Graph> _graph{nullptr};
209   std::shared_ptr<ExecutorMap> _executors{nullptr};
210   std::unique_ptr<Execution> _execution{nullptr};
211   const int32_t _activation_value{0};
212 };
213
214 TEST_F(InterpExecutorTest, create_empty)
215 {
216   Graph graph;
217   graph.finishBuilding();
218   auto executor = std::make_unique<InterpExecutor>(graph);
219   ASSERT_NE(executor, nullptr);
220 }
221
222 TEST_F(InterpExecutorTest, create_simple)
223 {
224   CreateSimpleModel();
225   ASSERT_NE(_executors, nullptr);
226   ASSERT_NE(_executors->at(onert::ir::SubgraphIndex{0}), nullptr);
227 }
228
229 TEST_F(InterpExecutorTest, neg_setInput)
230 {
231   CreateSimpleModel();
232   createExecution();
233
234   auto input1 = IOIndex{0};
235   const int32_t input1_buffer[4] = {1, 0, -1, -2};
236
237   EXPECT_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 4),
238                std::runtime_error);
239   EXPECT_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 12),
240                std::runtime_error);
241   EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
242 }
243
244 TEST_F(InterpExecutorTest, neg_setOutput)
245 {
246   CreateSimpleModel();
247   createExecution();
248
249   auto output = IOIndex{0};
250   auto output_idx = _graph->getOutputs().at(output);
251
252   int32_t output_buffer[4] = {};
253
254   EXPECT_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 4),
255                std::runtime_error);
256   EXPECT_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 12),
257                std::runtime_error);
258   EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
259 }
260
261 TEST_F(InterpExecutorTest, neg_setInputForUnspecifiedDimensions)
262 {
263   CreateUnspecifiedDimensionsModel();
264   createExecution();
265
266   auto input1 = IOIndex{0};
267   const int32_t input1_buffer[4] = {1, 0, -1, -2};
268
269   TypeInfo operand_type{DataType::INT32};
270   Shape operand_shape{1, 2, 2, 1};
271
272   EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape,
273                                     reinterpret_cast<const void *>(input1_buffer), 4),
274                std::runtime_error);
275   EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape,
276                                     reinterpret_cast<const void *>(input1_buffer), 12),
277                std::runtime_error);
278   EXPECT_NO_THROW(_execution->setInput(input1, operand_type, operand_shape,
279                                        reinterpret_cast<const void *>(input1_buffer), 16));
280 }
281
282 TEST_F(InterpExecutorTest, neg_setOutputForUnspecifiedDimensions)
283 {
284   CreateUnspecifiedDimensionsModel();
285   createExecution();
286
287   auto output = IOIndex{0};
288   auto output_idx = _graph->getOutputs().at(output);
289
290   TypeInfo operand_type{DataType::INT32};
291   Shape operand_shape{1, 2, 2, 1};
292
293   int32_t output_buffer[4] = {};
294
295   EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape,
296                                      reinterpret_cast<void *>(output_buffer), 4),
297                std::runtime_error);
298   EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape,
299                                      reinterpret_cast<void *>(output_buffer), 12),
300                std::runtime_error);
301   EXPECT_NO_THROW(_execution->setOutput(output, operand_type, operand_shape,
302                                         reinterpret_cast<void *>(output_buffer), 16));
303 }
304
305 TEST_F(InterpExecutorTest, execute)
306 {
307   CreateSimpleModel();
308   createExecution();
309
310   auto input1 = IOIndex{0};
311   auto input2 = IOIndex{1};
312   auto input1_idx = _graph->getInputs().at(input1);
313   auto input2_idx = _graph->getInputs().at(input2);
314
315   const int32_t input1_buffer[4] = {1, 0, -1, -2};
316   const int32_t input2_buffer[4] = {1, -3, 2, -4};
317
318   auto output = IOIndex{0};
319   auto output_idx = _graph->getOutputs().at(output);
320
321   int32_t output_buffer[4] = {};
322
323   EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
324   EXPECT_NO_THROW(_execution->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16));
325   EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
326   EXPECT_NO_THROW(_execution->execute());
327   EXPECT_EQ(output_buffer[0], 2);
328   EXPECT_EQ(output_buffer[1], -3);
329   EXPECT_EQ(output_buffer[2], 1);
330   EXPECT_EQ(output_buffer[3], -6);
331 }
332
333 TEST_F(InterpExecutorTest, executeTwoStep)
334 {
335   CreateTwoStepModel();
336   createExecution();
337
338   auto input1 = IOIndex{0};
339   auto input2 = IOIndex{1};
340   auto input1_idx = _graph->getInputs().at(input1);
341   auto input2_idx = _graph->getInputs().at(input2);
342
343   const int32_t input1_buffer[4] = {1, 0, -1, -2};
344   const int32_t input2_buffer[4] = {1, -3, 2, -4};
345
346   auto output = IOIndex{0};
347   auto output_idx = _graph->getOutputs().at(output);
348
349   int32_t output_buffer[4] = {};
350
351   EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
352   EXPECT_NO_THROW(_execution->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16));
353   EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
354   EXPECT_NO_THROW(_execution->execute());
355   EXPECT_EQ(output_buffer[0], 5);
356   EXPECT_EQ(output_buffer[1], -2);
357   EXPECT_EQ(output_buffer[2], 0);
358   EXPECT_EQ(output_buffer[3], -1);
359 }
360
361 } // namespace