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/Slice.h"
41 #include "kernels/Softmax.h"
42 #include "kernels/SpaceToDepth.h"
43 #include "kernels/Split.h"
44 #include "kernels/StridedSlice.h"
45 #include "kernels/Squeeze.h"
46 #include "kernels/Unpack.h"
47 #include "kernels/Transpose.h"
48 #include "kernels/TransposeConv.h"
52 namespace luci_interpreter
55 template <typename CircleNodeOut>
56 static std::vector<const loco::Node *> collectOutputNodes(const luci::CircleNode *node)
58 std::vector<const CircleNodeOut *> output_nodes;
59 for (const loco::Node *loco_node : loco::succs(node))
61 output_nodes.push_back(loco::must_cast<const CircleNodeOut *>(loco_node));
63 std::sort(output_nodes.begin(), output_nodes.end(),
64 [](const CircleNodeOut *node1, const CircleNodeOut *node2) {
65 return node1->index() < node2->index();
67 return {output_nodes.cbegin(), output_nodes.cend()};
70 const Tensor *KernelBuilder::getInputTensor(const loco::Node *node) const
72 const Tensor *tensor = _node_to_tensor.at(node);
73 assert(tensor != nullptr);
77 const Tensor *KernelBuilder::getOptionalInputTensor(const loco::Node *node) const
79 if (dynamic_cast<const luci::CircleOutputExclude *>(node))
83 return getInputTensor(node);
86 Tensor *KernelBuilder::getOutputTensor(const loco::Node *node) const
88 Tensor *tensor = _node_to_tensor.at(node);
89 assert(tensor != nullptr);
94 KernelBuilder::getOutputTensors(const std::vector<const loco::Node *> &nodes) const
96 std::vector<Tensor *> tensors;
97 tensors.reserve(nodes.size());
98 for (const loco::Node *node : nodes)
99 tensors.push_back(getOutputTensor(node));
103 RuntimeGraph *KernelBuilder::getRuntimeGraph(const loco::Graph *graph) const
105 RuntimeGraph *runtime_graph = _graph_to_runtime_graph.at(graph);
106 assert(runtime_graph != nullptr);
107 return runtime_graph;
110 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAdd *node)
112 assert(node->arity() == 2);
114 const Tensor *input1 = getInputTensor(node->x());
115 const Tensor *input2 = getInputTensor(node->y());
116 Tensor *output = getOutputTensor(node);
119 params.activation = node->fusedActivationFunction();
121 return std::make_unique<kernels::Add>(input1, input2, output, params);
124 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleArgMax *node)
126 assert(node->arity() == 2);
127 const Tensor *input = getInputTensor(node->input());
128 const Tensor *axis = getInputTensor(node->dimension());
129 Tensor *output = getOutputTensor(node);
131 ArgMaxParams params{};
132 params.output_type = node->output_type();
134 return std::make_unique<kernels::ArgMax>(input, axis, output, params);
137 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAveragePool2D *node)
139 assert(node->arity() == 1);
141 const Tensor *input = getInputTensor(node->value());
142 Tensor *output = getOutputTensor(node);
144 Pool2DParams params{};
145 params.padding = node->padding();
146 params.filter_height = node->filter()->h();
147 params.filter_width = node->filter()->w();
148 params.stride_height = node->stride()->h();
149 params.stride_width = node->stride()->w();
150 params.activation = node->fusedActivationFunction();
152 return std::make_unique<kernels::AveragePool2D>(input, output, params);
155 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConcatenation *node)
157 std::vector<const Tensor *> inputs(node->numValues());
158 for (uint32_t i = 0; i < node->numValues(); ++i)
160 inputs[i] = getInputTensor(node->values(i));
162 Tensor *output = getOutputTensor(node);
164 ConcatenationParams params{};
165 params.axis = node->axis();
167 return std::make_unique<kernels::Concatenation>(std::move(inputs), output, params);
170 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConst *)
172 throw std::runtime_error("Const node cannot be executed.");
175 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConv2D *node)
177 assert(node->arity() == 3);
179 const Tensor *input = getInputTensor(node->input());
180 const Tensor *filter = getInputTensor(node->filter());
181 const Tensor *bias = getInputTensor(node->bias());
182 Tensor *output = getOutputTensor(node);
184 Conv2DParams params{};
185 params.padding = node->padding();
186 params.stride_height = node->stride()->h();
187 params.stride_width = node->stride()->w();
188 params.dilation_height_factor = node->dilation()->h();
189 params.dilation_width_factor = node->dilation()->w();
190 params.activation = node->fusedActivationFunction();
192 return std::make_unique<kernels::Conv2D>(input, filter, bias, output, params);
195 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthToSpace *node)
197 assert(node->arity() == 1);
199 const Tensor *input = getInputTensor(node->input());
200 Tensor *output = getOutputTensor(node);
202 DepthToSpaceParams params{};
203 params.block_size = node->block_size();
205 return std::make_unique<kernels::DepthToSpace>(input, output, params);
208 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthwiseConv2D *node)
210 assert(node->arity() == 3);
212 const Tensor *input = getInputTensor(node->input());
213 const Tensor *filter = getInputTensor(node->filter());
214 const Tensor *bias = getInputTensor(node->bias());
215 Tensor *output = getOutputTensor(node);
217 DepthwiseConv2DParams params{};
218 params.padding = node->padding();
219 params.depth_multiplier = node->depthMultiplier();
220 params.stride_height = node->stride()->h();
221 params.stride_width = node->stride()->w();
222 params.dilation_height_factor = node->dilation()->h();
223 params.dilation_width_factor = node->dilation()->w();
224 params.activation = node->fusedActivationFunction();
226 return std::make_unique<kernels::DepthwiseConv2D>(input, filter, bias, output, params);
229 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleElu *node)
231 assert(node->arity() == 1);
233 const Tensor *input = getInputTensor(node->features());
234 Tensor *output = getOutputTensor(node);
236 return std::make_unique<kernels::Elu>(input, output);
239 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFullyConnected *node)
241 assert(node->arity() == 3);
243 const Tensor *input = getInputTensor(node->input());
244 const Tensor *weights = getInputTensor(node->weights());
245 const Tensor *bias = getOptionalInputTensor(node->bias());
246 Tensor *output = getOutputTensor(node);
248 FullyConnectedParams params{};
249 params.activation = node->fusedActivationFunction();
251 return std::make_unique<kernels::FullyConnected>(input, weights, bias, output, params);
254 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleIf *node)
256 auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
257 assert(node->arity() == 1 + node->input_count());
258 assert(output_nodes.size() == static_cast<size_t>(node->output_count()));
260 const Tensor *cond = getInputTensor(node->cond());
261 std::vector<const Tensor *> inputs(node->input_count());
262 for (uint32_t i = 0; i < node->input_count(); ++i)
264 inputs[i] = getInputTensor(node->input(i));
266 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
268 RuntimeGraph *then_graph = getRuntimeGraph(node->then_graph());
269 RuntimeGraph *else_graph = getRuntimeGraph(node->else_graph());
271 return std::make_unique<kernels::If>(cond, std::move(inputs), std::move(outputs), then_graph,
275 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleInput *)
277 throw std::runtime_error("Input node cannot be executed.");
280 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Normalize *node)
282 assert(node->arity() == 1);
284 const Tensor *input = getInputTensor(node->x());
285 Tensor *output = getOutputTensor(node);
287 L2NormParams params{};
288 params.activation = node->fusedActivationFunction();
290 return std::make_unique<kernels::L2Normalize>(input, output, params);
293 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Pool2D *node)
295 assert(node->arity() == 1);
297 const Tensor *input = getInputTensor(node->value());
298 Tensor *output = getOutputTensor(node);
300 Pool2DParams params{};
301 params.padding = node->padding();
302 params.filter_height = node->filter()->h();
303 params.filter_width = node->filter()->w();
304 params.stride_height = node->stride()->h();
305 params.stride_width = node->stride()->w();
306 params.activation = node->fusedActivationFunction();
308 return std::make_unique<kernels::L2Pool2D>(input, output, params);
311 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLeakyRelu *node)
313 assert(node->arity() == 1);
314 const Tensor *input = getInputTensor(node->features());
315 Tensor *output = getOutputTensor(node);
317 LeakyReluParams params{};
318 params.alpha = node->alpha();
320 return std::make_unique<kernels::LeakyRelu>(input, output, params);
323 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLocalResponseNormalization *node)
325 assert(node->arity() == 1);
326 const Tensor *input = getInputTensor(node->input());
327 Tensor *output = getOutputTensor(node);
329 LocalResponseNormalizationParams params{};
330 params.radius = node->radius();
331 params.bias = node->bias();
332 params.alpha = node->alpha();
333 params.beta = node->beta();
335 return std::make_unique<kernels::LocalResponseNormalization>(input, output, params);
338 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogistic *node)
340 assert(node->arity() == 1);
342 const Tensor *input = getInputTensor(node->x());
343 Tensor *output = getOutputTensor(node);
345 return std::make_unique<kernels::Logistic>(input, output);
348 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaxPool2D *node)
350 assert(node->arity() == 1);
352 const Tensor *input = getInputTensor(node->value());
353 Tensor *output = getOutputTensor(node);
355 Pool2DParams params{};
356 params.padding = node->padding();
357 params.filter_height = node->filter()->h();
358 params.filter_width = node->filter()->w();
359 params.stride_height = node->stride()->h();
360 params.stride_width = node->stride()->w();
361 params.activation = node->fusedActivationFunction();
363 return std::make_unique<kernels::MaxPool2D>(input, output, params);
366 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMean *node)
368 assert(node->arity() == 2);
370 const Tensor *input = getInputTensor(node->input());
371 const Tensor *axes = getInputTensor(node->reduction_indices());
372 Tensor *output = getOutputTensor(node);
374 ReducerParams params{};
375 params.keep_dims = node->keep_dims();
377 return std::make_unique<kernels::Mean>(input, axes, output, params);
380 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node)
382 assert(node->arity() == 2);
384 const Tensor *input1 = getInputTensor(node->x());
385 const Tensor *input2 = getInputTensor(node->y());
386 Tensor *output = getOutputTensor(node);
389 params.activation = node->fusedActivationFunction();
391 return std::make_unique<kernels::Mul>(input1, input2, output, params);
394 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleOutput *)
396 throw std::runtime_error("Output node cannot be executed.");
399 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePad *node)
401 assert(node->arity() == 2);
403 const Tensor *input = getInputTensor(node->input());
404 const Tensor *paddings = getInputTensor(node->paddings());
405 Tensor *output = getOutputTensor(node);
407 return std::make_unique<kernels::Pad>(input, paddings, output);
410 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReshape *node)
412 assert(node->arity() == 2);
414 const Tensor *input = getInputTensor(node->tensor());
415 const Tensor *shape = getInputTensor(node->shape());
416 Tensor *output = getOutputTensor(node);
418 // NOTE 'newShape' attribute is ignored.
419 return std::make_unique<kernels::Reshape>(input, shape, output);
422 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReverseV2 *node)
424 assert(node->arity() == 2);
426 const Tensor *input = getInputTensor(node->tensor());
427 const Tensor *axes = getInputTensor(node->axis());
428 Tensor *output = getOutputTensor(node);
430 return std::make_unique<kernels::Reverse>(input, axes, output);
433 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSlice *node)
435 assert(node->arity() == 3);
437 const Tensor *input = getInputTensor(node->input());
438 const Tensor *begin = getInputTensor(node->begin());
439 const Tensor *size = getInputTensor(node->size());
441 Tensor *output = getOutputTensor(node);
443 return std::make_unique<kernels::Slice>(input, begin, size, output);
446 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSoftmax *node)
448 assert(node->arity() == 1);
450 const Tensor *input = getInputTensor(node->logits());
451 Tensor *output = getOutputTensor(node);
453 SoftmaxParams params{};
454 params.beta = node->beta();
456 return std::make_unique<kernels::Softmax>(input, output, params);
459 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSpaceToDepth *node)
461 assert(node->arity() == 1);
462 const Tensor *input = getInputTensor(node->input());
464 Tensor *output = getOutputTensor(node);
466 SpaceToDepthParams params{};
467 params.block_size = node->block_size();
469 return std::make_unique<kernels::SpaceToDepth>(input, output, params);
472 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSplit *node)
474 auto output_nodes = collectOutputNodes<luci::CircleSplitOut>(node);
475 assert(node->arity() == 2);
476 assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
478 const Tensor *axis = getInputTensor(node->split_dim());
479 const Tensor *input = getInputTensor(node->input());
480 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
482 // NOTE 'num_splits' attribute is ignored.
483 return std::make_unique<kernels::Split>(axis, input, std::move(outputs));
486 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqueeze *node)
488 assert(node->arity() == 1);
490 const Tensor *input = getInputTensor(node->input());
491 Tensor *output = getOutputTensor(node);
493 SqueezeParams params{};
494 params.squeeze_dims = node->squeeze_dims();
496 return std::make_unique<kernels::Squeeze>(input, output, params);
499 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleStridedSlice *node)
501 assert(node->arity() == 4);
503 const Tensor *input = getInputTensor(node->input());
504 const Tensor *begin = getInputTensor(node->begin());
505 const Tensor *end = getInputTensor(node->end());
506 const Tensor *strides = getInputTensor(node->strides());
508 Tensor *output = getOutputTensor(node);
510 StridedSliceParams params{};
511 params.begin_mask = node->begin_mask();
512 params.ellipsis_mask = node->ellipsis_mask();
513 params.end_mask = node->end_mask();
514 params.new_axis_mask = node->new_axis_mask();
515 params.shrink_axis_mask = node->shrink_axis_mask();
517 return std::make_unique<kernels::StridedSlice>(input, begin, end, strides, output, params);
520 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTranspose *node)
522 assert(node->arity() == 2);
524 const Tensor *input = getInputTensor(node->a());
525 const Tensor *perm = getInputTensor(node->perm());
526 Tensor *output = getOutputTensor(node);
528 return std::make_unique<kernels::Transpose>(input, perm, output);
531 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTransposeConv *node)
533 assert(node->arity() == 3);
535 const Tensor *input_sizes = getInputTensor(node->inputSizes());
536 const Tensor *filter = getInputTensor(node->filter());
537 const Tensor *out_backprop = getInputTensor(node->outBackprop());
539 Tensor *output = getOutputTensor(node);
541 TransposeConvParams params{};
542 params.padding = node->padding();
543 params.stride_height = node->stride()->h();
544 params.stride_width = node->stride()->w();
546 return std::make_unique<kernels::TransposeConv>(input_sizes, filter, out_backprop, output,
550 std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleUnpack *node)
552 auto output_nodes = collectOutputNodes<luci::CircleUnpackOut>(node);
553 assert(node->arity() == 1);
554 assert(output_nodes.size() == static_cast<size_t>(node->num()));
556 const Tensor *input = getInputTensor(node->value());
557 std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
559 UnpackParams params{};
560 params.axis = node->axis();
562 // NOTE 'num' attribute is ignored.
563 return std::make_unique<kernels::Unpack>(input, std::move(outputs), params);
566 } // namespace luci_interpreter