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/DepthToSpace.h"
25 #include "kernels/DepthwiseConv2D.h"
26 #include "kernels/Elu.h"
27 #include "kernels/FullyConnected.h"
28 #include "kernels/If.h"
29 #include "kernels/L2Normalize.h"
30 #include "kernels/L2Pool2D.h"
31 #include "kernels/LeakyRelu.h"
32 #include "kernels/LocalResponseNormalization.h"
33 #include "kernels/Logistic.h"
34 #include "kernels/MaxPool2D.h"
35 #include "kernels/Mean.h"
36 #include "kernels/Mul.h"
37 #include "kernels/Pad.h"
38 #include "kernels/Reshape.h"
39 #include "kernels/Reverse.h"
40 #include "kernels/Rsqrt.h"
41 #include "kernels/Slice.h"
42 #include "kernels/Softmax.h"
43 #include "kernels/SpaceToDepth.h"
44 #include "kernels/Split.h"
45 #include "kernels/StridedSlice.h"
46 #include "kernels/Sqrt.h"
47 #include "kernels/Squeeze.h"
48 #include "kernels/Tanh.h"
49 #include "kernels/Unpack.h"
50 #include "kernels/Transpose.h"
51 #include "kernels/TransposeConv.h"
55 namespace luci_interpreter
58 template <typename CircleNodeOut>
59 static std::vector<const loco::Node *> collectOutputNodes(const luci::CircleNode *node)
61 std::vector<const CircleNodeOut *> output_nodes;
62 for (const loco::Node *loco_node : loco::succs(node))
64 output_nodes.push_back(loco::must_cast<const CircleNodeOut *>(loco_node));
66 std::sort(output_nodes.begin(), output_nodes.end(),
67 [](const CircleNodeOut *node1, const CircleNodeOut *node2) {
68 return node1->index() < node2->index();
70 return {output_nodes.cbegin(), output_nodes.cend()};
73 const Tensor *KernelBuilder::getInputTensor(const loco::Node *node) const
75 const Tensor *tensor = _node_to_tensor.at(node);
76 assert(tensor != nullptr);
80 const Tensor *KernelBuilder::getOptionalInputTensor(const loco::Node *node) const
82 if (dynamic_cast<const luci::CircleOutputExclude *>(node))
86 return getInputTensor(node);
89 Tensor *KernelBuilder::getOutputTensor(const loco::Node *node) const
91 Tensor *tensor = _node_to_tensor.at(node);
92 assert(tensor != nullptr);
97 KernelBuilder::getOutputTensors(const std::vector<const loco::Node *> &nodes) const
99 std::vector<Tensor *> tensors;
100 tensors.reserve(nodes.size());
101 for (const loco::Node *node : nodes)
102 tensors.push_back(getOutputTensor(node));
106 RuntimeGraph *KernelBuilder::getRuntimeGraph(const loco::Graph *graph) const
108 RuntimeGraph *runtime_graph = _graph_to_runtime_graph.at(graph);
109 assert(runtime_graph != nullptr);
110 return runtime_graph;
113 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAdd *node)
115 assert(node->arity() == 2);
117 const Tensor *input1 = getInputTensor(node->x());
118 const Tensor *input2 = getInputTensor(node->y());
119 Tensor *output = getOutputTensor(node);
122 params.activation = node->fusedActivationFunction();
124 return std::make_unique<kernels::Add>(input1, input2, output, params);
127 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleArgMax *node)
129 assert(node->arity() == 2);
130 const Tensor *input = getInputTensor(node->input());
131 const Tensor *axis = getInputTensor(node->dimension());
132 Tensor *output = getOutputTensor(node);
134 ArgMaxParams params{};
135 params.output_type = node->output_type();
137 return std::make_unique<kernels::ArgMax>(input, axis, output, params);
140 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAveragePool2D *node)
142 assert(node->arity() == 1);
144 const Tensor *input = getInputTensor(node->value());
145 Tensor *output = getOutputTensor(node);
147 Pool2DParams params{};
148 params.padding = node->padding();
149 params.filter_height = node->filter()->h();
150 params.filter_width = node->filter()->w();
151 params.stride_height = node->stride()->h();
152 params.stride_width = node->stride()->w();
153 params.activation = node->fusedActivationFunction();
155 return std::make_unique<kernels::AveragePool2D>(input, output, params);
158 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConcatenation *node)
160 std::vector<const Tensor *> inputs(node->numValues());
161 for (uint32_t i = 0; i < node->numValues(); ++i)
163 inputs[i] = getInputTensor(node->values(i));
165 Tensor *output = getOutputTensor(node);
167 ConcatenationParams params{};
168 params.axis = node->axis();
170 return std::make_unique<kernels::Concatenation>(std::move(inputs), output, params);
173 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConst *)
175 throw std::runtime_error("Const node cannot be executed.");
178 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConv2D *node)
180 assert(node->arity() == 3);
182 const Tensor *input = getInputTensor(node->input());
183 const Tensor *filter = getInputTensor(node->filter());
184 const Tensor *bias = getInputTensor(node->bias());
185 Tensor *output = getOutputTensor(node);
187 Conv2DParams params{};
188 params.padding = node->padding();
189 params.stride_height = node->stride()->h();
190 params.stride_width = node->stride()->w();
191 params.dilation_height_factor = node->dilation()->h();
192 params.dilation_width_factor = node->dilation()->w();
193 params.activation = node->fusedActivationFunction();
195 return std::make_unique<kernels::Conv2D>(input, filter, bias, output, params);
198 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthToSpace *node)
200 assert(node->arity() == 1);
202 const Tensor *input = getInputTensor(node->input());
203 Tensor *output = getOutputTensor(node);
205 DepthToSpaceParams params{};
206 params.block_size = node->block_size();
208 return std::make_unique<kernels::DepthToSpace>(input, output, params);
211 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthwiseConv2D *node)
213 assert(node->arity() == 3);
215 const Tensor *input = getInputTensor(node->input());
216 const Tensor *filter = getInputTensor(node->filter());
217 const Tensor *bias = getInputTensor(node->bias());
218 Tensor *output = getOutputTensor(node);
220 DepthwiseConv2DParams params{};
221 params.padding = node->padding();
222 params.depth_multiplier = node->depthMultiplier();
223 params.stride_height = node->stride()->h();
224 params.stride_width = node->stride()->w();
225 params.dilation_height_factor = node->dilation()->h();
226 params.dilation_width_factor = node->dilation()->w();
227 params.activation = node->fusedActivationFunction();
229 return std::make_unique<kernels::DepthwiseConv2D>(input, filter, bias, output, params);
232 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleElu *node)
234 assert(node->arity() == 1);
236 const Tensor *input = getInputTensor(node->features());
237 Tensor *output = getOutputTensor(node);
239 return std::make_unique<kernels::Elu>(input, output);
242 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFullyConnected *node)
244 assert(node->arity() == 3);
246 const Tensor *input = getInputTensor(node->input());
247 const Tensor *weights = getInputTensor(node->weights());
248 const Tensor *bias = getOptionalInputTensor(node->bias());
249 Tensor *output = getOutputTensor(node);
251 FullyConnectedParams params{};
252 params.activation = node->fusedActivationFunction();
254 return std::make_unique<kernels::FullyConnected>(input, weights, bias, output, params);
257 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleIf *node)
259 auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
260 assert(node->arity() == 1 + node->input_count());
261 assert(output_nodes.size() == static_cast<size_t>(node->output_count()));
263 const Tensor *cond = getInputTensor(node->cond());
264 std::vector<const Tensor *> inputs(node->input_count());
265 for (uint32_t i = 0; i < node->input_count(); ++i)
267 inputs[i] = getInputTensor(node->input(i));
269 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
271 RuntimeGraph *then_graph = getRuntimeGraph(node->then_graph());
272 RuntimeGraph *else_graph = getRuntimeGraph(node->else_graph());
274 return std::make_unique<kernels::If>(cond, std::move(inputs), std::move(outputs), then_graph,
278 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleInput *)
280 throw std::runtime_error("Input node cannot be executed.");
283 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Normalize *node)
285 assert(node->arity() == 1);
287 const Tensor *input = getInputTensor(node->x());
288 Tensor *output = getOutputTensor(node);
290 L2NormParams params{};
291 params.activation = node->fusedActivationFunction();
293 return std::make_unique<kernels::L2Normalize>(input, output, params);
296 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Pool2D *node)
298 assert(node->arity() == 1);
300 const Tensor *input = getInputTensor(node->value());
301 Tensor *output = getOutputTensor(node);
303 Pool2DParams params{};
304 params.padding = node->padding();
305 params.filter_height = node->filter()->h();
306 params.filter_width = node->filter()->w();
307 params.stride_height = node->stride()->h();
308 params.stride_width = node->stride()->w();
309 params.activation = node->fusedActivationFunction();
311 return std::make_unique<kernels::L2Pool2D>(input, output, params);
314 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLeakyRelu *node)
316 assert(node->arity() == 1);
317 const Tensor *input = getInputTensor(node->features());
318 Tensor *output = getOutputTensor(node);
320 LeakyReluParams params{};
321 params.alpha = node->alpha();
323 return std::make_unique<kernels::LeakyRelu>(input, output, params);
326 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLocalResponseNormalization *node)
328 assert(node->arity() == 1);
329 const Tensor *input = getInputTensor(node->input());
330 Tensor *output = getOutputTensor(node);
332 LocalResponseNormalizationParams params{};
333 params.radius = node->radius();
334 params.bias = node->bias();
335 params.alpha = node->alpha();
336 params.beta = node->beta();
338 return std::make_unique<kernels::LocalResponseNormalization>(input, output, params);
341 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogistic *node)
343 assert(node->arity() == 1);
345 const Tensor *input = getInputTensor(node->x());
346 Tensor *output = getOutputTensor(node);
348 return std::make_unique<kernels::Logistic>(input, output);
351 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaxPool2D *node)
353 assert(node->arity() == 1);
355 const Tensor *input = getInputTensor(node->value());
356 Tensor *output = getOutputTensor(node);
358 Pool2DParams params{};
359 params.padding = node->padding();
360 params.filter_height = node->filter()->h();
361 params.filter_width = node->filter()->w();
362 params.stride_height = node->stride()->h();
363 params.stride_width = node->stride()->w();
364 params.activation = node->fusedActivationFunction();
366 return std::make_unique<kernels::MaxPool2D>(input, output, params);
369 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMean *node)
371 assert(node->arity() == 2);
373 const Tensor *input = getInputTensor(node->input());
374 const Tensor *axes = getInputTensor(node->reduction_indices());
375 Tensor *output = getOutputTensor(node);
377 ReducerParams params{};
378 params.keep_dims = node->keep_dims();
380 return std::make_unique<kernels::Mean>(input, axes, output, params);
383 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node)
385 assert(node->arity() == 2);
387 const Tensor *input1 = getInputTensor(node->x());
388 const Tensor *input2 = getInputTensor(node->y());
389 Tensor *output = getOutputTensor(node);
392 params.activation = node->fusedActivationFunction();
394 return std::make_unique<kernels::Mul>(input1, input2, output, params);
397 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleOutput *)
399 throw std::runtime_error("Output node cannot be executed.");
402 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePad *node)
404 assert(node->arity() == 2);
406 const Tensor *input = getInputTensor(node->input());
407 const Tensor *paddings = getInputTensor(node->paddings());
408 Tensor *output = getOutputTensor(node);
410 return std::make_unique<kernels::Pad>(input, paddings, output);
413 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReshape *node)
415 assert(node->arity() == 2);
417 const Tensor *input = getInputTensor(node->tensor());
418 const Tensor *shape = getInputTensor(node->shape());
419 Tensor *output = getOutputTensor(node);
421 // NOTE 'newShape' attribute is ignored.
422 return std::make_unique<kernels::Reshape>(input, shape, output);
425 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReverseV2 *node)
427 assert(node->arity() == 2);
429 const Tensor *input = getInputTensor(node->tensor());
430 const Tensor *axes = getInputTensor(node->axis());
431 Tensor *output = getOutputTensor(node);
433 return std::make_unique<kernels::Reverse>(input, axes, output);
436 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRsqrt *node)
438 assert(node->arity() == 1);
440 const Tensor *input = getInputTensor(node->x());
441 Tensor *output = getOutputTensor(node);
443 return std::make_unique<kernels::Rsqrt>(input, output);
446 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSlice *node)
448 assert(node->arity() == 3);
450 const Tensor *input = getInputTensor(node->input());
451 const Tensor *begin = getInputTensor(node->begin());
452 const Tensor *size = getInputTensor(node->size());
454 Tensor *output = getOutputTensor(node);
456 return std::make_unique<kernels::Slice>(input, begin, size, output);
459 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSoftmax *node)
461 assert(node->arity() == 1);
463 const Tensor *input = getInputTensor(node->logits());
464 Tensor *output = getOutputTensor(node);
466 SoftmaxParams params{};
467 params.beta = node->beta();
469 return std::make_unique<kernels::Softmax>(input, output, params);
472 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSpaceToDepth *node)
474 assert(node->arity() == 1);
475 const Tensor *input = getInputTensor(node->input());
477 Tensor *output = getOutputTensor(node);
479 SpaceToDepthParams params{};
480 params.block_size = node->block_size();
482 return std::make_unique<kernels::SpaceToDepth>(input, output, params);
485 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSplit *node)
487 auto output_nodes = collectOutputNodes<luci::CircleSplitOut>(node);
488 assert(node->arity() == 2);
489 assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
491 const Tensor *axis = getInputTensor(node->split_dim());
492 const Tensor *input = getInputTensor(node->input());
493 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
495 // NOTE 'num_splits' attribute is ignored.
496 return std::make_unique<kernels::Split>(axis, input, std::move(outputs));
499 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqrt *node)
501 assert(node->arity() == 1);
503 const Tensor *input = getInputTensor(node->x());
504 Tensor *output = getOutputTensor(node);
506 return std::make_unique<kernels::Sqrt>(input, output);
509 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqueeze *node)
511 assert(node->arity() == 1);
513 const Tensor *input = getInputTensor(node->input());
514 Tensor *output = getOutputTensor(node);
516 SqueezeParams params{};
517 params.squeeze_dims = node->squeeze_dims();
519 return std::make_unique<kernels::Squeeze>(input, output, params);
522 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleStridedSlice *node)
524 assert(node->arity() == 4);
526 const Tensor *input = getInputTensor(node->input());
527 const Tensor *begin = getInputTensor(node->begin());
528 const Tensor *end = getInputTensor(node->end());
529 const Tensor *strides = getInputTensor(node->strides());
531 Tensor *output = getOutputTensor(node);
533 StridedSliceParams params{};
534 params.begin_mask = node->begin_mask();
535 params.ellipsis_mask = node->ellipsis_mask();
536 params.end_mask = node->end_mask();
537 params.new_axis_mask = node->new_axis_mask();
538 params.shrink_axis_mask = node->shrink_axis_mask();
540 return std::make_unique<kernels::StridedSlice>(input, begin, end, strides, output, params);
543 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTanh *node)
545 assert(node->arity() == 1);
547 const Tensor *input = getInputTensor(node->x());
548 Tensor *output = getOutputTensor(node);
550 return std::make_unique<kernels::Tanh>(input, output);
553 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTranspose *node)
555 assert(node->arity() == 2);
557 const Tensor *input = getInputTensor(node->a());
558 const Tensor *perm = getInputTensor(node->perm());
559 Tensor *output = getOutputTensor(node);
561 return std::make_unique<kernels::Transpose>(input, perm, output);
564 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTransposeConv *node)
566 assert(node->arity() == 4);
568 const Tensor *input_sizes = getInputTensor(node->inputSizes());
569 const Tensor *filter = getInputTensor(node->filter());
570 const Tensor *out_backprop = getInputTensor(node->outBackprop());
571 const Tensor *bias = getOptionalInputTensor(node->bias());
573 Tensor *output = getOutputTensor(node);
575 TransposeConvParams params{};
576 params.padding = node->padding();
577 params.stride_height = node->stride()->h();
578 params.stride_width = node->stride()->w();
580 return std::make_unique<kernels::TransposeConv>(input_sizes, filter, out_backprop, bias, output,
584 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleUnpack *node)
586 auto output_nodes = collectOutputNodes<luci::CircleUnpackOut>(node);
587 assert(node->arity() == 1);
588 assert(output_nodes.size() == static_cast<size_t>(node->num()));
590 const Tensor *input = getInputTensor(node->value());
591 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
593 UnpackParams params{};
594 params.axis = node->axis();
596 // NOTE 'num' attribute is ignored.
597 return std::make_unique<kernels::Unpack>(input, std::move(outputs), params);
600 } // namespace luci_interpreter