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 "exec/DynamicShapeInference.h"
18 #include "util/ShapeInference.h"
26 inline backend::IDynamicTensorManager *
27 dynamicTensorManagerOf(const std::shared_ptr<backend::ITensor> &tensor)
29 if (!tensor->dynamic_tensor_manager())
30 throw std::runtime_error{"Dynamic Tensor Manager is not available for this tensor."};
31 return tensor->dynamic_tensor_manager();
34 void DynamicShapeInferer::handleBinaryArithmeticOp(const ir::Operation &op,
35 const ir::OperandIndex lhs_idx,
36 const ir::OperandIndex rhs_idx)
38 auto lhs = _tensor_registry->getITensor(lhs_idx);
39 auto lhs_shape = lhs->getShape();
41 auto rhs = _tensor_registry->getITensor(rhs_idx);
42 auto rhs_shape = rhs->getShape();
45 Here, the state after compilation (satic shape inference) could be one of the following:
47 lhs rhs output execution-time shape inf required
48 ------------------------------------------ ---------------------------------
49 case 1) static static static X
50 case 2) one or both are dynamic dynamic O
52 Then nnfw_apply_tensorinf() could change one or both inputs dynamic.
53 So, in this method, we have one more state and we have to re-calculate shape for this shape.
55 case 3) one or both are dynamic static O
57 So, only when all inputs are static, we can skip dynamic shape inference.
59 if ((!lhs->is_dynamic()) && (!rhs->is_dynamic()))
62 auto output_idx = op.getOutputs().at(0);
63 auto output = _tensor_registry->getITensor(output_idx);
65 ir::Shape new_shape = shape_inference::inferEltwiseShape(lhs_shape, rhs_shape);
67 dynamicTensorManagerOf(output)->applyShape(output_idx, new_shape);
68 assert(output->buffer() != nullptr);
71 void DynamicShapeInferer::handleSimpleUnaryOp(const ir::Operation &op,
72 const ir::OperandIndex input_ind)
74 // check if input is not dynamic
75 auto input = _tensor_registry->getITensor(input_ind);
76 auto output_shape = input->getShape();
79 Here, the state after compilation (satic shape inference) could be one of the following:
81 input output execution-time shape inf required
82 ------------------------- ---------------------------------
83 case 1) static static X
84 case 2) dynamic dynamic O
86 Then nnfw_apply_tensorinf() could change input dynamic.
87 So, in this method, we have one more state and we have to re-calculate shape for this shape.
89 case 3) dynamic static O
91 So, only when input is static, we can skip dynamic shape inference.
93 if (!input->is_dynamic())
96 auto output_ind = op.getOutputs().at(0);
97 auto output = _tensor_registry->getITensor(output_ind);
99 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
100 assert(output->buffer() != nullptr);
103 void DynamicShapeInferer::visit(const ir::operation::ArgMax &op)
105 const auto input_idx{op.getInputs().at(ir::operation::ArgMax::Input::INPUT)};
106 const auto &input = _tensor_registry->getITensor(input_idx);
107 auto input_shape = input->getShape();
109 if (!input->is_dynamic())
112 const auto rank = input_shape.rank();
113 const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
115 assert(0 <= axis && axis < rank);
117 auto output_ind = op.getOutputs().at(0);
118 auto output = _tensor_registry->getITensor(output_ind);
120 ir::Shape new_shape = shape_inference::inferArgMaxShape(input_shape, axis, rank);
122 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
123 assert(output->buffer() != nullptr);
126 void DynamicShapeInferer::visit(const ir::operation::BatchMatMul &op)
128 const auto lhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::LHS);
129 const auto rhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::RHS);
130 auto lhs = _tensor_registry->getITensor(lhs_index);
131 auto rhs = _tensor_registry->getITensor(rhs_index);
133 if (!lhs->is_dynamic() && !rhs->is_dynamic())
136 const auto output_index = op.getOutputs().at(0);
137 auto output = _tensor_registry->getITensor(output_index);
139 auto lhs_shape = lhs->getShape();
140 auto rhs_shape = rhs->getShape();
143 auto new_shape = shape_inference::inferBatchMatMulShape(lhs_shape, rhs_shape, op.param());
144 dynamicTensorManagerOf(output)->applyShape(output_index, new_shape);
147 void DynamicShapeInferer::visit(const ir::operation::BinaryArithmetic &op)
149 handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::BinaryArithmetic::Input::LHS),
150 op.getInputs().at(ir::operation::BinaryArithmetic::Input::RHS));
153 void DynamicShapeInferer::visit(const ir::operation::BroadcastTo &op)
155 auto output_ind = op.getOutputs().at(0);
156 auto output = _tensor_registry->getITensor(output_ind);
158 auto input_idx = op.getInputs().at(ir::operation::BroadcastTo::INPUT);
159 auto input = _tensor_registry->getITensor(input_idx);
161 if ((!input->is_dynamic()) && (!output->is_dynamic()))
164 auto shape_idx = op.getInputs().at(ir::operation::Tile::Input::MULTIPLES);
165 const auto &shape = _tensor_registry->getITensor(shape_idx);
167 assert(shape); // It shouldn't be 0.
169 auto output_shape = shape_inference::inferBroadcastToShape(
170 shape->getShape(), reinterpret_cast<const int32_t *>(shape->buffer()));
172 // set output shape and output buffer
173 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
174 assert(output->buffer() != nullptr);
177 void DynamicShapeInferer::visit(const ir::operation::Comparison &op)
179 handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Comparison::Input::INPUT0),
180 op.getInputs().at(ir::operation::Comparison::Input::INPUT1));
183 void DynamicShapeInferer::visit(const ir::operation::Concat &op)
186 The state after compilation (satic shape inference) could be one of the following:
188 inputs output execution-time shape inf required
189 ------------------------------------------ ---------------------------------
190 case 1) all static static X
191 case 2) at least on is dynamic dynamic O
193 Then nnfw_apply_tensorinf() could change one or both inputs dynamic.
194 So, in this method, we have one more state and we have to re-calculate shape for this shape.
196 case 3) at least on is dynamic static O
198 So, only when all inputs are static, we can skip dynamic shape inference.
200 bool all_static = true;
201 for (auto input_ind : op.getInputs())
203 auto input = _tensor_registry->getITensor(input_ind);
204 if (input->is_dynamic())
216 auto isConcatible = [](const backend::ITensor *input1, const backend::ITensor *input2,
218 if (input1->num_dimensions() != input2->num_dimensions())
221 for (size_t i = 0; i < input1->num_dimensions(); i++)
223 auto positive_axis = (axis >= 0) ? axis : axis + input1->num_dimensions();
225 if (i != positive_axis)
226 if (input1->dimension(i) != input2->dimension(i))
233 auto first_input_ind = op.getInputs().at(0);
234 auto first_input = _tensor_registry->getITensor(first_input_ind);
236 for (auto input_ind : op.getInputs())
238 auto input = _tensor_registry->getITensor(input_ind);
239 if (input != first_input && !isConcatible(first_input.get(), input.get(), op.param().axis))
240 throw std::runtime_error("input shapes does not matched for concat");
244 // getting output shape
245 onert::shape_inference::Shapes in_shapes;
246 for (auto input_ind : op.getInputs())
248 auto input = _tensor_registry->getITensor(input_ind);
249 ir::Shape shape = input->getShape();
251 in_shapes.emplace_back(shape);
254 auto output_ind = op.getOutputs().at(0);
255 auto output = _tensor_registry->getITensor(output_ind);
256 auto output_shape = shape_inference::inferConcatShape(in_shapes, op.param());
258 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
261 void DynamicShapeInferer::visit(const ir::operation::Conv2D &op)
263 // check if input is not dynamic
264 auto input_ind = op.getInputs().at(ir::operation::Conv2D::INPUT);
265 auto input = _tensor_registry->getITensor(input_ind);
267 auto ker_ind = op.getInputs().at(ir::operation::Conv2D::KERNEL);
268 auto ker = _tensor_registry->getITensor(ker_ind);
270 if ((!input->is_dynamic()) && (!ker->is_dynamic()))
273 ir::Shape input_shape = input->getShape();
274 ir::Shape ker_shape = ker->getShape();
276 auto output_ind = op.getOutputs().at(0);
277 auto output = _tensor_registry->getITensor(output_ind);
279 ir::Shape output_shape = shape_inference::inferConv2DShape(input_shape, ker_shape, op.param());
281 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
282 assert(output->buffer() != nullptr);
285 void DynamicShapeInferer::visit(const ir::operation::ElementwiseActivation &op)
287 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseActivation::INPUT));
290 void DynamicShapeInferer::visit(const ir::operation::ElementwiseBinary &op)
292 handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::ElementwiseBinary::Input::LHS),
293 op.getInputs().at(ir::operation::ElementwiseBinary::Input::RHS));
296 void DynamicShapeInferer::visit(const ir::operation::ElementwiseUnary &op)
298 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseUnary::Input::INPUT));
301 void DynamicShapeInferer::visit(const ir::operation::ExpandDims &op)
303 // check if input is not dynamic
304 auto input_ind = op.getInputs().at(ir::operation::ExpandDims::INPUT);
305 auto input = _tensor_registry->getITensor(input_ind);
307 // check if output is not dynamic, meaning when 1st input is static and 2nd input is const
308 auto output_ind = op.getOutputs().at(0);
309 auto output = _tensor_registry->getITensor(output_ind);
312 Here, the state after compilation (satic shape inference) could be one of the following:
314 input1 input2 output execution-time shape inf required
315 ----------------------------- --------------------------------
316 case 1) static const static X
317 case 2) static placeholder dynamic O
318 case 3) dynamic const dynamic O
319 case 4) dynamic placeholder dynamic O
321 Then nnfw_apply_tensorinf() could change input dynamic.
322 So, in this method, we could have one more state and we have to re-calculate shape
325 case 5) dynamic const static O
327 So, only when input1 and ouput are static, we can skip dynamic shape inference.
329 if ((!input->is_dynamic()) && (!output->is_dynamic()))
332 ir::Shape input_shape = input->getShape();
334 auto axis_ind = op.getInputs().at(ir::operation::ExpandDims::AXIS);
335 auto axis = _tensor_registry->getITensor(axis_ind);
336 auto axis_buf = reinterpret_cast<const int32_t *>(axis->buffer());
339 auto output_shape = shape_inference::inferExpandDimsShape(input_shape, axis_buf[0]);
341 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
342 assert(output->buffer() != nullptr);
345 void DynamicShapeInferer::visit(const ir::operation::Fill &op)
347 // check if output is not dynamic
348 auto output_ind = op.getOutputs().at(0);
349 auto output = _tensor_registry->getITensor(output_ind);
350 auto input_ind = op.getInputs().at(ir::operation::Fill::Input::INPUT);
351 auto input = _tensor_registry->getITensor(input_ind);
352 ir::Shape input_shape = input->getShape();
354 if ((!input->is_dynamic()) && (!output->is_dynamic()))
357 assert(input.get()->data_type() == ir::DataType::INT32);
359 auto input_buf = reinterpret_cast<const int32_t *>(input->buffer());
362 auto output_shape = shape_inference::inferFillShape(input_shape, input_buf);
364 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
365 assert(output->buffer() != nullptr);
368 void DynamicShapeInferer::visit(const ir::operation::FullyConnected &op)
370 const auto input_idx{op.getInputs().at(ir::operation::FullyConnected::Input::INPUT)};
371 const auto &input = _tensor_registry->getITensor(input_idx);
373 const auto ker_idx{op.getInputs().at(ir::operation::FullyConnected::Input::WEIGHT)};
374 const auto &ker = _tensor_registry->getITensor(ker_idx);
376 if (!input->is_dynamic() && !ker->is_dynamic())
379 auto input_shape = input->getShape();
380 auto ker_shape = ker->getShape();
382 ir::Shape new_shape = shape_inference::inferFullyConnectedShape(input_shape, ker_shape);
384 auto output_ind = op.getOutputs().at(0);
385 auto output = _tensor_registry->getITensor(output_ind);
387 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
388 assert(output->buffer() != nullptr);
391 void DynamicShapeInferer::visit(const ir::operation::FusedBatchNorm &op)
393 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::FusedBatchNorm::Input::INPUT));
396 void DynamicShapeInferer::visit(const ir::operation::Gather &op)
398 const auto input_idx{op.getInputs().at(ir::operation::Gather::Input::INPUT)};
399 const auto &input = _tensor_registry->getITensor(input_idx);
400 auto input_shape = input->getShape();
402 const auto indices_idx{op.getInputs().at(ir::operation::Gather::Input::INDICES)};
403 const auto &indices = _tensor_registry->getITensor(indices_idx);
404 auto indices_shape = indices->getShape();
406 if (!(input->is_dynamic()) && !(indices->is_dynamic()))
409 const auto rank = input_shape.rank();
410 const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
412 assert(0 <= axis && axis < rank);
414 ir::Shape new_shape = shape_inference::inferGatherShape(input_shape, indices_shape, axis, rank);
416 auto output_ind = op.getOutputs().at(0);
417 auto output = _tensor_registry->getITensor(output_ind);
419 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
420 assert(output->buffer() != nullptr);
423 void DynamicShapeInferer::visit(const ir::operation::L2Normalization &op)
425 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::L2Normalization::INPUT));
428 void DynamicShapeInferer::visit(const ir::operation::MatrixBandPart &op)
430 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::MatrixBandPart::INPUT));
433 void DynamicShapeInferer::visit(const ir::operation::OneHot &op)
435 auto output_ind = op.getOutputs().at(0);
436 auto output = _tensor_registry->getITensor(output_ind);
438 auto indices_ind = op.getInputs().at(ir::operation::OneHot::INDICES);
439 const auto &indices = _tensor_registry->getITensor(indices_ind);
440 auto indices_shape = indices->getShape();
442 auto depth_ind = op.getInputs().at(ir::operation::OneHot::DEPTH);
443 const auto &depth = _tensor_registry->getITensor(depth_ind);
445 if (!indices->is_dynamic() && !depth->is_dynamic())
450 int32_t *depth_buf = reinterpret_cast<int32_t *>(depth->buffer());
452 const auto axis_val = op.param().axis;
454 ir::Shape new_shape = shape_inference::inferOnehotShape(indices_shape, *depth_buf, axis_val);
455 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
456 assert(output->buffer() != nullptr);
459 void DynamicShapeInferer::visit(const ir::operation::Pack &op)
461 bool is_any_of_inputs_dynamic = [&]() -> bool {
462 for (uint32_t i = 0; i < op.getInputs().size(); ++i)
464 const auto &input = _tensor_registry->getITensor(op.getInputs().at(i));
465 if (input->is_dynamic())
473 const auto input_idx{op.getInputs().at(0)};
474 const auto &input = _tensor_registry->getITensor(input_idx);
475 auto input_shape = input->getShape();
477 auto output_ind = op.getOutputs().at(0);
478 auto output = _tensor_registry->getITensor(output_ind);
480 if (!is_any_of_inputs_dynamic && !output->is_dynamic())
483 const auto rank = input_shape.rank() + 1;
484 const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
485 const auto num = op.param().num;
487 assert(0 <= axis && axis < rank);
489 ir::Shape new_shape = shape_inference::inferPackShape(input_shape, axis, rank, num);
491 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
492 assert(output->buffer() != nullptr);
495 void DynamicShapeInferer::visit(const ir::operation::Pad &op)
497 // check if output is not dynamic
498 auto output_ind = op.getOutputs().at(0);
499 auto output = _tensor_registry->getITensor(output_ind);
501 auto input_ind = op.getInputs().at(ir::operation::Pad::Input::INPUT);
502 auto input = _tensor_registry->getITensor(input_ind);
504 auto pad_ind = op.getInputs().at(ir::operation::Pad::Input::PAD);
505 auto pad = _tensor_registry->getITensor(pad_ind);
507 // check if input and output are not dynamic
508 if ((!input->is_dynamic()) && (!output->is_dynamic()))
511 int32_t *pad_buf = reinterpret_cast<int32_t *>(pad->buffer());
515 shape_inference::inferPadShape(input->getShape(), pad_buf, pad->getShape().num_elements());
517 // change output shape and reallocate output tensor memory
518 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
519 assert(output->buffer() != nullptr);
522 void DynamicShapeInferer::visit(const ir::operation::Permute & /* op */)
524 // NOTE Permute is a special operation which does not do shape inference before the actual
525 // function(kernel) execution. Shape inference and output allocation will be done in the kernel
526 // on-the-fly, as it must support inter-backend inference/allocation.
529 void DynamicShapeInferer::visit(const ir::operation::Pow &op)
531 handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Pow::Input::LHS),
532 op.getInputs().at(ir::operation::Pow::Input::RHS));
535 void DynamicShapeInferer::visit(const ir::operation::Range &op)
537 // check if output is not dynamic
538 auto output_ind = op.getOutputs().at(0);
539 auto output = _tensor_registry->getITensor(output_ind);
541 // from op, access the buffer of second input to read new shape
542 auto start_idx = op.getInputs().at(ir::operation::Range::Input::START);
543 auto start_tensor = _tensor_registry->getITensor(start_idx);
545 auto limit_idx = op.getInputs().at(ir::operation::Range::Input::LIMIT);
546 auto limit_tensor = _tensor_registry->getITensor(limit_idx);
548 auto delta_idx = op.getInputs().at(ir::operation::Range::Input::DELTA);
549 auto delta_tensor = _tensor_registry->getITensor(delta_idx);
551 if (!start_tensor->is_dynamic() && !limit_tensor->is_dynamic() && !delta_tensor->is_dynamic() &&
552 !output->is_dynamic())
556 if (output->data_type() == ir::DataType::FLOAT32)
559 shape_inference::inferRangeShape<float>(*reinterpret_cast<float *>(start_tensor->buffer()),
560 *reinterpret_cast<float *>(limit_tensor->buffer()),
561 *reinterpret_cast<float *>(delta_tensor->buffer()));
563 else if (output->data_type() == ir::DataType::INT32)
565 new_shape = shape_inference::inferRangeShape<int32_t>(
566 *reinterpret_cast<int32_t *>(start_tensor->buffer()),
567 *reinterpret_cast<int32_t *>(limit_tensor->buffer()),
568 *reinterpret_cast<int32_t *>(delta_tensor->buffer()));
570 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
571 assert(output->buffer() != nullptr);
574 void DynamicShapeInferer::visit(const ir::operation::Reduce &op)
576 const auto input_idx{op.getInputs().at(ir::operation::Reduce::Input::INPUT)};
577 const auto &input = _tensor_registry->getITensor(input_idx);
578 auto input_shape = input->getShape();
580 const auto axes_idx{op.getInputs().at(ir::operation::Reduce::Input::AXES)};
581 const auto &axes = _tensor_registry->getITensor(axes_idx);
583 if (!input->is_dynamic())
586 std::vector<int32_t> axes_vec;
587 for (uint32_t i = 0; i < axes->getShape().num_elements(); ++i)
589 const auto buffer = axes->buffer() + axes->calcOffset({i});
590 switch (axes->data_type())
592 case ir::DataType::INT32:
594 axes_vec.emplace_back(*reinterpret_cast<const int32_t *>(buffer));
597 case ir::DataType::INT64:
599 axes_vec.emplace_back(*reinterpret_cast<const int64_t *>(buffer));
603 throw std::runtime_error("DynamicShapeInferer " + op.name() + ": Not supported data type");
607 const auto keep_dims = op.param().keep_dims;
609 auto output_ind = op.getOutputs().at(0);
610 auto output = _tensor_registry->getITensor(output_ind);
612 ir::Shape new_shape = shape_inference::inferReduceShape(input_shape, axes_vec, keep_dims);
614 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
615 assert(output->buffer() != nullptr);
618 void DynamicShapeInferer::visit(const ir::operation::Reshape &op)
620 // check if output is not dynamic
621 auto output_ind = op.getOutputs().at(0);
622 auto output = _tensor_registry->getITensor(output_ind);
624 auto input_ind = op.getInputs().at(ir::operation::Reshape::Input::INPUT);
625 auto input = _tensor_registry->getITensor(input_ind);
628 Here, the state after compilation (satic shape inference) could be one of the following:
630 input1 input2 (or option) output execution-time shape inf required
631 ------------------------------------ --------------------------------
632 case 1) static const static X
633 case 2) static placeholder dynamic O
634 case 3) dynamic const dynamic O
635 case 4) dynamic placeholder dynamic O
637 Then nnfw_apply_tensorinf() could change input dynamic.
638 So, in this method, we could have one more state and we have to re-calculate shape
641 case 5) dynamic const static O
643 So, only when both input1 and ouput are static, we can skip dynamic shape inference.
645 if ((!input->is_dynamic()) && (!output->is_dynamic()))
648 // New shape is given by second input tensor
649 if (op.getInputs().size() == 2)
651 // from op, access the buffer of second input to read new shape
652 auto new_shape_ind = op.getInputs().at(ir::operation::Reshape::Input::SHAPE);
654 // getting output shape by reading new_shape tensor buffer
655 auto new_shape = _tensor_registry->getITensor(new_shape_ind);
658 int32_t *new_shape_buf = reinterpret_cast<int32_t *>(new_shape->buffer());
659 assert(new_shape_buf);
661 auto output_shape = shape_inference::inferReshapeShape(
662 new_shape_buf, new_shape->getShape().num_elements(), input->getShape().num_elements());
664 // if shape is changed, change output shape and reallocate output tensor memory
665 if (output_shape != output->getShape() || output->buffer() == nullptr)
667 // change on output shape
668 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
670 assert(output->buffer() != nullptr);
672 // New shape is given by option
673 else if (op.param().new_shape.size() != 0)
675 // Let's check the new_shape option
676 auto shape = op.param().new_shape;
677 auto output_shape = shape_inference::inferReshapeShape(shape.data(), shape.size(),
678 input->getShape().num_elements());
680 // if shape is changed, change output shape and reallocate output tensor memory
681 if (output_shape != output->getShape() || output->buffer() == nullptr)
683 // change on output shape
684 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
686 assert(output->buffer() != nullptr);
690 throw std::runtime_error("Reshape: new shape is missing");
695 void DynamicShapeInferer::visit(const ir::operation::ResizeBilinear &op)
697 // check if output is not dynamic
698 auto output_ind = op.getOutputs().at(0);
699 auto output = _tensor_registry->getITensor(output_ind);
701 auto input_ind = op.getInputs().at(ir::operation::Reshape::Input::INPUT);
702 auto input = _tensor_registry->getITensor(input_ind);
704 if ((!input->is_dynamic()) && (!output->is_dynamic()))
707 // getting output shape from input shape and Params
708 auto output_shape = shape_inference::inferResizeBilinearShape(
709 input->getShape(), op.param().height_out, op.param().width_out);
711 // if shape is changed, change output shape and reallocate output tensor memory
712 if (output_shape != output->getShape() || output->buffer() == nullptr)
714 // change on output shape
715 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
717 assert(output->buffer() != nullptr);
720 void DynamicShapeInferer::visit(const ir::operation::Reverse &op)
722 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Reverse::INPUT));
725 void DynamicShapeInferer::visit(const ir::operation::Select &op)
727 const auto input_cond_idx = op.getInputs().at(ir::operation::Select::Input::CONDITION);
728 const auto &input_cond = _tensor_registry->getITensor(input_cond_idx);
730 const auto input_true_idx = op.getInputs().at(ir::operation::Select::Input::INPUT_TRUE);
731 const auto &input_true = _tensor_registry->getITensor(input_true_idx);
733 const auto input_false_idx = op.getInputs().at(ir::operation::Select::Input::INPUT_FALSE);
734 const auto &input_false = _tensor_registry->getITensor(input_false_idx);
736 if ((!input_cond->is_dynamic()) && (!input_true->is_dynamic()) && (!input_false->is_dynamic()))
741 auto input_cond_shape = input_cond->getShape();
742 auto input_true_shape = input_true->getShape();
743 auto input_false_shape = input_false->getShape();
745 // Select output shpae
746 ir::Shape new_shape =
747 shape_inference::inferSelectShape(input_cond_shape, input_true_shape, input_false_shape);
749 auto output_ind = op.getOutputs().at(0);
750 auto output = _tensor_registry->getITensor(output_ind);
752 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
753 assert(output->buffer() != nullptr);
756 void DynamicShapeInferer::visit(const ir::operation::Shape &op)
758 const auto input_idx{op.getInputs().at(0)};
759 const auto &input = _tensor_registry->getITensor(input_idx);
760 auto input_shape = input->getShape();
762 if (!input->is_dynamic())
765 auto output_ind = op.getOutputs().at(0);
766 auto output = _tensor_registry->getITensor(output_ind);
768 ir::Shape output_shape;
769 output_shape.append(input_shape.rank());
771 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
772 assert(output->buffer() != nullptr);
775 void DynamicShapeInferer::visit(const ir::operation::Slice &op)
777 const auto input_index{op.getInputs().at(ir::operation::Slice::Input::INPUT)};
778 const auto input = _tensor_registry->getITensor(input_index);
779 const auto begins_index{op.getInputs().at(ir::operation::Slice::Input::BEGINS)};
780 const auto begins = _tensor_registry->getITensor(begins_index);
781 const auto sizes_index{op.getInputs().at(ir::operation::Slice::Input::SIZES)};
782 const auto sizes = _tensor_registry->getITensor(sizes_index);
783 auto output_index = op.getOutputs().at(0);
784 auto output = _tensor_registry->getITensor(output_index);
786 if (!(input->is_dynamic() || begins->is_dynamic() || sizes->is_dynamic() || output->is_dynamic()))
791 ir::Shape input_shape = input->getShape();
792 auto begins_buf = reinterpret_cast<const int32_t *>(begins->buffer());
793 auto sizes_buf = reinterpret_cast<const int32_t *>(sizes->buffer());
795 ir::Shape new_shape = shape_inference::inferSliceShape(input_shape, begins_buf, sizes_buf);
797 dynamicTensorManagerOf(output)->applyShape(output_index, new_shape);
798 assert(output->buffer() != nullptr);
801 void DynamicShapeInferer::visit(const ir::operation::Softmax &op)
803 handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Softmax::INPUT));
806 void DynamicShapeInferer::visit(const ir::operation::SpaceToBatchND &op)
808 const auto input_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::INPUT)};
809 const auto block_shape_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::BLOCK_SIZE)};
810 const auto padding_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::PADDINGS)};
811 auto output_idx{op.getOutputs().at(0)};
813 const auto &input = _tensor_registry->getITensor(input_idx);
814 const auto &block_shape = _tensor_registry->getITensor(block_shape_idx);
815 const auto &padding = _tensor_registry->getITensor(padding_idx);
816 auto output = _tensor_registry->getITensor(output_idx);
818 if (!(input->is_dynamic() || block_shape->is_dynamic() || padding->is_dynamic() ||
819 output->is_dynamic()))
824 auto input_shape = input->getShape();
825 auto block_shape_shape = block_shape->getShape();
826 auto padding_shape = padding->getShape();
828 auto block_shape_data = reinterpret_cast<int32_t *>(block_shape->buffer());
829 auto padding_data = reinterpret_cast<int32_t *>(padding->buffer());
831 ir::Shape new_shape = shape_inference::inferSpaceToBatchNDShape(
832 input_shape, block_shape_shape, padding_shape, block_shape_data, padding_data);
834 dynamicTensorManagerOf(output)->applyShape(output_idx, new_shape);
835 assert(output->buffer() != nullptr);
838 void DynamicShapeInferer::visit(const ir::operation::Split &op)
840 const auto input_idx{op.getInputs().at(ir::operation::Split::Input::INPUT)};
841 const auto &input = _tensor_registry->getITensor(input_idx);
843 if (!input->is_dynamic())
848 auto input_shape = input->getShape();
850 const auto axis = op.param().axis;
851 const auto num_splits = op.param().num_splits;
852 const auto rank = input_shape.rank();
853 auto axis_resolved = axis < 0 ? axis + rank : axis;
855 assert(0 <= axis_resolved && axis_resolved < rank);
857 ir::Shape new_shape = shape_inference::inferSplitShape(input_shape, axis_resolved, num_splits);
858 for (int out_tensor_idx = 0; out_tensor_idx < num_splits; out_tensor_idx++)
860 auto output_ind = op.getOutputs().at(out_tensor_idx);
861 auto output = _tensor_registry->getITensor(output_ind);
863 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
864 assert(output->buffer() != nullptr);
868 void DynamicShapeInferer::visit(const ir::operation::SquaredDifference &op)
870 handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::SquaredDifference::Input::LHS),
871 op.getInputs().at(ir::operation::SquaredDifference::Input::RHS));
874 void DynamicShapeInferer::visit(const ir::operation::Squeeze &op)
876 const auto input_idx{op.getInputs().at(ir::operation::Squeeze::Input::INPUT)};
877 const auto &input = _tensor_registry->getITensor(input_idx);
879 if (!input->is_dynamic())
884 auto input_shape = input->getShape();
886 // Squeeze output shpae
887 ir::Shape new_shape = shape_inference::inferSqueezeShape(input_shape, op.param());
889 auto output_ind = op.getOutputs().at(0);
890 auto output = _tensor_registry->getITensor(output_ind);
892 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
893 assert(output->buffer() != nullptr);
896 void DynamicShapeInferer::visit(const ir::operation::StridedSlice &op)
899 const auto input_index{op.getInputs().at(ir::operation::StridedSlice::Input::INPUT)};
900 auto input = _tensor_registry->getITensor(input_index);
901 ir::Shape input_shape = input->getShape();
903 const auto starts_index{op.getInputs().at(ir::operation::StridedSlice::Input::STARTS)};
904 auto starts = _tensor_registry->getITensor(starts_index);
906 const auto ends_index{op.getInputs().at(ir::operation::StridedSlice::Input::ENDS)};
907 auto ends = _tensor_registry->getITensor(ends_index);
909 const auto strides_index{op.getInputs().at(ir::operation::StridedSlice::Input::STRIDES)};
910 auto strides = _tensor_registry->getITensor(strides_index);
912 if (!(input->is_dynamic() || starts->is_dynamic() || ends->is_dynamic() || strides->is_dynamic()))
917 const auto begin_mask = op.param().begin_mask;
918 const auto end_mask = op.param().end_mask;
919 const auto shrink_axis_mask = op.param().shrink_axis_mask;
920 const auto rank = input_shape.rank();
922 auto op_params = shape_inference::buildStridedSliceParams(
923 reinterpret_cast<uint32_t *>(starts->buffer()), reinterpret_cast<uint32_t *>(ends->buffer()),
924 reinterpret_cast<uint32_t *>(strides->buffer()), begin_mask, end_mask, shrink_axis_mask,
927 auto output_index = op.getOutputs().at(0);
928 auto output = _tensor_registry->getITensor(output_index);
930 ir::Shape output_shape =
931 onert::shape_inference::inferStridedSliceShape(input_shape, op_params, rank);
933 dynamicTensorManagerOf(output)->applyShape(output_index, output_shape);
934 assert(output->buffer() != nullptr);
937 void DynamicShapeInferer::visit(const ir::operation::Tile &op)
939 auto output_ind = op.getOutputs().at(0);
940 auto output = _tensor_registry->getITensor(output_ind);
942 auto input_idx = op.getInputs().at(ir::operation::Tile::Input::INPUT);
943 auto input = _tensor_registry->getITensor(input_idx);
945 auto multiplier_idx = op.getInputs().at(ir::operation::Tile::Input::MULTIPLES);
946 auto multiplier = _tensor_registry->getITensor(multiplier_idx);
948 if ((!input->is_dynamic()) && (!output->is_dynamic()))
951 auto input_shape = input->getShape();
952 auto multiplier_buffer = reinterpret_cast<const int32_t *>(multiplier->buffer());
953 assert(multiplier_buffer);
955 auto output_shape = shape_inference::inferTileShape(input_shape, multiplier_buffer);
957 // set output shape and output buffer
958 dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
959 assert(output->buffer() != nullptr);
962 void DynamicShapeInferer::visit(const ir::operation::Transpose &op)
964 // check if output is not dynamic
965 auto output_ind = op.getOutputs().at(0);
966 auto output = _tensor_registry->getITensor(output_ind);
968 // from op, access the buffer of second input to read new shape
969 auto input_ind = op.getInputs().at(ir::operation::Transpose::Input::INPUT);
970 auto input_tensor = _tensor_registry->getITensor(input_ind);
971 auto input_shape = input_tensor->getShape();
973 if (!input_tensor->is_dynamic())
976 const auto perm{op.param().perm};
977 // set output shape, based on input and params
978 ir::Shape new_shape = shape_inference::inferTransposeShape(input_shape, perm);
980 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
981 assert(output->buffer() != nullptr);
984 void DynamicShapeInferer::visit(const ir::operation::Unpack &op)
986 // check if output is not dynamic
987 const auto input_idx{op.getInputs().at(0)};
988 const auto &input = _tensor_registry->getITensor(input_idx);
990 if (!input->is_dynamic())
993 auto input_shape = input->getShape();
995 const auto rank = input_shape.rank();
996 const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
997 const auto num = op.param().num;
999 assert(0 <= axis && axis < rank);
1001 ir::Shape new_shape = shape_inference::inferUnpackShape(input_shape, axis, rank);
1003 for (int out_tensor_idx = 0; out_tensor_idx < num; out_tensor_idx++)
1005 auto output_ind = op.getOutputs().at(out_tensor_idx);
1006 auto output = _tensor_registry->getITensor(output_ind);
1008 dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
1010 assert(output->buffer() != nullptr);
1015 } // namespace onert