ONE - On-device Neural Engine
Loading...
Searching...
No Matches
CircleShapeInferenceRule.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
19#include "Check.h"
20
22
23#include <luci/IR/CircleNodes.h>
26#include <luci/Log.h>
27
28#include <oops/InternalExn.h>
29
30#include <algorithm>
31#include <cassert>
32#include <cmath>
33#include <limits>
34#include <stdexcept>
35
36namespace
37{
38
39std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
40{
41 os << "[";
42 for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
43 {
44 if (r)
45 os << ",";
46
47 if (tensor_shape.dim(r).known())
48 os << tensor_shape.dim(r).value();
49 else
50 os << "?";
51 }
52 os << "]";
53 return os;
54}
55
56loco::TensorShape own_shape(const luci::CircleNode *node)
57{
59 shape.rank(node->rank());
60 for (uint32_t r = 0; r < node->rank(); ++r)
61 {
62 // Shape inference rules in this file did not consider unknown dimension.
63 // If some node has unknown dimension, 0 is inserted and wrong shape
64 // inference was done as a result.
65 // To fix this, new shape inference algorithm is being implemented.
66 // Until new inference algorithm is fully implemented, unknown dimension
67 // would be represented as 1 along with TFLite expression.
68 shape.dim(r) = node->dim(r).known() ? node->dim(r).value() : 1;
69 }
70 return shape;
71}
72
73loco::NodeShape use_own(const luci::CircleNode *node)
74{
75 loco::TensorShape shape = own_shape(node);
76 return loco::NodeShape{shape};
77}
78
86class TensorShapeExpander
87{
88public:
89 TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
90 {
91 // DO NOTHING
92 }
93
94public:
95 loco::TensorShape to(uint32_t output_rank)
96 {
97 auto const &input_shape = _shape;
98 uint32_t const input_rank = input_shape.rank();
99
100 assert(input_rank <= output_rank && "Cannot shrink rank");
101 uint32_t const axis_shift = output_rank - input_rank;
102
104
105 output_shape.rank(output_rank);
106 for (uint32_t axis = 0; axis < output_rank; ++axis)
107 {
108 output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
109 }
110
111 return output_shape;
112 }
113
114private:
115 const loco::TensorShape _shape;
116};
117
121void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
122{
123 auto x_rank = x.rank();
124 auto y_rank = y.rank();
125
126 if (x_rank == y_rank)
127 return;
128
129 TensorShapeExpander x_exp(x);
130 TensorShapeExpander y_exp(y);
131
132 auto xy_rank = std::max(x_rank, y_rank);
133
134 x = x_rank > y_rank ? x : x_exp.to(xy_rank);
135 y = y_rank > x_rank ? y : y_exp.to(xy_rank);
136}
137
141loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
142{
143 assert(x.rank() == y.rank());
144
145 auto rank = x.rank();
146
148
149 output_shape.rank(rank);
150 for (uint32_t axis = 0; axis < rank; ++axis)
151 {
152 auto x_dim = x.dim(axis).known() ? x.dim(axis).value() : 1;
153 auto y_dim = y.dim(axis).known() ? y.dim(axis).value() : 1;
154
155 // each dimension of x and y should be same or one must be 1 if different
156 if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
157 INTERNAL_EXN("Cannot produce expand_dimension of two shapes");
158
159 output_shape.dim(axis) = std::max(x_dim, y_dim);
160 }
161
162 return output_shape;
163}
164
166{
167 auto x_match = x;
168 auto y_match = y;
169
170 expand_rank(x_match, y_match);
171
172 auto output_shape = expand_dimension(x_match, y_match);
173
174 return output_shape;
175}
176
180template <loco::DataType T> std::vector<int64_t> vector_from_constant(luci::CircleConst *const_node)
181{
182 std::vector<int64_t> result;
183
184 for (uint32_t idx = 0; idx < const_node->size<T>(); ++idx)
185 result.push_back(const_node->at<T>(idx));
186
187 return result;
188}
189
190template <class CIRCLENODE> loco::NodeShape broadcast_xy(const CIRCLENODE *node)
191{
192 auto x_shape = luci::shape_get(node->x()).template as<loco::TensorShape>();
193 auto y_shape = luci::shape_get(node->y()).template as<loco::TensorShape>();
194
195 auto output_shape = broadcast_shape(x_shape, y_shape);
196
198}
199
200#define DECLARE_USE_SINGLE(NAME) \
201 template <class CIRCLENODE> loco::NodeShape use_##NAME(const CIRCLENODE *node) \
202 { \
203 auto inputs_shape = luci::shape_get(node->NAME()).template as<loco::TensorShape>(); \
204 return loco::NodeShape{inputs_shape}; \
205 }
206
207DECLARE_USE_SINGLE(input);
208DECLARE_USE_SINGLE(inputs);
210DECLARE_USE_SINGLE(logits);
211
212#undef DECLARE_USE_SINGLE
213
214template <class CIRCLENODE>
215loco::NodeShape use_paddings(const CIRCLENODE *node, const luci::CircleConst *paddings)
216{
217 const loco::DataType S32 = loco::DataType::S32;
218 const loco::DataType S64 = loco::DataType::S64;
219
220 auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
221
222 // TODO support other data type
223 LUCI_ASSERT(paddings->dtype() == S32 || paddings->dtype() == S64, "Support int 32/64 for now");
224 LUCI_ASSERT(paddings->rank() == 2, "paddings should be rank 2")
225
226 int32_t n = paddings->dim(0).value();
227 int32_t v = paddings->dim(1).value();
228
229 LUCI_ASSERT(v == 2, "paddings should be [n, 2]");
230 LUCI_ASSERT(n == int32_t(input_shape.rank()),
231 "paddings [n, 2] should have same value of input rank");
232
233 loco::TensorShape output_shape;
234
235 output_shape.rank(input_shape.rank());
236 for (int32_t ni = 0; ni < n; ++ni)
237 {
238 int32_t idx = ni * 2;
239 int value = input_shape.dim(ni).value();
240 if (paddings->dtype() == S32)
241 {
242 value += paddings->at<S32>(idx + 0); // left
243 value += paddings->at<S32>(idx + 1); // right
244 }
245 else
246 {
247 auto pl = paddings->at<S64>(idx + 0);
248 auto pr = paddings->at<S64>(idx + 1);
249 auto max = static_cast<int64_t>(std::numeric_limits<int32_t>::max());
250 auto low = static_cast<int64_t>(std::numeric_limits<int32_t>::lowest());
251 LUCI_ASSERT(pl <= max, "paddings is over 32 bit limit");
252 LUCI_ASSERT(pl >= low, "paddings is over 32 bit limit");
253 LUCI_ASSERT(pr <= max, "paddings is over 32 bit limit");
254 LUCI_ASSERT(pr >= low, "paddings is over 32 bit limit");
255 value += static_cast<int32_t>(pl); // left
256 value += static_cast<int32_t>(pr); // right
257 }
258 output_shape.dim(ni) = value;
259 }
260
262}
263
264loco::NodeShape infer_add_n(const luci::CircleAddN *node)
265{
266 auto shape = luci::shape_get(node->inputs(0)).as<loco::TensorShape>();
267
268 for (uint32_t idx = 1; idx < node->arity(); ++idx)
269 {
270 auto shape_idx = luci::shape_get(node->inputs(idx)).as<loco::TensorShape>();
271 if (!(shape == shape_idx))
272 {
273 INTERNAL_EXN_V("ADD_N shape not same as the first input: ", idx);
274 }
275 }
276 return loco::NodeShape{shape};
277}
278
279template <class CIRCLENODE> loco::NodeShape infer_arg_maxmin(const CIRCLENODE *node)
280{
281 auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
282 auto dimension_shape = luci::shape_get(node->dimension()).template as<loco::TensorShape>();
283
284 int64_t select_axis = 0;
285 {
286 LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr");
287
288 // Only support node's shape() is CircleConst with S32/S64
289 // Support S32 for now.
290 auto const_shape_node = loco::must_cast<luci::CircleConst *>(node->dimension());
291 LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
292 "Only support int32 CircleConst for CircleArgMax/CircleArgMin");
293
294 if (const_shape_node->rank() > 1)
295 INTERNAL_EXN_V("Only support rank 0/1 CircleConst",
296 oops::to_uint32(const_shape_node->rank()));
297
298 select_axis = const_shape_node->template scalar<loco::DataType::S32>();
299 }
300
301 assert(select_axis < input_shape.rank());
302
303 if (select_axis < 0)
304 select_axis += static_cast<int64_t>(input_shape.rank());
305
306 // NOTE select_axis is removed
307 loco::TensorShape shape_output;
308 uint32_t rank = input_shape.rank();
309 uint32_t shrink = static_cast<uint32_t>(select_axis);
310 assert(rank > 0);
311 shape_output.rank(rank - 1);
312 for (uint32_t r = 0, d = 0; r < rank; ++r)
313 {
314 if (r == shrink)
315 continue;
316 shape_output.dim(d++) = input_shape.dim(r);
317 }
318 return loco::NodeShape{shape_output};
319}
320
321// Call this for CircleAvgPool2D and CircleMaxPool2D only
322template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node)
323{
324 auto ifm_shape = luci::shape_get(node->value()).template as<loco::TensorShape>();
325 assert(ifm_shape.rank() == 4);
326 assert(ifm_shape.dim(1).known());
327 assert(ifm_shape.dim(2).known());
328
329 uint32_t input_height = ifm_shape.dim(1).value();
330 uint32_t input_width = ifm_shape.dim(2).value();
331 uint32_t stride_height = node->stride()->h();
332 uint32_t stride_width = node->stride()->w();
333 uint32_t window_height = node->filter()->h();
334 uint32_t window_width = node->filter()->w();
335 uint32_t dilation_height = 1; // dilation for CircleAvgPool2D and CircleMaxPool2D is 1
336 uint32_t dilation_width = 1;
337 uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
338 uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
339
340 uint32_t output_height = 0;
341 uint32_t output_width = 0;
342
343 if (node->padding() == luci::Padding::VALID)
344 {
345 LUCI_ASSERT(input_height + stride_height > effective_window_height, "Invalid shape");
346 LUCI_ASSERT(input_width + stride_width > effective_window_width, "Invalid shape");
347 output_height = (input_height + stride_height - effective_window_height) / stride_height;
348 output_width = (input_width + stride_width - effective_window_width) / stride_width;
349 }
350 else if (node->padding() == luci::Padding::SAME)
351 {
352 output_height = (input_height + stride_height - 1) / stride_height;
353 output_width = (input_width + stride_width - 1) / stride_width;
354 }
355 else
356 LUCI_ASSERT(false, "Wrong padding type");
357
358 loco::TensorShape ofm_shape;
359 ofm_shape.rank(4);
360 ofm_shape.dim(0) = ifm_shape.dim(0);
361 ofm_shape.dim(1) = output_height;
362 ofm_shape.dim(2) = output_width;
363 ofm_shape.dim(3) = ifm_shape.dim(3);
364
365 return loco::NodeShape{ofm_shape};
366}
367
368loco::NodeShape infer_batch_to_space_nd(const luci::CircleBatchToSpaceND *node)
369{
370 const loco::DataType S32 = loco::DataType::S32;
371
372 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
373 // Support only input rank is 3 and 4
374 assert(input_shape.rank() == 3 || input_shape.rank() == 4);
375
376 // Only support block_shape() with S32 type CircleConst for now
377 auto const_block_shape = loco::must_cast<luci::CircleConst *>(node->block_shape());
378 LUCI_ASSERT(const_block_shape->dtype() == loco::DataType::S32, "Only support int32 block_shape");
379
380 // Only support crops() with S32 type CircleConst for now
381 auto const_crops = loco::must_cast<luci::CircleConst *>(node->crops());
382 LUCI_ASSERT(const_crops->dtype() == loco::DataType::S32, "Only support int32 crops");
383
384 auto const_block_shape_shape = luci::shape_get(const_block_shape).as<loco::TensorShape>();
385 auto const_crops_shape = luci::shape_get(const_crops).as<loco::TensorShape>();
386 assert(const_block_shape_shape.rank() == 1);
387 assert(const_crops_shape.rank() == 2);
388
389 int32_t input_spatial_dim = input_shape.rank() - 2;
390 assert(const_block_shape_shape.dim(0) == input_spatial_dim);
391 assert(const_crops_shape.dim(0) == input_spatial_dim);
392 assert(const_crops_shape.dim(1) == 2);
393
394 loco::TensorShape shape_output;
395
396 shape_output.rank(input_shape.rank());
397
398 int32_t output_batch_size = input_shape.dim(0).value();
399 for (int32_t dim = 0; dim < input_spatial_dim; ++dim)
400 {
401 int dim_size = input_shape.dim(dim + 1).value() * const_block_shape->at<S32>(dim);
402 dim_size -= const_crops->at<S32>(dim * 2);
403 dim_size -= const_crops->at<S32>(dim * 2 + 1);
404 shape_output.dim(dim + 1) = dim_size;
405
406 assert(output_batch_size % const_block_shape->at<S32>(dim) == 0);
407 output_batch_size = output_batch_size / const_block_shape->at<S32>(dim);
408 }
409 shape_output.dim(0) = output_batch_size;
410 shape_output.dim(input_shape.rank() - 1) = input_shape.dim(input_shape.rank() - 1);
411
412 return loco::NodeShape{shape_output};
413}
414
415struct OutputSize
416{
417 uint32_t height = 0;
418 uint32_t width = 0;
419};
420
421template <class Conv2DType> OutputSize infer_conv2d_type(const Conv2DType *node)
422{
423 auto ifm_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
424 auto ker_shape = luci::shape_get(node->filter()).template as<loco::TensorShape>();
425 assert(ifm_shape.rank() == 4);
426 assert(ker_shape.rank() == 4);
427 assert(ifm_shape.dim(1).known());
428 assert(ifm_shape.dim(2).known());
429 assert(ker_shape.dim(1).known());
430 assert(ker_shape.dim(2).known());
431
432 uint32_t input_height = ifm_shape.dim(1).value();
433 uint32_t input_width = ifm_shape.dim(2).value();
434 uint32_t stride_height = node->stride()->h();
435 uint32_t stride_width = node->stride()->w();
436 uint32_t ker_height = ker_shape.dim(1).value();
437 uint32_t ker_width = ker_shape.dim(2).value();
438 uint32_t dilation_height = node->dilation()->h();
439 uint32_t dilation_width = node->dilation()->w();
440 uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
441 uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
442
443 uint32_t output_height = 0;
444 uint32_t output_width = 0;
445
446 if (node->padding() == luci::Padding::VALID)
447 {
448 LUCI_ASSERT(input_height + stride_height > effective_ker_height, "Invalid shape");
449 LUCI_ASSERT(input_width + stride_width > effective_ker_width, "Invalid shape");
450 output_height = (input_height + stride_height - effective_ker_height) / stride_height;
451 output_width = (input_width + stride_width - effective_ker_width) / stride_width;
452 }
453 else if (node->padding() == luci::Padding::SAME)
454 {
455 output_height = (input_height + stride_height - 1) / stride_height;
456 output_width = (input_width + stride_width - 1) / stride_width;
457 }
458 else
459 LUCI_ASSERT(false, "Wrong padding type");
460
461 OutputSize os{output_height, output_width};
462
463 return os;
464}
465
466loco::NodeShape infer_broadcast_to(const luci::CircleBroadcastTo *node)
467{
468 const loco::DataType S32 = loco::DataType::S32;
469
470 loco::TensorShape shape_by_input;
471 {
472 LUCI_ASSERT(node->shape(), "2nd input shape() should not be nullptr");
473
474 // Only support node's shape() is CircleConst with S32
475 auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->shape());
476 if (const_shape_node != nullptr)
477 {
478 LUCI_ASSERT(const_shape_node->dtype() == S32, "Only support int32 CircleConst");
479
480 shape_by_input.rank(const_shape_node->size<S32>());
481 for (uint32_t axis = 0; axis < shape_by_input.rank(); ++axis)
482 {
483 shape_by_input.dim(axis) = const_shape_node->at<S32>(axis);
484 }
485 }
486 else
487 {
488 // We use shape from the node itself
489 shape_by_input = own_shape(node);
490 }
491 }
492
493 return loco::NodeShape{shape_by_input};
494}
495
496loco::NodeShape infer_conv2d(const luci::CircleConv2D *node)
497{
498 LOGGER(l);
499
500 auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
501 auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI
502
503 assert(ifm_shape.rank() == 4);
504 assert(ker_shape.rank() == 4);
505 assert(ifm_shape.dim(3) == ker_shape.dim(3));
506
507 auto os = infer_conv2d_type(node);
508
509 loco::TensorShape ofm_shape;
510 ofm_shape.rank(4);
511 ofm_shape.dim(0) = ifm_shape.dim(0);
512 ofm_shape.dim(1) = os.height;
513 ofm_shape.dim(2) = os.width;
514 ofm_shape.dim(3) = ker_shape.dim(0);
515
516 INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker(" << ker_shape.rank()
517 << ") output(" << ofm_shape.dim(0).value() << "," << ofm_shape.dim(1).value() << ","
518 << ofm_shape.dim(2).value() << "," << ofm_shape.dim(3).value() << ") " << node->name()
519 << std::endl;
520
521 return loco::NodeShape{ofm_shape};
522}
523
524loco::NodeShape infer_depth_to_space(const luci::CircleDepthToSpace *node)
525{
526 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
527 LUCI_ASSERT(input_shape.rank() == 4, "Only input rank 4 is supported");
528
529 // Only data format NHWC is supported
530 // TODO need to clarify what to do with layout in this operator
531 int32_t height = input_shape.dim(1).value();
532 int32_t width = input_shape.dim(2).value();
533 int32_t depth = input_shape.dim(3).value();
534
535 int block_size = node->block_size();
536
537 if (block_size < 2)
538 INTERNAL_EXN("Block size must be >= 2");
539
540 if (depth % (block_size * block_size))
541 {
542 INTERNAL_EXN("The input tensor's depth must be divisible by block_size^2");
543 }
544
546 output_shape.rank(4);
547
548 output_shape.dim(0) = input_shape.dim(0).value();
549 output_shape.dim(1) = height * block_size;
550 output_shape.dim(2) = width * block_size;
551 output_shape.dim(3) = depth / (block_size * block_size);
552
554}
555
556loco::NodeShape infer_depthwise_conv2d(const luci::CircleDepthwiseConv2D *node)
557{
558 auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
559 auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM
560
561 assert(ifm_shape.rank() == 4);
562 assert(ker_shape.rank() == 4);
563 assert(ker_shape.dim(0).value() == 1);
564 assert(ifm_shape.dim(3).value() * node->depthMultiplier() == ker_shape.dim(3).value());
565
566 auto os = infer_conv2d_type(node);
567
568 loco::TensorShape ofm_shape;
569 ofm_shape.rank(4);
570 ofm_shape.dim(0) = ifm_shape.dim(0);
571 ofm_shape.dim(1) = os.height;
572 ofm_shape.dim(2) = os.width;
573 ofm_shape.dim(3) = ker_shape.dim(3);
574
575 return loco::NodeShape{ofm_shape};
576}
577
578loco::NodeShape infer_expand_dims(const luci::CircleExpandDims *node)
579{
580 const loco::DataType S32 = loco::DataType::S32;
581 auto x_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
582 if (x_shape.rank() == 0)
583 {
584 // This maybe for unknown shape. We use shape from the node itself.
585 return use_own(node);
586 }
587 auto const_axis = loco::must_cast<luci::CircleConst *>(node->axis());
588 LUCI_ASSERT(const_axis->dtype() == S32, "Only support int32 CircleConst for axis");
589 if (const_axis->rank() != 0 && const_axis->rank() != 1)
590 {
591 INTERNAL_EXN_V("Non-scalar axis in OP", node->opnum());
592 }
593 int32_t axis = const_axis->at<S32>(0);
594 LUCI_ASSERT((axis <= static_cast<int32_t>(x_shape.rank())) &&
595 (axis >= -1 - static_cast<int32_t>(x_shape.rank())),
596 "Axis has to be between [-(D+1), D], where D is rank of input.");
597 size_t positive_axis = axis < 0 ? x_shape.rank() + axis + 1 : axis;
599 output_shape.rank(x_shape.rank() + 1);
600 size_t i = 0;
601 for (; i < positive_axis; i++)
602 output_shape.dim(i) = x_shape.dim(i);
603 output_shape.dim(i) = loco::Dimension(1);
604 for (; i < x_shape.rank(); i++)
605 output_shape.dim(i + 1) = x_shape.dim(i);
607}
608
609loco::NodeShape infer_fill(const luci::CircleFill *node)
610{
611 loco::TensorShape shape;
612 {
613 LUCI_ASSERT(node->dims(), "dims input should not be nullptr");
614
615 auto dims_node = dynamic_cast<luci::CircleConst *>(node->dims());
616 if (dims_node != nullptr)
617 {
618 // Only support node with S32
619 LUCI_ASSERT(dims_node->dtype() == loco::DataType::S32, "Only support int32 CircleConst");
620
621 if (dims_node->rank() != 1)
622 INTERNAL_EXN_V("Only support rank 1 CircleConst", oops::to_uint32(dims_node->rank()));
623
624 shape.rank(dims_node->dim(0).value());
625
626 for (uint32_t axis = 0; axis < shape.rank(); ++axis)
627 {
628 shape.dim(axis) = dims_node->at<loco::DataType::S32>(axis);
629 }
630 }
631 else
632 {
633 shape = own_shape(node);
634 }
635 }
636
637 return loco::NodeShape{shape};
638}
639
640loco::NodeShape infer_gather(const luci::CircleGather *node)
641{
643
644 const auto input_shape = luci::shape_get(node->params()).as<loco::TensorShape>();
645 const auto positions_shape = luci::shape_get(node->indices()).as<loco::TensorShape>();
646 int32_t axis = node->axis();
647
648 // If CircleGather input has a dynamic shape, it can't inference this shape. So, it returns the
649 // shape that node already has.
650 if (input_shape.rank() == 0 || positions_shape.rank() == 0)
651 return use_own(node);
652
653 if (axis < 0)
654 axis += input_shape.rank();
655
656 output_shape.rank(input_shape.rank() - 1 + positions_shape.rank());
657 int32_t outdim_index = 0;
658 for (int32_t i = 0; i < axis; ++i)
659 output_shape.dim(outdim_index++) = input_shape.dim(i);
660 for (uint32_t i = 0; i < positions_shape.rank(); ++i)
661 output_shape.dim(outdim_index++) = positions_shape.dim(i);
662 for (uint32_t i = axis + 1; i < input_shape.rank(); ++i)
663 output_shape.dim(outdim_index++) = input_shape.dim(i);
664
666}
667
668loco::NodeShape infer_gather_nd(const luci::CircleGatherNd *node)
669{
671
672 const auto params_shape = luci::shape_get(node->params()).as<loco::TensorShape>();
673 const auto indices_shape = luci::shape_get(node->indices()).as<loco::TensorShape>();
674
675 const auto params_rank = params_shape.rank();
676 const auto indices_rank = indices_shape.rank();
677
678 // see https://www.tensorflow.org/api_docs/python/tf/gather_nd
679 // output.shape = indices.shape[:-1] + params.shape[indices.shape[-1]:]
680 // batch_dims isn't supported in tflite
681
682 // TODO: replace exceptions with setting shape to unknown?
683
684 if (!indices_shape.dim(indices_rank - 1).known())
685 INTERNAL_EXN("Last indices dimension is unknown");
686
687 auto indices_last_dim = indices_shape.dim(indices_rank - 1).value();
688
689 if (indices_last_dim > params_rank)
690 INTERNAL_EXN("Last indices dimension should be <= params rank");
691
692 const uint32_t output_rank = indices_rank + params_rank - indices_last_dim - 1;
693
694 output_shape.rank(output_rank);
695
696 uint32_t output_index = 0;
697 for (uint32_t i = 0; i < indices_rank - 1; ++i)
698 {
699 auto &dim = indices_shape.dim(i);
700 if (!dim.known())
701 INTERNAL_EXN("Unknown indices dimension is unsupported");
702 output_shape.dim(output_index++).set(dim.value());
703 }
704
705 for (uint32_t i = indices_last_dim; i < params_rank; ++i)
706 {
707 auto &dim = params_shape.dim(i);
708 if (!dim.known())
709 INTERNAL_EXN("Unknown params dimension is unsupported");
710 output_shape.dim(output_index++).set(dim.value());
711 }
712
714}
715
716loco::NodeShape infer_matrix_diag(const luci::CircleMatrixDiag *node)
717{
719
720 auto diagonal_shape = luci::shape_get(node->diagonal()).as<loco::TensorShape>();
721 auto rank = diagonal_shape.rank();
722
723 output_shape.rank(rank + 1);
724
725 for (uint32_t i = 0; i < rank; i++)
726 {
727 output_shape.dim(i) = diagonal_shape.dim(i);
728 }
729
730 output_shape.dim(rank) = diagonal_shape.dim(rank - 1);
731
733}
734
735loco::NodeShape infer_matrix_set_diag(const luci::CircleMatrixSetDiag *node)
736{
737 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
738 auto diagonal_shape = luci::shape_get(node->diagonal()).as<loco::TensorShape>();
739
740 auto rank = diagonal_shape.rank();
741
742 LUCI_ASSERT(rank == input_shape.rank() - 1, "diagonal rank = input rank - 1");
743
744 for (uint32_t i = 0; i < rank - 1; i++)
745 {
746 LUCI_ASSERT(diagonal_shape.dim(i) == input_shape.dim(i), "diagonal dims = input dims");
747 }
748
749 auto dim = std::min(input_shape.dim(rank - 1).value(), input_shape.dim(rank).value());
750
751 LUCI_ASSERT(dim == diagonal_shape.dim(rank - 1), "Max diag len error");
752
753 return loco::NodeShape{input_shape};
754}
755
756loco::TensorShape infer_reducer(const loco::Node *input, const loco::Node *indices, bool keep_dims)
757{
758 const loco::DataType S32 = loco::DataType::S32;
759
760 auto input_shape = luci::shape_get(input).as<loco::TensorShape>();
761 auto reduction_indices = loco::must_cast<const luci::CircleConst *>(indices);
762
763 { // Exceptions
764 // TODO support non-const case
765 // TODO support other data type
766 LUCI_ASSERT(reduction_indices->dtype() == S32, "Only support int 32");
767 }
768
769 std::vector<int32_t> reduction_values;
770
771 for (uint32_t i = 0; i < reduction_indices->size<S32>(); ++i)
772 {
773 int32_t axis = reduction_indices->at<S32>(i);
774 if (axis < 0)
775 axis += input_shape.rank();
776 if (not(0 <= axis and axis < static_cast<int32_t>(input_shape.rank())))
777 INTERNAL_EXN_V("Invalid reduction axis for REDUCER", oops::to_uint32(axis));
778 reduction_values.push_back(axis);
779 }
780
782
783 if (keep_dims)
784 {
785 output_shape.rank(input_shape.rank());
786 for (uint32_t i = 0; i < input_shape.rank(); ++i)
787 output_shape.dim(i) = input_shape.dim(i);
788 for (uint32_t i = 0; i < reduction_values.size(); ++i)
789 output_shape.dim(reduction_values.at(i)) = 1;
790 }
791 else
792 {
793 std::vector<bool> check_reduce(input_shape.rank(), false);
794 for (uint32_t i = 0; i < reduction_values.size(); ++i)
795 check_reduce.at(reduction_values.at(i)) = true;
796
797 uint32_t reduce_cnt = 0;
798 for (uint32_t i = 0; i < check_reduce.size(); ++i)
799 if (check_reduce.at(i))
800 ++reduce_cnt;
801
802 output_shape.rank(input_shape.rank() - reduce_cnt);
803 for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i)
804 if (check_reduce.at(i) == false)
805 output_shape.dim(j++) = input_shape.dim(i);
806 }
807
808 return output_shape;
809}
810
811loco::NodeShape infer_mirror_pad(const luci::CircleMirrorPad *node)
812{
813 // TODO support non-const case
814 auto paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
815 return use_paddings(node, paddings);
816}
817
818loco::NodeShape infer_one_hot(const luci::CircleOneHot *node)
819{
820 const loco::DataType S32 = loco::DataType::S32;
821 auto indices_shape = luci::shape_get(node->indices()).as<loco::TensorShape>();
822 // Only support OneHot node's depth() is CircleConst with type S32
823 // TODO support depth with other types
824 auto depth = loco::must_cast<luci::CircleConst *>(node->depth());
825 LUCI_ASSERT(depth->dtype() == S32, "Only support int32 CircleConst");
826 if (depth->rank() != 0)
827 INTERNAL_EXN_V("Only support rank 0 CircleOneHot in Depth", oops::to_uint32(depth->rank()));
829 output_shape.rank(indices_shape.rank() + 1);
830 auto axis = node->axis();
831 if (axis < 0)
832 axis += indices_shape.rank() + 1;
833 LUCI_ASSERT(0 <= axis, "Axis is out of range");
834 LUCI_ASSERT(static_cast<uint32_t>(axis) <= indices_shape.rank(), "Axis is out of range");
835 uint32_t j = 0;
836 for (uint32_t i = 0; i < output_shape.rank(); i++)
837 {
838 if (i == static_cast<uint32_t>(axis))
839 {
840 output_shape.dim(i) = depth->at<S32>(0);
841 }
842 else
843 {
844 output_shape.dim(i) = indices_shape.dim(j++);
845 }
846 }
848}
849
850loco::NodeShape infer_pack(const luci::CirclePack *node)
851{
852 LUCI_ASSERT(node->values_count() > 0, "Only support one or more inputs");
853
854 auto first_shape = luci::shape_get(node->values(0)).as<loco::TensorShape>();
855 // Make sure all inputs have the same shape.
856 for (uint32_t i = 1; i < node->values_count(); ++i)
857 {
858 auto in_shape = luci::shape_get(node->values(i)).as<loco::TensorShape>();
859 LUCI_ASSERT(loco::NodeShape{first_shape} == loco::NodeShape{in_shape},
860 "All inputs must have the same shape");
861 }
862
863 // Checking shape capability for pack layer
864 // Input: tensors [D1, D2, ... Dn]
865 // Axis: K
866 // Output: [D1, D2, ... , D_K-1, n, D_K+1, ... Dn]
867 auto axis = node->axis();
868 if (axis < 0)
869 axis += first_shape.rank() + 1;
870
871 LUCI_ASSERT(0 <= axis, "Axis is out of range");
872 LUCI_ASSERT(static_cast<uint32_t>(axis) <= first_shape.rank(), "Axis is out of range");
873
875 output_shape.rank(first_shape.rank() + 1);
876
877 uint32_t j = 0;
878 for (uint32_t i = 0; i < output_shape.rank(); ++i)
879 {
880 if (i == static_cast<uint32_t>(axis))
881 {
882 output_shape.dim(i) = node->values_count();
883 }
884 else
885 {
886 output_shape.dim(i) = first_shape.dim(j++);
887 }
888 }
889
891}
892
893loco::NodeShape infer_pad_v2(const luci::CirclePadV2 *node)
894{
895 // TODO support non-const case
896 auto paddings = dynamic_cast<luci::CircleConst *>(node->paddings());
897 if (!paddings)
898 {
899 auto node_shape = own_shape(node);
901 }
902 return use_paddings(node, paddings);
903}
904
905loco::NodeShape infer_p_relu(const luci::CirclePRelu *node)
906{
907 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
908 auto alpha_shape = luci::shape_get(node->alpha()).as<loco::TensorShape>();
909
910 auto output_shape = broadcast_shape(input_shape, alpha_shape);
911
913}
914
915template <class CIRCLENODE> loco::NodeShape infer_resize_type(const CIRCLENODE *node)
916{
917 auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
918
919 if (input_shape.rank() != 4)
920 INTERNAL_EXN("Expected input to have rank 4");
921
922 auto *const_node = loco::must_cast<luci::CircleConst *>(node->size());
923
924 if (const_node->dtype() != loco::DataType::S32)
925 INTERNAL_EXN("Only S32 datatype is supported for size");
926
927 if (const_node->rank() != 1)
928 INTERNAL_EXN("Expected size tensor of rank 1");
929
930 if (const_node->dim(0).value() != 2)
931 INTERNAL_EXN("Expected size tensor with shape [2]");
932
934 output_shape.rank(4);
935 output_shape.dim(0) = input_shape.dim(0);
936 output_shape.dim(1) = const_node->template at<loco::DataType::S32>(0);
937 output_shape.dim(2) = const_node->template at<loco::DataType::S32>(1);
938 output_shape.dim(3) = input_shape.dim(3);
939
941}
942
943loco::NodeShape infer_scatter_nd(const luci::CircleScatterNd *node)
944{
946
947 auto shape_node = loco::must_cast<luci::CircleConst *>(node->shape());
948
949 const loco::DataType S32 = loco::DataType::S32;
950 const loco::DataType S64 = loco::DataType::S64;
951
952 std::vector<int64_t> vect_shape;
953
954 if (shape_node->dtype() == S32)
955 vect_shape = vector_from_constant<S32>(shape_node);
956 else if (shape_node->dtype() == S64)
957 vect_shape = vector_from_constant<S64>(shape_node);
958 else
959 LUCI_ASSERT(false, "Only support int32/int64 for shape()");
960
961 output_shape.rank(vect_shape.size());
962 for (uint32_t i = 0; i < vect_shape.size(); ++i)
963 output_shape.dim(i) = vect_shape[i];
964
966}
967
968loco::NodeShape infer_segment_sum(const luci::CircleSegmentSum *node)
969{
970 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
971 auto segment_shape = luci::shape_get(node->segment_ids()).as<loco::TensorShape>();
972
973 LUCI_ASSERT(segment_shape.rank() == 1, "segment_ids must be 1-D tensor");
974 LUCI_ASSERT(segment_shape.dim(0).value() == input_shape.dim(0).value(),
975 "segment_ids size must be equal to the size of data's first dimension");
976
977 auto ids_shape_value = loco::must_cast<luci::CircleConst *>(node->segment_ids());
978
979 std::vector<int64_t> vect_ids;
980
981 if (ids_shape_value->dtype() == loco::DataType::S32)
982 vect_ids = vector_from_constant<loco::DataType::S32>(ids_shape_value);
983
984 LUCI_ASSERT(std::is_sorted(vect_ids.begin(), vect_ids.end()),
985 "segment_ids values should be sorted")
986
987 loco::TensorShape output_shape;
988
989 output_shape.rank(input_shape.rank());
990
991 for (uint32_t i = 1; i < input_shape.rank(); ++i)
992 output_shape.dim(i) = input_shape.dim(i);
993
994 output_shape.dim(0) = vect_ids.back() + 1;
995
996 return loco::NodeShape{output_shape};
997}
998
999loco::NodeShape infer_select(const luci::CircleSelect *node)
1000{
1001 auto t_shape = luci::shape_get(node->t()).as<loco::TensorShape>();
1002 assert(t_shape == luci::shape_get(node->e()).as<loco::TensorShape>());
1003
1004 // condition shape validation
1005 auto c_shape = luci::shape_get(node->condition()).as<loco::TensorShape>();
1006 if (c_shape.rank() != t_shape.rank())
1007 {
1008 if (c_shape.rank() != 0 && c_shape.rank() != 1)
1009 INTERNAL_EXN_V("CircleSelect condition rank is not 0 nor 1: ", c_shape.rank());
1010
1011 if (c_shape.rank() == 1)
1012 {
1013 if (c_shape.dim(0).value() != t_shape.dim(0).value())
1014 INTERNAL_EXN("CircleSelect condition dim(0) should match with t.dim(0)");
1015 }
1016 }
1017
1018 return loco::NodeShape{t_shape};
1019}
1020
1021loco::NodeShape infer_select_v2(const luci::CircleSelectV2 *node)
1022{
1023 auto c_shape = luci::shape_get(node->condition()).as<loco::TensorShape>();
1024 auto t_shape = luci::shape_get(node->t()).as<loco::TensorShape>();
1025 auto e_shape = luci::shape_get(node->e()).as<loco::TensorShape>();
1026
1027 // validate ability to broadcast shapes to each other
1028 auto b_shape = broadcast_shape(broadcast_shape(c_shape, t_shape), e_shape);
1029 return loco::NodeShape{b_shape};
1030}
1031
1032loco::NodeShape infer_shape(const luci::CircleShape *node)
1033{
1034 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1035
1037
1038 output_shape.rank(1);
1039 output_shape.dim(0) = input_shape.rank();
1040
1042}
1043
1044loco::NodeShape infer_slice(const luci::CircleSlice *node)
1045{
1046 const loco::DataType S32 = loco::DataType::S32;
1047 const loco::DataType S64 = loco::DataType::S64;
1048
1049 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1050
1051 auto const_begin = loco::must_cast<luci::CircleConst *>(node->begin());
1052 auto const_size = loco::must_cast<luci::CircleConst *>(node->size());
1053
1055 std::vector<int64_t> vect_begin; // to hold both S32/S64, we use int64_t
1056 std::vector<int64_t> vect_size;
1057
1058 if (const_begin->dtype() == S32)
1059 vect_begin = vector_from_constant<S32>(const_begin);
1060 else if (const_begin->dtype() == S64)
1061 vect_begin = vector_from_constant<S64>(const_begin);
1062 else
1063 LUCI_ASSERT(false, "Only support int32/int64 for begin()");
1064
1065 if (const_size->dtype() == S32)
1066 vect_size = vector_from_constant<S32>(const_size);
1067 else if (const_size->dtype() == S64)
1068 vect_size = vector_from_constant<S64>(const_size);
1069 else
1070 LUCI_ASSERT(false, "Only support int32/int64 for size()");
1071
1072 assert(input_shape.rank() == vect_begin.size());
1073 assert(input_shape.rank() == vect_size.size());
1074
1075 output_shape.rank(vect_begin.size());
1076 for (uint32_t idx = 0; idx < vect_begin.size(); ++idx)
1077 {
1078 auto size = vect_size.at(idx);
1079 if (size == -1)
1080 {
1081 size = static_cast<int64_t>(input_shape.dim(idx).value()) - vect_begin.at(idx);
1082 }
1083 output_shape.dim(idx) = size;
1084 }
1085
1087}
1088
1089loco::NodeShape infer_space_to_batch_nd(const luci::CircleSpaceToBatchND *node)
1090{
1091 const loco::DataType S32 = loco::DataType::S32;
1092
1093 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1094 // Support only input rank is 3 and 4
1095 assert(input_shape.rank() == 3 || input_shape.rank() == 4);
1096
1097 // Only support block_shape() with S32 type CircleConst for now
1098 auto const_block_shape = loco::must_cast<luci::CircleConst *>(node->block_shape());
1099 LUCI_ASSERT(const_block_shape->dtype() == S32, "Only support int32 block_shape");
1100
1101 // Only support paddings() with S32 type CircleConst for now
1102 auto const_paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
1103 LUCI_ASSERT(const_paddings->dtype() == S32, "Only support int32 paddings");
1104
1105 auto const_block_shape_shape = luci::shape_get(const_block_shape).as<loco::TensorShape>();
1106 auto const_paddings_shape = luci::shape_get(const_paddings).as<loco::TensorShape>();
1107 assert(const_block_shape_shape.rank() == 1);
1108 assert(const_paddings_shape.rank() == 2);
1109
1110 int32_t input_spatial_dim = input_shape.rank() - 2;
1111 assert(const_block_shape_shape.dim(0) == input_spatial_dim);
1112 assert(const_paddings_shape.dim(0) == input_spatial_dim);
1113 assert(const_paddings_shape.dim(1) == 2);
1114
1115 // Check all values of block_shape >= 1
1116 uint32_t ele_count = const_block_shape->size<S32>();
1117 for (uint32_t e = 0; e < ele_count; ++e)
1118 {
1119 auto val = const_block_shape->at<S32>(e);
1120 if (val < 1)
1121 {
1122 INTERNAL_EXN_V("All values of block_shape >= 1: ", e);
1123 }
1124 }
1125
1126 loco::TensorShape shape_output;
1127
1128 shape_output.rank(input_shape.rank());
1129
1130 int32_t output_batch_size = input_shape.dim(0).value();
1131 for (int32_t dim = 0; dim < input_spatial_dim; ++dim)
1132 {
1133 int dim_size = input_shape.dim(dim + 1).value();
1134 dim_size += const_paddings->at<S32>(dim * 2);
1135 dim_size += const_paddings->at<S32>(dim * 2 + 1);
1136 shape_output.dim(dim + 1) = dim_size / const_block_shape->at<S32>(dim);
1137
1138 assert(dim_size % const_block_shape->at<S32>(dim) == 0);
1139 output_batch_size = output_batch_size * const_block_shape->at<S32>(dim);
1140 }
1141 shape_output.dim(0) = output_batch_size;
1142 shape_output.dim(input_shape.rank() - 1) = input_shape.dim(input_shape.rank() - 1);
1143
1144 return loco::NodeShape{shape_output};
1145}
1146
1147loco::NodeShape infer_space_to_depth(const luci::CircleSpaceToDepth *node)
1148{
1149 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1150 LUCI_ASSERT(input_shape.rank() == 4, "Only input rank 4 is supported");
1151
1152 // Only data format NHWC is supported
1153 int32_t height = input_shape.dim(1).value();
1154 int32_t width = input_shape.dim(2).value();
1155 int32_t depth = input_shape.dim(3).value();
1156
1157 int block_size = node->block_size();
1158
1159 if (block_size < 2)
1160 INTERNAL_EXN("Block size must be >= 2");
1161
1162 if ((height % block_size) || (width % block_size))
1163 {
1164 INTERNAL_EXN("The input tensor's height and width must be divisible by block_size");
1165 }
1166
1168 output_shape.rank(4);
1169
1170 output_shape.dim(0) = input_shape.dim(0).value();
1171 output_shape.dim(1) = height / block_size;
1172 output_shape.dim(2) = width / block_size;
1173 output_shape.dim(3) = block_size * block_size * depth;
1174
1176}
1177
1178loco::NodeShape infer_sparse_to_dense(const luci::CircleSparseToDense *node)
1179{
1180 loco::TensorShape shape;
1181 {
1182 LUCI_ASSERT(node->output_shape(), "dims input should not be nullptr");
1183
1184 auto output_shape_node = dynamic_cast<luci::CircleConst *>(node->output_shape());
1185 if (output_shape_node != nullptr)
1186 {
1187 const auto output_shape_type = output_shape_node->dtype();
1188
1189 if (output_shape_node->rank() != 1)
1190 INTERNAL_EXN_V("Only support rank 1 CircleConst",
1191 oops::to_uint32(output_shape_node->rank()));
1192
1193 if (output_shape_type == loco::DataType::S32)
1194 {
1195 shape.rank(output_shape_node->size<loco::DataType::S32>());
1196
1197 for (uint32_t axis = 0; axis < shape.rank(); ++axis)
1198 {
1199 shape.dim(axis) = output_shape_node->at<loco::DataType::S32>(axis);
1200 }
1201 }
1202 else if (output_shape_type == loco::DataType::S64)
1203 {
1204 shape.rank(output_shape_node->size<loco::DataType::S64>());
1205
1206 for (uint32_t axis = 0; axis < shape.rank(); ++axis)
1207 {
1208 shape.dim(axis) = output_shape_node->at<loco::DataType::S64>(axis);
1209 }
1210 }
1211 else
1212 {
1213 INTERNAL_EXN("Output shape of SparseToDense must be either int32 or int64");
1214 }
1215 }
1216 else
1217 {
1218 shape = own_shape(node);
1219 }
1220 }
1221
1222 return loco::NodeShape{shape};
1223}
1224
1225loco::NodeShape infer_squeeze(const luci::CircleSqueeze *node)
1226{
1227 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1228
1229 // TODO input shape may be unknown before runtime
1230 std::vector<bool> do_squeeze(input_shape.rank(), false);
1231 uint32_t num_squeezed = 0;
1232
1233 if (!node->squeeze_dims().empty())
1234 {
1235 // SqueezeDims not empty, squeeze only dims specified
1236 for (int32_t raw_dim : node->squeeze_dims())
1237 {
1238 int32_t dim = raw_dim < 0 ? raw_dim + input_shape.rank() : raw_dim;
1239
1240 if (dim < 0 || static_cast<uint32_t>(dim) >= input_shape.rank() ||
1241 input_shape.dim(dim).value() != 1)
1242 {
1243 INTERNAL_EXN("invalid dimention specified to Squeeze");
1244 }
1245
1246 if (!do_squeeze[dim])
1247 ++num_squeezed;
1248 do_squeeze[dim] = true;
1249 }
1250 }
1251 else
1252 {
1253 // SqueezeDims empty, squeeze any dims with size == 1
1254 for (uint32_t dim = 0; dim < input_shape.rank(); ++dim)
1255 {
1256 if (input_shape.dim(dim) == 1)
1257 {
1258 do_squeeze[dim] = true;
1259 ++num_squeezed;
1260 }
1261 }
1262 }
1263
1265 output_shape.rank(input_shape.rank() - num_squeezed);
1266
1267 for (uint32_t in_dim = 0, out_dim = 0; in_dim < input_shape.rank(); ++in_dim)
1268 {
1269 if (!do_squeeze[in_dim])
1270 {
1271 output_shape.dim(out_dim++) = input_shape.dim(in_dim);
1272 }
1273 }
1274
1276}
1277
1278loco::NodeShape infer_svdf(const luci::CircleSVDF *node)
1279{
1280 const auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1281 const auto weight_feature_shape = luci::shape_get(node->weight_feature()).as<loco::TensorShape>();
1282
1283 assert(ifm_shape.rank() == 2);
1284 assert(weight_feature_shape.rank() == 2);
1285
1286 assert(ifm_shape.dim(1) == weight_feature_shape.dim(1));
1287 assert(weight_feature_shape.dim(0).known());
1288
1289 const auto rank = node->svdf_rank();
1290 const auto num_filters = weight_feature_shape.dim(0).value();
1291 assert(num_filters % rank == 0);
1292 const auto num_units = num_filters / rank;
1293
1294 loco::TensorShape ofm_shape;
1295 ofm_shape.rank(2);
1296 ofm_shape.dim(0) = ifm_shape.dim(0);
1297 ofm_shape.dim(1) = num_units;
1298
1299 return loco::NodeShape{ofm_shape};
1300}
1301
1302loco::NodeShape infer_tile(const luci::CircleTile *node)
1303{
1304 const loco::DataType S32 = loco::DataType::S32;
1305
1306 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1307 auto multiples = loco::must_cast<luci::CircleConst *>(node->multiples());
1308
1309 // TODO support non-const case
1310 // TODO support S64 type
1311 LUCI_ASSERT(multiples->dtype() == S32, "Only support int32 multiples");
1312 LUCI_ASSERT(multiples->rank() == 1, "multiples should be rank 1")
1313
1314 uint32_t n = multiples->dim(0).value();
1315
1316 LUCI_ASSERT(n == input_shape.rank(), "length of multiples should be the same with input rank");
1317
1318 loco::TensorShape output_shape;
1319
1320 output_shape.rank(input_shape.rank());
1321 for (uint32_t ni = 0; ni < n; ++ni)
1322 {
1323 int32_t multiple = multiples->at<S32>(ni);
1324 output_shape.dim(ni) = input_shape.dim(ni).value() * static_cast<uint32_t>(multiple);
1325 }
1326
1328}
1329
1330loco::NodeShape infer_transpose(const luci::CircleTranspose *node)
1331{
1332 auto input_shape = luci::shape_get(node->a()).as<loco::TensorShape>();
1333
1334 auto perm_node = loco::must_cast<luci::CircleConst *>(node->perm());
1335
1337 output_shape.rank(input_shape.rank());
1338
1339 assert(perm_node->dtype() == loco::DataType::S32);
1340 assert(input_shape.rank() == perm_node->template size<loco::DataType::S32>());
1341
1342 for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++)
1343 {
1344 auto in_axis = perm_node->template at<loco::DataType::S32>(out_axis);
1345 output_shape.dim(out_axis) = input_shape.dim(in_axis);
1346 }
1347
1348 return output_shape;
1349}
1350
1351loco::NodeShape infer_transpose_conv(const luci::CircleTransposeConv *node)
1352{
1353 // TransposeConv's output shape is written in its 'inputSizes' argument
1354 auto input_sizes_const = dynamic_cast<luci::CircleConst *>(node->inputSizes());
1355 if (not input_sizes_const)
1356 return use_own(node);
1357 // TODO support non-const type
1358 LUCI_ASSERT(input_sizes_const->dtype() == loco::DataType::S32, "Only support S32 dtype")
1359 LUCI_ASSERT(input_sizes_const->rank() == 1 && input_sizes_const->dim(0).value() == 4,
1360 "Only support rank 1 with 4 entries")
1361
1362 loco::TensorShape shape;
1363
1364 shape.rank(4);
1365 for (uint32_t axis = 0; axis < 4; ++axis)
1366 shape.dim(axis) = input_sizes_const->at<loco::DataType::S32>(axis);
1367
1368 return loco::NodeShape{shape};
1369}
1370
1371loco::NodeShape infer_unpack(const luci::CircleUnpack *node)
1372{
1373 // CircleUnpack provides list(array) of Tensors which has one less dimension of the input
1374 // We'll set shape of CircleUnpack to shape of actual outputs
1375 // TODO fix this if any problem rises
1376 auto value_shape = luci::shape_get(node->value()).as<loco::TensorShape>();
1377
1378 auto axis = node->axis();
1379 auto num = node->num();
1380 auto rank = static_cast<int32_t>(value_shape.rank());
1381
1382 if (rank == 0)
1383 {
1384 // Unknown shape
1385 return use_own(node);
1386 }
1387
1388 LUCI_ASSERT(-rank <= axis && axis < rank, "Axis is out of range");
1389
1390 if (axis < 0)
1391 axis += rank;
1392
1393 LUCI_ASSERT(num == static_cast<int32_t>(value_shape.dim(axis).value()),
1394 "num, axis maybe incorrect");
1395
1397 output_shape.rank(rank - 1);
1398
1399 for (int32_t i = 0, o = 0; i < rank; ++i)
1400 {
1401 if (i != axis)
1402 output_shape.dim(o++) = value_shape.dim(i);
1403 }
1404
1406}
1407
1408loco::NodeShape infer_unidirectionalsequencelstm(const luci::CircleUnidirectionalSequenceLSTM *node)
1409{
1410 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1411 auto recurrent_to_output_weights =
1413 auto rank = input_shape.rank();
1415 output_shape.rank(rank);
1416 for (uint32_t i = 0; i < rank - 1; i++)
1417 {
1418 output_shape.dim(i) = input_shape.dim(i);
1419 }
1420 output_shape.dim(rank - 1) = recurrent_to_output_weights.dim(1);
1422}
1423
1424loco::NodeShape infer_unique(const luci::CircleUnique *node)
1425{
1426 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1427
1428 assert(input_shape.rank() == 1);
1429
1430 loco::TensorShape shape_output;
1431 shape_output = own_shape(node);
1432
1433 return loco::NodeShape{shape_output};
1434}
1435
1436// Circle Only
1437loco::NodeShape infer_bcq_fully_connected(const luci::CircleBCQFullyConnected *node)
1438{
1439 loco::TensorShape out_shape;
1440
1441 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1442 auto weights_clusters = loco::must_cast<luci::CircleConst *>(node->weights_clusters());
1443
1444 LUCI_ASSERT(input_shape.rank() == 2, "Input rank of BCQFullyConnected should be 2");
1445
1446 int32_t qbits_sum = 0;
1447 for (uint32_t i = 0; i < weights_clusters->dim(0).value(); ++i)
1448 {
1449 qbits_sum += weights_clusters->at<loco::DataType::S32>(i * 2 + 1);
1450 }
1451
1452 out_shape.rank(2);
1453 out_shape.dim(0) = qbits_sum;
1454 out_shape.dim(1) = input_shape.dim(1);
1455
1456 return loco::NodeShape{out_shape};
1457}
1458
1459loco::NodeShape infer_bcq_gather(const luci::CircleBCQGather *node)
1460{
1461 loco::TensorShape input_shape;
1463
1464 const auto input_binary_shape = luci::shape_get(node->input_binary()).as<loco::TensorShape>();
1465 const auto indices_shape = luci::shape_get(node->indices()).as<loco::TensorShape>();
1466 auto axis = node->axis();
1467
1468 auto input_clusters = loco::must_cast<luci::CircleConst *>(node->input_clusters());
1469 auto qbits_sum = 0;
1470 for (uint32_t i = 0; i < input_clusters->dim(0).value(); ++i)
1471 {
1472 qbits_sum += input_clusters->at<loco::DataType::S32>(i * 2 + 1);
1473 }
1474
1475 input_shape.rank(2);
1476 input_shape.dim(0) = qbits_sum;
1477 input_shape.dim(1) = input_binary_shape.dim(1).value() * 32;
1478
1479 output_shape.rank(input_shape.rank() - 1 + indices_shape.rank());
1480 int32_t outdim_index = 0;
1481 for (int32_t i = 0; i < axis; ++i)
1482 output_shape.dim(outdim_index++) = input_shape.dim(i);
1483 for (uint32_t i = 0; i < indices_shape.rank(); ++i)
1484 output_shape.dim(outdim_index++) = indices_shape.dim(i);
1485 for (uint32_t i = axis + 1; i < input_shape.rank(); ++i)
1486 output_shape.dim(outdim_index++) = input_shape.dim(i);
1487
1489}
1490
1491loco::NodeShape infer_circle_gru(const luci::CircleGRU *node)
1492{
1494
1495 const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1496 const auto state_shape = luci::shape_get(node->state()).as<loco::TensorShape>();
1497
1498 auto rank = input_shape.rank();
1499 assert(rank > 1);
1500 output_shape.rank(rank);
1501 for (uint32_t i = 0; i < rank - 1; i++)
1502 {
1503 output_shape.dim(i) = input_shape.dim(i);
1504 }
1505 output_shape.dim(rank - 1) = state_shape.dim(1);
1506
1507 if (not node->returnSequences())
1508 output_shape.dim(0) = 1;
1509
1511}
1512
1513// Virtual
1514loco::NodeShape infer_input(const luci::CircleInput *node)
1515{
1516 loco::TensorShape shape;
1517
1518 shape.rank(node->rank());
1519 for (uint32_t axis = 0; axis < node->rank(); axis++)
1520 shape.dim(axis) = node->dim(axis);
1521
1522 return loco::NodeShape{shape};
1523}
1524
1525loco::NodeShape infer_output(const luci::CircleOutput *node)
1526{
1527 auto graph_outputs = node->graph()->outputs();
1528 auto graph_output = graph_outputs->at(node->index());
1529 auto output_shape = graph_output->shape();
1530
1532}
1533
1534loco::NodeShape infer_non_max_suppression_v4_out(const luci::CircleNonMaxSuppressionV4Out *node)
1535{
1536 const loco::DataType S32 = loco::DataType::S32;
1537
1538 auto nmsv4 = dynamic_cast<const luci::CircleNonMaxSuppressionV4 *>(node->input());
1539 if (nmsv4 == nullptr)
1540 INTERNAL_EXN("CircleNonMaxSuppressionV4 IR is not configured correctly");
1541
1542 auto index = node->index();
1543 if (index == 1)
1544 return loco::TensorShape({0});
1545
1546 assert(index == 0);
1547
1548 auto unknown = loco::TensorShape{loco::Dimension()};
1549 auto max_output_size = dynamic_cast<const luci::CircleConst *>(nmsv4->max_output_size());
1550 if (max_output_size == nullptr)
1551 return unknown; // we need CircleConst for max output size
1552
1553 LUCI_ASSERT(max_output_size->dtype() == S32, "Only support int32 for max_output_size");
1554
1555 if (max_output_size->size<S32>() < 1)
1556 return unknown;
1557
1558 auto max_output_size_value = uint32_t(max_output_size->at<S32>(0));
1559 return loco::TensorShape{max_output_size_value};
1560}
1561
1562loco::NodeShape infer_non_max_suppression_v5_out(const luci::CircleNonMaxSuppressionV5Out *node)
1563{
1564 const loco::DataType S32 = loco::DataType::S32;
1565
1566 auto nmsv5 = dynamic_cast<const luci::CircleNonMaxSuppressionV5 *>(node->input());
1567 if (nmsv5 == nullptr)
1568 INTERNAL_EXN("CircleNonMaxSuppressionV5 IR is not configured correctly");
1569
1570 auto index = node->index();
1571 if (index == 2)
1572 return loco::TensorShape({0});
1573
1574 assert(index == 0 || index == 1);
1575
1576 auto unknown = loco::TensorShape{loco::Dimension()};
1577 auto max_output_size = dynamic_cast<const luci::CircleConst *>(nmsv5->max_output_size());
1578 if (max_output_size == nullptr)
1579 return unknown; // we need CircleConst for max output size
1580
1581 LUCI_ASSERT(max_output_size->dtype() == S32, "Only support int32 for max_output_size");
1582
1583 if (max_output_size->size<S32>() < 1)
1584 return unknown;
1585
1586 auto max_output_size_value = uint32_t(max_output_size->at<S32>(0));
1587 return loco::TensorShape{max_output_size_value};
1588}
1589
1590loco::NodeShape infer_split_out(const luci::CircleSplitOut *node)
1591{
1592 const loco::DataType S32 = loco::DataType::S32;
1593
1594 auto split = dynamic_cast<const luci::CircleSplit *>(node->input());
1595 if (split == nullptr)
1596 INTERNAL_EXN("CircleSplit IR is not configured correctly");
1597
1598 loco::NodeShape unknown;
1599
1600 auto split_shape = luci::shape_get(split).as<loco::TensorShape>();
1601
1602 auto split_dim = dynamic_cast<const luci::CircleConst *>(split->split_dim());
1603 if (split_dim == nullptr)
1604 return unknown; // we need CircleConst for split_dim
1605 LUCI_ASSERT(split_dim->dtype() == S32, "Only support int32 for split_dim");
1606
1607 assert(split_dim->size<S32>() == 1);
1608 auto split_dim_axis = split_dim->at<S32>(0);
1609 if (split_dim_axis < 0)
1610 split_dim_axis += split_shape.rank();
1611
1612 auto split_dim_value = split_shape.dim(split_dim_axis).value();
1613 assert(split_dim_value % split->num_split() == 0);
1614 const int split_depth = split_dim_value / split->num_split();
1615
1616 loco::TensorShape output_shape = split_shape;
1617
1618 // All shapes are equally same
1619 output_shape.dim(split_dim_axis) = loco::Dimension(split_depth);
1620
1622}
1623
1624loco::NodeShape infer_split_v_out(const luci::CircleSplitVOut *node)
1625{
1626 const loco::DataType S32 = loco::DataType::S32;
1627
1628 auto split = dynamic_cast<const luci::CircleSplitV *>(node->input());
1629 if (split == nullptr)
1630 INTERNAL_EXN("CircleSplit IR is not configured correctly");
1631
1632 loco::NodeShape unknown;
1633
1634 auto split_shape = luci::shape_get(split).as<loco::TensorShape>();
1635
1636 auto size_splits = dynamic_cast<const luci::CircleConst *>(split->size_splits());
1637 if (size_splits == nullptr)
1638 return unknown; // we need CircleConst for size_splits
1639 LUCI_ASSERT(size_splits->dtype() == S32, "Only support int32 for size_splits");
1640
1641 auto split_dim = dynamic_cast<const luci::CircleConst *>(split->split_dim());
1642 if (split_dim == nullptr)
1643 return unknown; // we need CircleConst for split_dim
1644 LUCI_ASSERT(split_dim->dtype() == S32, "Only support int32 for split_dim");
1645
1646 // fetch axis
1647 assert(split_dim->size<S32>() == 1);
1648 auto split_dim_axis = split_dim->at<S32>(0);
1649 if (split_dim_axis < 0)
1650 split_dim_axis += split_shape.rank();
1651
1652 // interpret size_splits values
1653 int32_t size_splits_count = static_cast<int32_t>(size_splits->size<S32>());
1654 assert(size_splits_count == split->num_split());
1655
1656 int64_t minus_one_count = 0, size_splits_sum = 0;
1657 for (int32_t idx = 0; idx < size_splits_count; ++idx)
1658 {
1659 auto size = size_splits->at<S32>(idx);
1660 assert(size >= -1);
1661 if (size == -1)
1662 ++minus_one_count;
1663 else
1664 size_splits_sum += size;
1665 }
1666 if (minus_one_count > 1)
1667 INTERNAL_EXN("CircleSplitV size_splits has more than two -1 values");
1668
1669 // calcuate this SplitVOut shape
1670 auto input_size = split_shape.dim(split_dim_axis).value();
1671 assert(size_splits_sum <= input_size);
1672
1673 auto index_this = node->index();
1674 assert(0 <= index_this && index_this < split->num_split());
1675 auto split_depth = size_splits->at<S32>(index_this);
1676 if (split_depth == -1)
1677 split_depth = static_cast<int32_t>(input_size) - static_cast<int32_t>(size_splits_sum);
1678
1679 loco::TensorShape output_shape = split_shape;
1680
1681 output_shape.dim(split_dim_axis) = loco::Dimension(split_depth);
1682
1684}
1685
1686loco::NodeShape infer_top_k_v2_out(const luci::CircleTopKV2Out *node)
1687{
1688 const loco::DataType S32 = loco::DataType::S32;
1689
1690 auto topkv2 = dynamic_cast<const luci::CircleTopKV2 *>(node->input());
1691 if (topkv2 == nullptr)
1692 INTERNAL_EXN("CircleSplit IR is not configured correctly");
1693
1694 // shape of topkv2 is same as topkv2->input()
1695 auto input_shape = luci::shape_get(topkv2).as<loco::TensorShape>();
1696
1697 auto node_k = loco::must_cast<const luci::CircleConst *>(topkv2->k());
1698 LUCI_ASSERT(node_k->dtype() == S32, "Only support Int32");
1699 assert(node_k->size<S32>() == 1);
1700
1702
1703 output_shape.rank(input_shape.rank());
1704 for (uint32_t idx = 0; idx < input_shape.rank() - 1; ++idx)
1705 {
1706 output_shape.dim(idx) = input_shape.dim(idx);
1707 }
1708 output_shape.dim(input_shape.rank() - 1) = node_k->at<S32>(0);
1709
1711}
1712
1713loco::NodeShape infer_unique_out(const luci::CircleUniqueOut *node)
1714{
1715 if (node->index() == 0)
1716 {
1717 auto unique_shape = own_shape(node);
1718 return loco::NodeShape{unique_shape};
1719 }
1720 assert(node->index() == 1);
1721 auto unique = loco::must_cast<luci::CircleUnique *>(node->input());
1722 auto unique_shape = luci::shape_get(unique->input()).as<loco::TensorShape>();
1723
1724 assert(unique_shape.rank() == 1);
1725
1726 loco::TensorShape shape_output;
1727 shape_output.rank(1);
1728 shape_output.dim(0) = unique_shape.dim(0);
1729 return loco::NodeShape{shape_output};
1730}
1731
1732loco::NodeShape infer_unpack_out(const luci::CircleUnpackOut *node)
1733{
1734 auto unpack = dynamic_cast<const luci::CircleUnpack *>(node->input());
1735 if (unpack == nullptr)
1736 {
1737 INTERNAL_EXN("CircleUnpack IR is not configured correctly");
1738 }
1739
1740 auto unpack_shape = luci::shape_get(unpack).as<loco::TensorShape>();
1741
1742 return loco::NodeShape{unpack_shape};
1743}
1744
1745loco::NodeShape infer_while_out(const luci::CircleWhileOut *node)
1746{
1751 auto circle_while = dynamic_cast<const luci::CircleWhile *>(node->input());
1752 if (circle_while == nullptr)
1753 {
1754 INTERNAL_EXN("CircleWhile IR is not configured correctly");
1755 }
1756
1757 auto index = node->index();
1758 auto cond_graph = circle_while->cond_graph();
1759 assert(cond_graph != nullptr);
1760
1761 // Assumption: the index of CircleWhileOut matches with the index of input nodes returned by
1762 // loco::input_nodes
1763 auto cond_inputs = loco::input_nodes(cond_graph);
1764 auto cond_in = loco::must_cast<luci::CircleInput *>(cond_inputs.at(index));
1765
1766 auto cond_graph_inputs = cond_graph->inputs();
1767 auto cond_graph_input = cond_graph_inputs->at(cond_in->index());
1768
1769 const auto &cond_graph_input_shape = *cond_graph_input->shape();
1770 auto this_shape = own_shape(node);
1771
1772 if (!(this_shape == cond_graph_input_shape))
1773 {
1774 LOGGER(l);
1775 WARN(l) << "Warning: CircleWhileOut '" << node->name() << "' shape mispatch " << this_shape
1776 << " vs " << cond_graph_input_shape;
1777 }
1778
1779 return loco::NodeShape{this_shape};
1780}
1781
1787class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeShape>
1788{
1789public:
1790 loco::NodeShape visit(const luci::CircleAbs *node) final { return use_x(node); }
1791
1792 loco::NodeShape visit(const luci::CircleAddN *node) final { return infer_add_n(node); }
1793
1794 loco::NodeShape visit(const luci::CircleArgMax *node) final { return infer_arg_maxmin(node); }
1795
1796 loco::NodeShape visit(const luci::CircleArgMin *node) final { return infer_arg_maxmin(node); }
1797
1799 {
1800 return infer_pool_2d_shape(node);
1801 }
1802
1804 {
1805 return infer_batch_to_space_nd(node);
1806 }
1807
1809 {
1810 return infer_broadcast_to(node);
1811 }
1812
1813 loco::NodeShape visit(const luci::CircleCast *node) final { return use_x(node); }
1814
1815 loco::NodeShape visit(const luci::CircleCeil *node) final { return use_x(node); }
1816
1817 loco::NodeShape visit(const luci::CircleConst *node) final { return use_own(node); }
1818
1819 loco::NodeShape visit(const luci::CircleConv2D *node) final { return infer_conv2d(node); }
1820
1821 loco::NodeShape visit(const luci::CircleCos *node) final { return use_x(node); }
1822
1823 loco::NodeShape visit(const luci::CircleCumSum *node) final { return use_input(node); }
1824
1825 loco::NodeShape visit(const luci::CircleCustom *node) final { return use_own(node); }
1826
1827 loco::NodeShape visit(const luci::CircleDensify *node) final { return use_input(node); }
1828
1830 {
1831 return infer_depth_to_space(node);
1832 }
1833
1835 {
1836 return infer_depthwise_conv2d(node);
1837 }
1838
1840 {
1841 const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1842 return loco::NodeShape{input_shape};
1843 }
1844
1845 loco::NodeShape visit(const luci::CircleElu *node) final
1846 {
1847 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
1848
1849 return loco::NodeShape{input_shape};
1850 }
1851
1852 loco::NodeShape visit(const luci::CircleEqual *node) final { return broadcast_xy(node); }
1853
1854 loco::NodeShape visit(const luci::CircleExp *node) final { return use_x(node); }
1855
1857 {
1858 return infer_expand_dims(node);
1859 }
1860
1861 loco::NodeShape visit(const luci::CircleFakeQuant *node) final { return use_inputs(node); }
1862
1863 loco::NodeShape visit(const luci::CircleFill *node) final { return infer_fill(node); }
1864
1865 loco::NodeShape visit(const luci::CircleFloor *node) final { return use_x(node); }
1866
1867 loco::NodeShape visit(const luci::CircleFloorDiv *node) final { return broadcast_xy(node); }
1868
1869 loco::NodeShape visit(const luci::CircleFloorMod *node) final { return broadcast_xy(node); }
1870
1871 loco::NodeShape visit(const luci::CircleGather *node) final { return infer_gather(node); }
1872
1873 loco::NodeShape visit(const luci::CircleGatherNd *node) final { return infer_gather_nd(node); }
1874
1875 loco::NodeShape visit(const luci::CircleGelu *node) final
1876 {
1877 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
1878
1879 return loco::NodeShape{input_shape};
1880 }
1881
1882 loco::NodeShape visit(const luci::CircleGreater *node) final { return broadcast_xy(node); }
1883
1884 loco::NodeShape visit(const luci::CircleGreaterEqual *node) final { return broadcast_xy(node); }
1885
1887 {
1888 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
1889
1890 return loco::NodeShape{input_shape};
1891 }
1892
1893 loco::NodeShape visit(const luci::CircleIf *node) final
1894 {
1895 // Shape of CircleIf is not used. Just use input 0
1896 assert(node->input_count() > 0);
1897 const auto input_shape = luci::shape_get(node->input(0)).as<loco::TensorShape>();
1898 return loco::NodeShape{input_shape};
1899 }
1900
1901 loco::NodeShape visit(const luci::CircleL2Normalize *node) final { return use_x(node); }
1902
1903 loco::NodeShape visit(const luci::CircleL2Pool2D *node) final
1904 {
1905 return infer_pool_2d_shape(node);
1906 }
1907
1909 {
1910 const auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
1911 return loco::NodeShape{input_shape};
1912 }
1913
1914 loco::NodeShape visit(const luci::CircleLess *node) final { return broadcast_xy(node); }
1915
1916 loco::NodeShape visit(const luci::CircleLessEqual *node) final { return broadcast_xy(node); }
1917
1919 {
1920 const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
1921 return loco::NodeShape{input_shape};
1922 }
1923
1924 loco::NodeShape visit(const luci::CircleLog *node) final { return use_x(node); }
1925
1926 loco::NodeShape visit(const luci::CircleLogicalAnd *node) final { return use_x(node); }
1927
1928 loco::NodeShape visit(const luci::CircleLogicalNot *node) final { return use_x(node); }
1929
1930 loco::NodeShape visit(const luci::CircleLogicalOr *node) final { return use_x(node); }
1931
1932 loco::NodeShape visit(const luci::CircleLogSoftmax *node) final { return use_logits(node); }
1933
1935 {
1936 return infer_matrix_diag(node);
1937 }
1938
1940 {
1941 return infer_matrix_set_diag(node);
1942 }
1943
1944 loco::NodeShape visit(const luci::CircleMaximum *node) final { return broadcast_xy(node); }
1945
1947 {
1948 return infer_pool_2d_shape(node);
1949 }
1950
1951 loco::NodeShape visit(const luci::CircleMean *node) final
1952 {
1953 auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
1955 }
1956
1957 loco::NodeShape visit(const luci::CircleMinimum *node) final { return broadcast_xy(node); }
1958
1959 loco::NodeShape visit(const luci::CircleMirrorPad *node) final { return infer_mirror_pad(node); }
1960
1962 {
1963 const auto boxes_shape = luci::shape_get(node->boxes()).as<loco::TensorShape>();
1964 return loco::NodeShape{boxes_shape};
1965 }
1966
1968 {
1969 const auto boxes_shape = luci::shape_get(node->boxes()).as<loco::TensorShape>();
1970 return loco::NodeShape{boxes_shape};
1971 }
1972
1973 loco::NodeShape visit(const luci::CircleNotEqual *node) final { return broadcast_xy(node); }
1974
1975 loco::NodeShape visit(const luci::CircleOneHot *node) final { return infer_one_hot(node); }
1976
1977 loco::NodeShape visit(const luci::CirclePack *node) final { return infer_pack(node); }
1978
1979 loco::NodeShape visit(const luci::CirclePadV2 *node) final { return infer_pad_v2(node); }
1980
1981 loco::NodeShape visit(const luci::CirclePow *node) final { return broadcast_xy(node); }
1982
1983 loco::NodeShape visit(const luci::CirclePRelu *node) final { return infer_p_relu(node); }
1984
1986 {
1987 loco::TensorShape shape_output;
1988 shape_output.rank(0);
1989
1990 return loco::NodeShape{shape_output};
1991 }
1992
1994 {
1995 auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
1997 }
1998
2000 {
2001 auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
2003 }
2004
2006 {
2007 auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
2009 }
2010
2012 {
2013 auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
2015 }
2016
2017 loco::NodeShape visit(const luci::CircleRelu *node) final
2018 {
2019 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
2020
2021 return loco::NodeShape{input_shape};
2022 }
2023
2024 loco::NodeShape visit(const luci::CircleRelu0To1 *node) final
2025 {
2026 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
2027
2028 return loco::NodeShape{input_shape};
2029 }
2030
2031 loco::NodeShape visit(const luci::CircleRelu6 *node) final
2032 {
2033 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
2034
2035 return loco::NodeShape{input_shape};
2036 }
2037
2039 {
2040 auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>();
2041
2042 return loco::NodeShape{input_shape};
2043 }
2044
2046 {
2047 return infer_resize_type(node);
2048 }
2049
2051 {
2052 return infer_resize_type(node);
2053 }
2054
2056 {
2057 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2058
2059 return loco::NodeShape{input_shape};
2060 }
2061
2062 loco::NodeShape visit(const luci::CircleRound *node) final { return use_x(node); }
2063
2065 {
2066 auto input_shape = luci::shape_get(node->tensor()).as<loco::TensorShape>();
2067
2068 LUCI_ASSERT(luci::shape_get(node->axis()).as<loco::TensorShape>().rank() == 1,
2069 "Tensor must be 1-D");
2070
2071 return loco::NodeShape{input_shape};
2072 }
2073
2074 loco::NodeShape visit(const luci::CircleScatterNd *node) final { return infer_scatter_nd(node); }
2075
2077 {
2078 return infer_segment_sum(node);
2079 }
2080
2081 loco::NodeShape visit(const luci::CircleSelect *node) final { return infer_select(node); }
2082
2083 loco::NodeShape visit(const luci::CircleSelectV2 *node) final { return infer_select_v2(node); }
2084
2085 loco::NodeShape visit(const luci::CircleShape *node) final { return infer_shape(node); }
2086
2087 loco::NodeShape visit(const luci::CircleSin *node) final { return use_x(node); }
2088
2089 loco::NodeShape visit(const luci::CircleSlice *node) final { return infer_slice(node); }
2090
2092 {
2093 return infer_space_to_batch_nd(node);
2094 }
2095
2097 {
2098 return infer_space_to_depth(node);
2099 }
2100
2102 {
2103 return infer_sparse_to_dense(node);
2104 }
2105
2106 loco::NodeShape visit(const luci::CircleSplit *node) final
2107 {
2108 // We'll set Split output as same as input so that SplitOut can handle it's own shape
2109 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2110 return loco::NodeShape{input_shape};
2111 }
2112
2113 loco::NodeShape visit(const luci::CircleSplitV *node) final
2114 {
2115 // We'll set SplitV output as same as input so that SplitOut can handle it's own shape
2116 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2117 return loco::NodeShape{input_shape};
2118 }
2119
2120 loco::NodeShape visit(const luci::CircleSqrt *node) final { return use_x(node); }
2121
2122 loco::NodeShape visit(const luci::CircleSquare *node) final { return use_x(node); }
2123
2125 {
2126 return broadcast_xy(node);
2127 }
2128
2129 loco::NodeShape visit(const luci::CircleSqueeze *node) final { return infer_squeeze(node); }
2130
2131 loco::NodeShape visit(const luci::CircleSub *node) final { return broadcast_xy(node); }
2132
2133 loco::NodeShape visit(const luci::CircleSum *node) final
2134 {
2135 auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
2137 }
2138
2139 loco::NodeShape visit(const luci::CircleSVDF *node) final { return infer_svdf(node); }
2140
2141 loco::NodeShape visit(const luci::CircleTanh *node) final { return use_x(node); }
2142
2143 loco::NodeShape visit(const luci::CircleTile *node) final { return infer_tile(node); }
2144
2145 loco::NodeShape visit(const luci::CircleTopKV2 *node) final
2146 {
2147 // set shape of this node as same as input
2148 const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2149 return loco::NodeShape{input_shape};
2150 }
2151
2152 loco::NodeShape visit(const luci::CircleTranspose *node) final { return infer_transpose(node); }
2153
2155 {
2156 return infer_transpose_conv(node);
2157 }
2158
2159 loco::NodeShape visit(const luci::CircleUnpack *node) final { return infer_unpack(node); }
2160
2162 {
2163 return infer_unidirectionalsequencelstm(node);
2164 }
2165
2166 loco::NodeShape visit(const luci::CircleUnique *node) final { return infer_unique(node); }
2167
2168 loco::NodeShape visit(const luci::CircleWhere *node) final { return use_own(node); }
2169
2170 loco::NodeShape visit(const luci::CircleWhile *node) final
2171 {
2172 // Shape of CircleWhile is not used. Just use input 0
2173 assert(node->arity() > 0);
2174 const auto input_shape = luci::shape_get(node->input(0)).as<loco::TensorShape>();
2175 return loco::NodeShape{input_shape};
2176 }
2177
2179 {
2180 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2181
2182 return loco::NodeShape{input_shape};
2183 }
2184
2185 // Circle Only
2187 {
2188 return infer_bcq_fully_connected(node);
2189 }
2190
2191 loco::NodeShape visit(const luci::CircleBCQGather *node) final { return infer_bcq_gather(node); }
2192
2194 {
2195 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2196
2197 return loco::NodeShape{input_shape};
2198 }
2199
2200 loco::NodeShape visit(const luci::CircleGRU *node) final { return infer_circle_gru(node); }
2201
2202 loco::NodeShape visit(const luci::CircleRmsNorm *node) final
2203 {
2204 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2205
2206 return loco::NodeShape{input_shape};
2207 }
2208
2209 loco::NodeShape visit(const luci::CircleRoPE *node) final
2210 {
2211 auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
2212
2213 return loco::NodeShape{input_shape};
2214 }
2215
2216 // Virtual
2217 loco::NodeShape visit(const luci::CircleInput *node) final { return infer_input(node); }
2218
2219 loco::NodeShape visit(const luci::CircleOutput *node) final { return infer_output(node); }
2220
2221 loco::NodeShape visit(const luci::CircleOutputDummy *node) final { return use_own(node); }
2222
2223 loco::NodeShape visit(const luci::CircleOutputExclude *node) final { return use_own(node); }
2224
2225 loco::NodeShape visit(const luci::CircleCustomOut *node) final { return use_own(node); }
2226
2228 {
2229 return infer_non_max_suppression_v4_out(node);
2230 }
2231
2233 {
2234 return infer_non_max_suppression_v5_out(node);
2235 }
2236
2237 loco::NodeShape visit(const luci::CircleSplitOut *node) final { return infer_split_out(node); }
2238
2239 loco::NodeShape visit(const luci::CircleSplitVOut *node) final { return infer_split_v_out(node); }
2240
2242 {
2243 return infer_top_k_v2_out(node);
2244 }
2245
2246 loco::NodeShape visit(const luci::CircleUniqueOut *node) final { return infer_unique_out(node); }
2247
2248 loco::NodeShape visit(const luci::CircleUnpackOut *node) final { return infer_unpack_out(node); }
2249
2250 loco::NodeShape visit(const luci::CircleVariable *node) final { return use_own(node); }
2251
2252 loco::NodeShape visit(const luci::CircleWhileOut *node) final { return infer_while_out(node); }
2253};
2254
2255} // namespace
2256
2257namespace luci
2258{
2259
2261{
2262 return CircleDialect::get() == d;
2263}
2264
2266{
2267 LOGGER(l);
2268
2269 assert(node->dialect() == CircleDialect::get());
2270
2271 ShapeInferenceAlgorithm alg;
2272 auto circle_node = loco::must_cast<const CircleNode *>(node);
2273
2274 bool is_shape_undefined = (circle_node->shape_status() == ShapeStatus::UNDEFINED);
2275 bool is_shape_none = (circle_node->shape_status() == ShapeStatus::NOSHAPE);
2276 bool is_scalar = (circle_node->rank() == 0);
2277
2278 if (is_shape_undefined)
2279 shape = circle_node->accept(&alg);
2280 else
2281 {
2282 if (is_shape_none || is_scalar)
2283 shape = own_shape(circle_node);
2284 else
2285 shape = circle_node->accept(&alg);
2286 }
2287
2288 VERBOSE(l, 1) << "[luci] shape: " << circle_node->name();
2289 VERBOSE(l, 1) << " own_shape: " << own_shape(circle_node)
2290 << " -> infer: " << shape.as<loco::TensorShape>();
2291
2292 return true;
2293}
2294
2295} // namespace luci
#define INTERNAL_EXN(msg)
@ brief throw internal exception with message
Definition InternalExn.h:25
#define INTERNAL_EXN_V(msg, val)
@ brief throw internal exception with message and value
Definition InternalExn.h:28
#define LOGGER(name)
Definition Log.h:65
#define INFO(name)
Definition Log.h:68
std::ostream & operator<<(std::ostream &os, const circledump::ModelEx &model)
Definition Dump.cpp:467
Dialect interface.
Definition Dialect.h:37
The value of one dimension in a tensor shape.
Definition Dimension.h:30
uint32_t value(void) const
Return the value.
Definition Dimension.h:51
bool known(void) const
Return whether the value is known (or not)
Definition Dimension.h:47
OutputContext * outputs(void)
Definition Graph.h:222
Logical unit of computation.
Definition Node.h:54
Graph * graph(void)
Definition Node.h:70
virtual const Dialect * dialect(void) const =0
Return "Dialect" identifier that this node belongs to.
ShapeType as(void) const
T * at(uint32_t n) const
Access N-th object.
Definition ObjectPool.h:41
const Dimension & dim(uint32_t axis) const
Definition TensorShape.h:38
uint32_t rank(void) const
Definition TensorShape.h:35
ABS in Circle.
Definition CircleAbs.h:32
ADD_N in Circle.
Definition CircleAddN.h:32
Node * inputs(uint32_t index) const
Definition CircleAddN.h:40
ARG_MAX in Circle.
ARG_Min in Circle.
AVERAGE_POOL_2D in Circle.
BCQ_FULLY_CONNECTED in Circle.
loco::Node * weights_clusters(void) const
BCQ_GATHER in Circle.
loco::Node * input_binary(void) const
loco::Node * input_clusters(void) const
int32_t axis(void) const
loco::Node * indices(void) const
BATCH_TO_SPACE_ND in Circle.
loco::Node * input(void) const
loco::Node * crops(void) const
loco::Node * block_shape(void) const
BroadcastTo in Circle.
loco::Node * shape(void) const
CAST in Circle.
Definition CircleCast.h:32
CEIL in Circle.
Definition CircleCeil.h:32
Class to build tensor data.
Definition CircleConst.h:35
const loco::DataTypeImpl< DT >::Type & at(uint32_t n) const
uint32_t size(void) const
CONV_2D in Circle.
loco::Node * filter(void) const
loco::Node * input(void) const
COS in Circle.
Definition CircleCos.h:32
CUSTOM in Circle.
Virtual CIRCLECUSTOMOUT in Circle.
DENSIFY in Circle.
DEPTH_TO_SPACE in Circle.
int32_t block_size(void) const
loco::Node * input(void) const
DEPTHWISE_CONV_2D in Circle.
loco::Node * filter(void) const
loco::Node * input(void) const
DEQUANTIZE in Circle.
static loco::Dialect * get(void)
ELU in Circle.
Definition CircleElu.h:32
EQUAL in Circle.
Definition CircleEqual.h:32
EXP in Circle.
Definition CircleExp.h:32
EXPAND_DIMS in Circle.
loco::Node * axis(void) const
loco::Node * input(void) const
FAKE_QUANT in Circle.
FILL in Circle.
Definition CircleFill.h:32
loco::Node * dims(void) const
Definition CircleFill.h:34
FLOOR_DIV in Circle.
FLOOR in Circle.
Definition CircleFloor.h:32
FLOOR_MOD in Circle.
GRU in Circle.
Definition CircleGRU.h:32
loco::Node * input(void) const
Definition CircleGRU.h:34
loco::Node * state(void) const
Definition CircleGRU.h:49
bool returnSequences() const
Definition CircleGRU.h:56
GATHER in Circle.
int32_t axis(void) const
loco::Node * params(void) const
loco::Node * indices(void) const
GATHER_ND in Circle.
loco::Node * params(void) const
loco::Node * indices(void) const
GELU in Circle.
Definition CircleGelu.h:32
GREATER EQUAL in Circle.
Greater in Circle.
HardSwish in Circle.
IF in Circle.
Definition CircleIf.h:34
CircleNode used for Input of the Graph.
Definition CircleInput.h:36
INSTANCE_NORM in Circle.
L2_NORMALIZATION in Circle.
L2_POOL_2D in Circle.
LEAKY_RELU in Circle.
LESS_EQUAL in Circle.
LESS in Circle.
Definition CircleLess.h:32
LOCAL_RESPONSE_NORMALIZATION in Circle.
LOG in Circle.
Definition CircleLog.h:32
LOG_SOFTMAX in Circle.
LOGICAL_AND in Circle.
LOGICAL_NOT in Circle.
LOGICAL_OR in Circle.
MATRIX_DIAG in Circle.
loco::Node * diagonal(void) const
MATRIX_SET_DIAG in Circle.
loco::Node * diagonal(void) const
loco::Node * input(void) const
MAX_POOL_2D in Circle.
MAXIMUM in Circle.
MEAN in Circle.
Definition CircleMean.h:32
MINIMUM in Circle.
MIRROR_PAD in Circle.
loco::Node * paddings(void) const
NON_MAX_SUPPRESSION_V4 in Circle.
Virtual NONMAXSUPPRESSIONV4OUT in Circle.
NON_MAX_SUPPRESSION_V5 in Circle.
Virtual NONMAXSUPPRESSIONV5OUT in Circle.
NOT EQUAL in Circle.
ONEHOT in Circle.
loco::Node * depth(void) const
loco::Node * indices(void) const
int32_t axis(void) const
Temporary DummyNode used with dangle CircleNode.
CircleOutputExclude is used to specifying not exported nodes.
CircleNode for Output of the Graph.
void index(const loco::GraphOutputIndex &index)
PRelu in Circle.
Definition CirclePRelu.h:32
loco::Node * input(void) const
Definition CirclePRelu.h:34
loco::Node * alpha(void) const
Definition CirclePRelu.h:37
PACK in Circle.
Definition CirclePack.h:34
Node * values(uint32_t index) const
Definition CirclePack.h:46
int32_t axis(void) const
Definition CirclePack.h:58
uint32_t values_count(void) const
Definition CirclePack.h:43
PADV2 in Circle.
Definition CirclePadV2.h:32
loco::Node * paddings(void) const
Definition CirclePadV2.h:37
POW in Circle.
Definition CirclePow.h:32
RANK in Circle.
Definition CircleRank.h:32
REDUCE_ANY in Circle.
REDUCE_MAX in Circle.
REDUCE_MIN in Circle.
REDUCE_PROD in Circle.
RELU_0_TO_1 in Circle.
RELU6 in Circle.
Definition CircleRelu6.h:32
RELU in Circle.
Definition CircleRelu.h:32
RELU_N1_TO_1 in Circle.
RESIZE_BILINEAR in Circle.
RESIZE_NEAREST_NEIGHBOR in Circle.
REVERSE_SEQUENCE in Circle.
ReverseV2 in Circle.
RMS_NORM in Circle.
ROPE in Circle.
Definition CircleRoPE.h:33
ROUND in Circle.
Definition CircleRound.h:32
SVDF in Circle.
Definition CircleSVDF.h:33
loco::Node * input(void) const
Definition CircleSVDF.h:38
loco::Node * weight_feature(void) const
Definition CircleSVDF.h:41
int32_t svdf_rank() const
Definition CircleSVDF.h:60
SCATTER_ND in Circle.
loco::Node * shape(void) const
SEGMENT_SUM in Circle.
loco::Node * segment_ids(void) const
loco::Node * input(void) const
SELECT in Circle.
loco::Node * e(void) const
loco::Node * condition(void) const
loco::Node * t(void) const
SELECT_V2 in Circle.
loco::Node * e(void) const
loco::Node * t(void) const
loco::Node * condition(void) const
SHAPE in Circle.
Definition CircleShape.h:32
loco::Node * input(void) const
Definition CircleShape.h:34
SIN in Circle.
Definition CircleSin.h:32
SLICE in Circle.
Definition CircleSlice.h:32
loco::Node * size(void) const
Definition CircleSlice.h:40
loco::Node * input(void) const
Definition CircleSlice.h:34
loco::Node * begin(void) const
Definition CircleSlice.h:37
SPACE_TO_BATCH_ND in Circle.
loco::Node * block_shape(void) const
loco::Node * input(void) const
loco::Node * paddings(void) const
SPACE_TO_DEPTH in Circle.
int32_t block_size(void) const
loco::Node * input(void) const
SPARSE_TO_DENSE in Circle.
loco::Node * output_shape(void) const
SPLIT in Circle.
Definition CircleSplit.h:32
Virtual CIRCLESPLITOUT in Circle.
loco::Node * input(void) const
SPLIT_V in Circle.
Virtual CIRCLESPLITVOUT in Circle.
int32_t index(void) const
loco::Node * input(void) const
SQRT in Circle.
Definition CircleSqrt.h:32
SQUARE in Circle.
SQUARED_DIFFERENCE in Circle.
SQUEEZE in Circle.
loco::Node * input(void) const
const std::vector< int32_t > & squeeze_dims() const
SUB in Circle.
Definition CircleSub.h:34
SUM in Circle.
Definition CircleSum.h:32
TANH in Circle.
Definition CircleTanh.h:32
TILE in Circle.
Definition CircleTile.h:32
loco::Node * input(void) const
Definition CircleTile.h:34
loco::Node * multiples(void) const
Definition CircleTile.h:37
TOPK_V2 in Circle.
Virtual CIRCLETOPKV2OUT in Circle.
loco::Node * input(void) const
TRANSPOSE_CONV in Circle.
loco::Node * inputSizes(void) const
TRANSPOSE in Circle.
loco::Node * a(void) const
loco::Node * perm(void) const
UNIDIRECTIONAL_SEQUENCE_LSTM in Circle.
Unique in Circle.
loco::Node * input(void) const
Virtual CIRCLEUNIQUEOUT in Circle.
int32_t index(void) const
loco::Node * input(void) const
UNPACK in Circle.
int32_t axis(void) const
int32_t num(void) const
loco::Node * value(void) const
Virtual CIRCLEUNPACKOUT in Circle.
loco::Node * input(void) const
Virtual CircleVariable in Circle for 'variable' Tensor.
WHERE in Circle.
Definition CircleWhere.h:34
WHILE in Circle.
Definition CircleWhile.h:34
Virtual CIRCLEWHILEOUT in Circle.
loco::Node * input(void) const
int32_t index(void) const
ZEROS_LIKE in Circle.
uint32_t arity(void) const final
uint32_t arity(void) const final
const luci_interpreter::RuntimeShape output_shape
#define LUCI_ASSERT(condition, msg)
Definition Check.h:26
#define WARN(name)
Definition Log.h:70
#define VERBOSE(name, lv)
Definition Log.h:71
#define DECLARE_USE_SINGLE(NAME)
result
Definition infer.py:103
std::vector< Node * > input_nodes(const Graph *)
Definition Graph.cpp:71
DataType
"scalar" value type
Definition DataType.h:27
const loco::DataType S32
loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
loco::NodeShape shape_get(const loco::Node *node)
loco::NodeShape node_shape(const loco::Node *node)
loco::GraphInputIndex index(const TFPlaceholder *node)
Definition TFNode.cpp:54
std::vector< std::string > split(const std::string &s, char delim)
uint32_t to_uint32(T a)
Definition InternalExn.h:33
int32_t size[5]
Definition Slice.cpp:35
NodeName name(void) const
uint32_t opnum(void) const final
virtual T visit(const CircleNode *)
Default fallback.
bool recognize(const loco::Dialect *) const final
Return true if this rule recognizes a given dialect.
bool infer(const loco::Node *, loco::NodeShape &) const final
Infer node's shape.