2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <gtest/gtest.h>
22 #include "interp/InterpExecutor.h"
23 #include "exec/Execution.h"
24 #include "ir/operation/Add.h"
29 using namespace onert::ir;
30 using InterpExecutor = onert::interp::InterpExecutor;
31 using Execution = onert::exec::Execution;
32 using ExecutorMap = onert::exec::ExecutorMap;
34 class InterpExecutorTest : public ::testing::Test
37 virtual void SetUp() {}
38 void CreateSimpleModel()
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>();
49 Shape shape{1, 2, 2, 1};
50 TypeInfo type{DataType::INT32};
51 Shape shape_scalar(0);
52 TypeInfo type_scalar{DataType::INT32};
54 auto operand_lhs = _graph->addOperand(shape, type);
55 auto operand_rhs = _graph->addOperand(shape, type);
56 auto operand_result = _graph->addOperand(shape, type);
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));
66 // Identify model inputs and outputs
68 _graph->getInputs().append(operand_lhs);
69 _graph->getInputs().append(operand_rhs);
70 _graph->getOutputs().append(operand_result);
72 _graph->finishBuilding();
74 auto subgs = std::make_shared<onert::ir::Subgraphs>();
75 subgs->push(onert::ir::SubgraphIndex{0}, _graph);
76 _graph->setSubgraphs(subgs);
78 _executors = std::make_shared<ExecutorMap>();
80 std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
83 void CreateTwoStepModel()
85 // Model: two elementwise add operation
86 // model input: lhs, rhs1
87 // model output: second add result (result2)
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>();
95 // 1st add operands (result1 <= lhs + rhs1)
97 Shape shape{1, 2, 2, 1};
98 TypeInfo type{DataType::INT32};
99 Shape shape_scalar(0);
100 TypeInfo type_scalar{DataType::INT32};
102 static int32_t rhs2_data[4] = {3, 1, -1, 5};
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);
111 .data(std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(&rhs2_data), 16));
113 // 2nd add operations (result2 <= result1 + rhs2)
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));
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));
127 // Identify model inputs and outputs
129 _graph->getInputs().append(operand_lhs);
130 _graph->getInputs().append(operand_rhs1);
131 _graph->getOutputs().append(operand_result2);
133 _graph->finishBuilding();
135 auto subgs = std::make_shared<onert::ir::Subgraphs>();
136 subgs->push(onert::ir::SubgraphIndex{0}, _graph);
137 _graph->setSubgraphs(subgs);
139 _executors = std::make_shared<ExecutorMap>();
141 std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
144 void CreateUnspecifiedDimensionsModel()
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>();
155 Shape shape{1, 0, 2, 1};
156 TypeInfo type{DataType::INT32};
157 Shape shape_scalar(0);
158 TypeInfo type_scalar{DataType::INT32};
160 auto operand_lhs = _graph->addOperand(shape, type);
161 auto operand_rhs = _graph->addOperand(shape, type);
163 auto operand_activation = _graph->addOperand(shape_scalar, type_scalar);
165 .at(operand_activation)
167 std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(&_activation_value), 4));
169 auto operand_result = _graph->addOperand(shape, type);
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));
179 // Identify model inputs and outputs
181 _graph->getInputs().append(operand_lhs);
182 _graph->getInputs().append(operand_rhs);
183 _graph->getOutputs().append(operand_result);
185 _graph->finishBuilding();
187 auto subgs = std::make_shared<onert::ir::Subgraphs>();
188 subgs->push(onert::ir::SubgraphIndex{0}, _graph);
189 _graph->setSubgraphs(subgs);
191 _executors = std::make_shared<ExecutorMap>();
193 std::make_pair(onert::ir::SubgraphIndex{0}, std::make_unique<InterpExecutor>(*_graph)));
196 void createExecution() { _execution = std::make_unique<Execution>(_executors); }
198 virtual void TearDown() { _executors = nullptr; }
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};
206 TEST_F(InterpExecutorTest, create_empty)
209 graph.finishBuilding();
210 auto executor = std::make_unique<InterpExecutor>(graph);
211 ASSERT_NE(executor, nullptr);
214 TEST_F(InterpExecutorTest, create_simple)
217 ASSERT_NE(_executors, nullptr);
218 ASSERT_NE(_executors->at(onert::ir::SubgraphIndex{0}), nullptr);
221 TEST_F(InterpExecutorTest, setInput)
226 auto input1 = IOIndex{0};
227 const int32_t input1_buffer[4] = {1, 0, -1, -2};
229 EXPECT_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 4),
231 EXPECT_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 12),
233 EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
236 TEST_F(InterpExecutorTest, setOutput)
241 auto output = IOIndex{0};
242 auto output_idx = _graph->getOutputs().at(output);
244 int32_t output_buffer[4] = {};
246 EXPECT_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 4),
248 EXPECT_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 12),
250 EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
253 TEST_F(InterpExecutorTest, setInputForUnspecifiedDimensions)
255 CreateUnspecifiedDimensionsModel();
258 auto input1 = IOIndex{0};
259 const int32_t input1_buffer[4] = {1, 0, -1, -2};
261 TypeInfo operand_type{DataType::INT32};
262 Shape operand_shape{1, 2, 2, 1};
264 EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape,
265 reinterpret_cast<const void *>(input1_buffer), 4),
267 EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape,
268 reinterpret_cast<const void *>(input1_buffer), 12),
270 EXPECT_NO_THROW(_execution->setInput(input1, operand_type, operand_shape,
271 reinterpret_cast<const void *>(input1_buffer), 16));
274 TEST_F(InterpExecutorTest, setOutputForUnspecifiedDimensions)
276 CreateUnspecifiedDimensionsModel();
279 auto output = IOIndex{0};
280 auto output_idx = _graph->getOutputs().at(output);
282 TypeInfo operand_type{DataType::INT32};
283 Shape operand_shape{1, 2, 2, 1};
285 int32_t output_buffer[4] = {};
287 EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape,
288 reinterpret_cast<void *>(output_buffer), 4),
290 EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape,
291 reinterpret_cast<void *>(output_buffer), 12),
293 EXPECT_NO_THROW(_execution->setOutput(output, operand_type, operand_shape,
294 reinterpret_cast<void *>(output_buffer), 16));
297 TEST_F(InterpExecutorTest, execute)
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);
307 const int32_t input1_buffer[4] = {1, 0, -1, -2};
308 const int32_t input2_buffer[4] = {1, -3, 2, -4};
310 auto output = IOIndex{0};
311 auto output_idx = _graph->getOutputs().at(output);
313 int32_t output_buffer[4] = {};
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);
325 TEST_F(InterpExecutorTest, executeTwoStep)
327 CreateTwoStepModel();
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);
335 const int32_t input1_buffer[4] = {1, 0, -1, -2};
336 const int32_t input2_buffer[4] = {1, -3, 2, -4};
338 auto output = IOIndex{0};
339 auto output_idx = _graph->getOutputs().at(output);
341 int32_t output_buffer[4] = {};
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);