2 * Copyright (c) 2020 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 "loader/KernelBuilder.h"
19 #include "kernels/Add.h"
20 #include "kernels/ArgMax.h"
21 #include "kernels/AveragePool2D.h"
22 #include "kernels/Concatenation.h"
23 #include "kernels/Conv2D.h"
24 #include "kernels/DepthwiseConv2D.h"
25 #include "kernels/Elu.h"
26 #include "kernels/FullyConnected.h"
27 #include "kernels/If.h"
28 #include "kernels/L2Normalize.h"
29 #include "kernels/L2Pool2D.h"
30 #include "kernels/LeakyRelu.h"
31 #include "kernels/LocalResponseNormalization.h"
32 #include "kernels/Logistic.h"
33 #include "kernels/MaxPool2D.h"
34 #include "kernels/Mean.h"
35 #include "kernels/Mul.h"
36 #include "kernels/Pad.h"
37 #include "kernels/Reshape.h"
38 #include "kernels/Softmax.h"
39 #include "kernels/SpaceToDepth.h"
40 #include "kernels/Split.h"
41 #include "kernels/StridedSlice.h"
42 #include "kernels/Squeeze.h"
43 #include "kernels/Unpack.h"
44 #include "kernels/Transpose.h"
45 #include "kernels/TransposeConv.h"
46 #include "loader/GraphLoader.h"
47 #include "loader/ModuleLoader.h"
51 namespace luci_interpreter
54 template <typename CircleNodeOut>
55 static std::vector<const loco::Node *> collectOutputNodes(const luci::CircleNode *node)
57 std::vector<const CircleNodeOut *> output_nodes;
58 for (const loco::Node *loco_node : loco::succs(node))
60 output_nodes.push_back(loco::must_cast<const CircleNodeOut *>(loco_node));
62 std::sort(output_nodes.begin(), output_nodes.end(),
63 [](const CircleNodeOut *node1, const CircleNodeOut *node2) {
64 return node1->index() < node2->index();
66 return {output_nodes.cbegin(), output_nodes.cend()};
69 const Tensor *KernelBuilder::getInputTensor(const loco::Node *node) const
71 const Tensor *tensor = _graph_loader.getTensorForNode(node);
72 assert(tensor != nullptr);
76 const Tensor *KernelBuilder::getOptionalInputTensor(const loco::Node *node) const
78 // TODO Revise this when optional inputs are implemented in the IR.
79 return getInputTensor(node);
82 Tensor *KernelBuilder::getOutputTensor(const loco::Node *node) const
84 Tensor *tensor = _graph_loader.getTensorForNode(node);
85 assert(tensor != nullptr);
90 KernelBuilder::getOutputTensors(const std::vector<const loco::Node *> &nodes) const
92 std::vector<Tensor *> tensors;
93 tensors.reserve(nodes.size());
94 for (const loco::Node *node : nodes)
95 tensors.push_back(getOutputTensor(node));
99 RuntimeGraph *KernelBuilder::getRuntimeGraph(const loco::Graph *graph) const
101 RuntimeGraph *runtime_graph = _module_loader.getRuntimeGraph(graph);
102 assert(runtime_graph != nullptr);
103 return runtime_graph;
106 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAdd *node)
108 assert(node->arity() == 2);
110 const Tensor *input1 = getInputTensor(node->x());
111 const Tensor *input2 = getInputTensor(node->y());
112 Tensor *output = getOutputTensor(node);
115 params.activation = node->fusedActivationFunction();
117 return std::make_unique<kernels::Add>(input1, input2, output, params);
120 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleArgMax *node)
122 assert(node->arity() == 2);
123 const Tensor *input1 = getInputTensor(node->input());
124 const Tensor *input2 = getInputTensor(node->dimension());
125 Tensor *output = getOutputTensor(node);
127 ArgMaxParams params{};
128 params.output_type = node->output_type();
130 return std::make_unique<kernels::ArgMax>(input1, input2, output, params);
133 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAveragePool2D *node)
135 assert(node->arity() == 1);
137 const Tensor *input = getInputTensor(node->value());
138 Tensor *output = getOutputTensor(node);
140 Pool2DParams params{};
141 params.padding = node->padding();
142 params.filter_height = node->filter()->h();
143 params.filter_width = node->filter()->w();
144 params.stride_height = node->stride()->h();
145 params.stride_width = node->stride()->w();
146 params.activation = node->fusedActivationFunction();
148 return std::make_unique<kernels::AveragePool2D>(input, output, params);
151 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConcatenation *node)
153 std::vector<const Tensor *> inputs(node->numValues());
154 for (uint32_t i = 0; i < node->numValues(); ++i)
156 inputs[i] = getInputTensor(node->values(i));
158 Tensor *output = getOutputTensor(node);
160 ConcatenationParams params{};
161 params.axis = node->axis();
163 return std::make_unique<kernels::Concatenation>(std::move(inputs), output, params);
166 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConst *)
168 throw std::runtime_error("Const node cannot be executed.");
171 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConv2D *node)
173 assert(node->arity() == 3);
175 const Tensor *input = getInputTensor(node->input());
176 const Tensor *filter = getInputTensor(node->filter());
177 const Tensor *bias = getInputTensor(node->bias());
178 Tensor *output = getOutputTensor(node);
180 Conv2DParams params{};
181 params.padding = node->padding();
182 params.stride_height = node->stride()->h();
183 params.stride_width = node->stride()->w();
184 params.dilation_height_factor = node->dilation()->h();
185 params.dilation_width_factor = node->dilation()->w();
186 params.activation = node->fusedActivationFunction();
188 return std::make_unique<kernels::Conv2D>(input, filter, bias, output, params);
191 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthwiseConv2D *node)
193 assert(node->arity() == 3);
195 const Tensor *input = getInputTensor(node->input());
196 const Tensor *filter = getInputTensor(node->filter());
197 const Tensor *bias = getInputTensor(node->bias());
198 Tensor *output = getOutputTensor(node);
200 DepthwiseConv2DParams params{};
201 params.padding = node->padding();
202 params.depth_multiplier = node->depthMultiplier();
203 params.stride_height = node->stride()->h();
204 params.stride_width = node->stride()->w();
205 params.dilation_height_factor = node->dilation()->h();
206 params.dilation_width_factor = node->dilation()->w();
207 params.activation = node->fusedActivationFunction();
209 return std::make_unique<kernels::DepthwiseConv2D>(input, filter, bias, output, params);
212 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleElu *node)
214 assert(node->arity() == 1);
216 const Tensor *input = getInputTensor(node->features());
217 Tensor *output = getOutputTensor(node);
219 return std::make_unique<kernels::Elu>(input, output);
222 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFullyConnected *node)
224 assert(node->arity() == 3);
226 const Tensor *input = getInputTensor(node->input());
227 const Tensor *filter = getInputTensor(node->weights());
228 const Tensor *bias = getOptionalInputTensor(node->bias());
229 Tensor *output = getOutputTensor(node);
231 FullyConnectedParams params{};
232 params.activation = node->fusedActivationFunction();
234 return std::make_unique<kernels::FullyConnected>(input, filter, bias, output, params);
237 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleIf *node)
239 auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
240 assert(node->arity() == 1 + node->input_count());
241 assert(output_nodes.size() == static_cast<size_t>(node->output_count()));
243 const Tensor *cond = getInputTensor(node->cond());
244 std::vector<const Tensor *> inputs(node->input_count());
245 for (uint32_t i = 0; i < node->input_count(); ++i)
247 inputs[i] = getInputTensor(node->input(i));
249 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
251 RuntimeGraph *then_graph = getRuntimeGraph(node->then_graph());
252 RuntimeGraph *else_graph = getRuntimeGraph(node->else_graph());
254 return std::make_unique<kernels::If>(cond, std::move(inputs), std::move(outputs), then_graph,
258 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Normalize *node)
260 assert(node->arity() == 1);
262 const Tensor *input = getInputTensor(node->x());
263 Tensor *output = getOutputTensor(node);
265 L2NormParams params{};
266 params.activation = node->fusedActivationFunction();
268 return std::make_unique<kernels::L2Normalize>(input, output, params);
271 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Pool2D *node)
273 assert(node->arity() == 1);
275 const Tensor *input = getInputTensor(node->value());
276 Tensor *output = getOutputTensor(node);
278 Pool2DParams params{};
279 params.padding = node->padding();
280 params.filter_height = node->filter()->h();
281 params.filter_width = node->filter()->w();
282 params.stride_height = node->stride()->h();
283 params.stride_width = node->stride()->w();
284 params.activation = node->fusedActivationFunction();
286 return std::make_unique<kernels::L2Pool2D>(input, output, params);
289 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLeakyRelu *node)
291 assert(node->arity() == 1);
292 const Tensor *input = getInputTensor(node->features());
293 Tensor *output = getOutputTensor(node);
295 LeakyReluParams params{};
296 params.alpha = node->alpha();
298 return std::make_unique<kernels::LeakyRelu>(input, output, params);
301 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLocalResponseNormalization *node)
303 assert(node->arity() == 1);
304 const Tensor *input = getInputTensor(node->input());
305 Tensor *output = getOutputTensor(node);
307 LocalResponseNormalizationParams params{};
308 params.radius = node->radius();
309 params.bias = node->bias();
310 params.alpha = node->alpha();
311 params.beta = node->beta();
313 return std::make_unique<kernels::LocalResponseNormalization>(input, output, params);
316 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogistic *node)
318 assert(node->arity() == 1);
320 const Tensor *input = getInputTensor(node->x());
321 Tensor *output = getOutputTensor(node);
323 return std::make_unique<kernels::Logistic>(input, output);
326 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleInput *)
328 throw std::runtime_error("Input node cannot be executed.");
331 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaxPool2D *node)
333 assert(node->arity() == 1);
335 const Tensor *input = getInputTensor(node->value());
336 Tensor *output = getOutputTensor(node);
338 Pool2DParams params{};
339 params.padding = node->padding();
340 params.filter_height = node->filter()->h();
341 params.filter_width = node->filter()->w();
342 params.stride_height = node->stride()->h();
343 params.stride_width = node->stride()->w();
344 params.activation = node->fusedActivationFunction();
346 return std::make_unique<kernels::MaxPool2D>(input, output, params);
349 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMean *node)
351 assert(node->arity() == 2);
353 const Tensor *input = getInputTensor(node->input());
354 const Tensor *axes = getInputTensor(node->reduction_indices());
355 Tensor *output = getOutputTensor(node);
357 ReducerParams params{};
358 params.keep_dims = node->keep_dims();
360 return std::make_unique<kernels::Mean>(input, axes, output, params);
363 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node)
365 assert(node->arity() == 2);
367 const Tensor *input1 = getInputTensor(node->x());
368 const Tensor *input2 = getInputTensor(node->y());
369 Tensor *output = getOutputTensor(node);
372 params.activation = node->fusedActivationFunction();
374 return std::make_unique<kernels::Mul>(input1, input2, output, params);
377 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleOutput *)
379 throw std::runtime_error("Output node cannot be executed.");
382 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePad *node)
384 assert(node->arity() == 2);
386 const Tensor *input = getInputTensor(node->input());
387 const Tensor *paddings = getInputTensor(node->paddings());
388 Tensor *output = getOutputTensor(node);
390 return std::make_unique<kernels::Pad>(input, paddings, output);
393 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReshape *node)
395 assert(node->arity() == 2);
397 const Tensor *input = getInputTensor(node->tensor());
398 const Tensor *shape = getInputTensor(node->shape());
399 Tensor *output = getOutputTensor(node);
401 // NOTE 'newShape' attribute is ignored.
402 return std::make_unique<kernels::Reshape>(input, shape, output);
405 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSoftmax *node)
407 assert(node->arity() == 1);
409 const Tensor *input = getInputTensor(node->logits());
410 Tensor *output = getOutputTensor(node);
412 SoftmaxParams params{};
413 params.beta = node->beta();
415 return std::make_unique<kernels::Softmax>(input, output, params);
418 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSpaceToDepth *node)
420 assert(node->arity() == 1);
421 const Tensor *input = getInputTensor(node->input());
423 Tensor *output = getOutputTensor(node);
425 SpaceToDepthParams params{};
426 params.block_size = node->block_size();
428 return std::make_unique<kernels::SpaceToDepth>(input, output, params);
431 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSplit *node)
433 auto output_nodes = collectOutputNodes<luci::CircleSplitOut>(node);
434 assert(node->arity() == 2);
435 assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
437 const Tensor *axis = getInputTensor(node->split_dim());
438 const Tensor *input = getInputTensor(node->input());
439 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
441 // NOTE 'num_splits' attribute is ignored.
442 return std::make_unique<kernels::Split>(axis, input, std::move(outputs));
445 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleStridedSlice *node)
447 assert(node->arity() == 4);
449 const Tensor *input = getInputTensor(node->input());
450 const Tensor *begin = getInputTensor(node->begin());
451 const Tensor *end = getInputTensor(node->end());
452 const Tensor *strides = getInputTensor(node->strides());
454 Tensor *output = getOutputTensor(node);
456 StridedSliceParams params{};
457 params.begin_mask = node->begin_mask();
458 params.ellipsis_mask = node->ellipsis_mask();
459 params.end_mask = node->end_mask();
460 params.new_axis_mask = node->new_axis_mask();
461 params.shrink_axis_mask = node->shrink_axis_mask();
463 return std::make_unique<kernels::StridedSlice>(input, begin, end, strides, output, params);
466 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqueeze *node)
468 assert(node->arity() == 1);
470 const Tensor *input = getInputTensor(node->input());
471 Tensor *output = getOutputTensor(node);
473 SqueezeParams params{};
474 assert(node->squeeze_dims().size() <= 4);
475 for (size_t i = 0; i < node->squeeze_dims().size(); i++)
477 params.squeeze_dims.push_back(node->squeeze_dims().at(i));
480 return std::make_unique<kernels::Squeeze>(input, output, params);
483 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTransposeConv *node)
485 assert(node->arity() == 3);
487 const Tensor *input_sizes = getInputTensor(node->inputSizes());
488 const Tensor *filter = getInputTensor(node->filter());
489 const Tensor *out_backprop = getInputTensor(node->outBackprop());
491 Tensor *output = getOutputTensor(node);
493 TransposeConvParams params{};
494 params.padding = node->padding();
495 params.stride_height = node->stride()->h();
496 params.stride_width = node->stride()->w();
498 return std::make_unique<kernels::TransposeConv>(input_sizes, filter, out_backprop, output,
502 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleUnpack *node)
504 auto output_nodes = collectOutputNodes<luci::CircleUnpackOut>(node);
505 assert(node->arity() == 1);
506 assert(output_nodes.size() == static_cast<size_t>(node->num()));
508 const Tensor *input = getInputTensor(node->value());
509 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
511 UnpackParams params{};
512 params.axis = node->axis();
514 // NOTE 'num' attribute is ignored.
515 return std::make_unique<kernels::Unpack>(input, std::move(outputs), params);
518 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTranspose *node)
520 assert(node->arity() == 2);
522 const Tensor *input = getInputTensor(node->a());
523 const Tensor *perm = getInputTensor(node->perm());
524 Tensor *output = getOutputTensor(node);
526 return std::make_unique<kernels::Transpose>(input, perm, output);
529 } // namespace luci_interpreter