ONE - On-device Neural Engine
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
luci::sinf::Algorithm Class Referencefinal

#include <CircleShapeInference.h>

Collaboration diagram for luci::sinf::Algorithm:

Public Member Functions

loco::TensorShape visit (const luci::CircleNode *node) final
 Default fallback.
 
loco::TensorShape visit (const luci::CircleAdd *node) final
 
loco::TensorShape visit (const luci::CircleBatchMatMul *node) final
 
loco::TensorShape visit (const luci::CircleConcatenation *node) final
 
loco::TensorShape visit (const luci::CircleDiv *node) final
 
loco::TensorShape visit (const luci::CircleFullyConnected *node) final
 
loco::TensorShape visit (const luci::CircleLogistic *node) final
 
loco::TensorShape visit (const luci::CircleMul *node) final
 
loco::TensorShape visit (const luci::CircleNeg *node) final
 
loco::TensorShape visit (const luci::CirclePad *node) final
 
loco::TensorShape visit (const luci::CircleQuantize *node) final
 
loco::TensorShape visit (const luci::CircleRange *node) final
 
loco::TensorShape visit (const luci::CircleReshape *node) final
 
loco::TensorShape visit (const luci::CircleRsqrt *node) final
 
loco::TensorShape visit (const luci::CircleSoftmax *node) final
 
loco::TensorShape visit (const luci::CircleStridedSlice *node) final
 
loco::TensorShape visit (const luci::CircleIfOut *node) final
 
loco::TensorShape visit (const luci::CircleOutput *node) final
 
- Public Member Functions inherited from luci::CircleNodeVisitor< loco::TensorShape >
virtual ~CircleNodeVisitor ()=default
 
- Public Member Functions inherited from luci::CircleNodeVisitorBase< T >
virtual ~CircleNodeVisitorBase ()=default
 

Detailed Description

Definition at line 38 of file CircleShapeInference.h.

Member Function Documentation

◆ visit() [1/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleAdd node)
final

Definition at line 39 of file CircleAdd.cpp.

40{
41 const auto x = loco::must_cast<luci::CircleNode *>(node->x());
42 const auto y = loco::must_cast<luci::CircleNode *>(node->y());
43
44 const auto x_shape = circle_shape(x);
45 const auto y_shape = circle_shape(y);
46
47 return broadcast_shape(x_shape, y_shape);
48}
loco::Node * x(void) const
Definition CircleAdd.h:36
loco::Node * y(void) const
Definition CircleAdd.h:39
loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
loco::TensorShape circle_shape(const luci::CircleNode *node)

References luci::sinf::broadcast_shape(), luci::sinf::circle_shape(), luci::CircleAdd::x(), and luci::CircleAdd::y().

◆ visit() [2/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleBatchMatMul node)
final

Definition at line 83 of file CircleBatchMatMul.cpp.

84{
85 const auto x = loco::must_cast<CircleNode *>(node->x());
86 const auto y = loco::must_cast<CircleNode *>(node->y());
87
88 const auto x_shape = circle_shape(x);
89 const auto y_shape = circle_shape(y);
90
91 uint32_t x_rank = x_shape.rank();
92 uint32_t y_rank = y_shape.rank();
93
94 // throw internal exception if condition not met
95 throw_unless(x_rank >= 2, "x_rank shoud be >= 2");
96 throw_unless(y_rank >= 2, "y_rank shoud be >= 2");
97 throw_unless((not contain_zero(x_shape)), "x_shape should NOT have 0");
98 throw_unless((not contain_zero(y_shape)), "y_shape should NOT have 0");
99
100 // BatchMatMul shape inference rule works with two-part
101 //
102 // 1) Batch dimensions part
103 // - Batch dimensions correspond to the input_shape[:-2]
104 // - General broadcast rules are used to infer the shape of the output batch
105 //
106 // 2) Contracting dimensions part
107 // - Contracting dimensions correspond to the input_shape[-2:]
108 // - General matrix multiplication shape inference applied for this part,
109 // which means '(x_lhs, x_rhs) x (y_lhs, y_rhs) => (x_lhs, y_rhs)'
110
111 uint32_t max_rank = x_rank > y_rank ? x_rank : y_rank;
113 output_shape.rank(max_rank);
114
115 // broadcast in the batch dimensions
116 if (x_rank > 2 || y_rank > 2)
117 {
118 const auto x_batch_dims = remove_last_two(x_shape);
119 const auto y_batch_dims = remove_last_two(y_shape);
120
121 const auto o_batch_dims = sinf::broadcast_shape(x_batch_dims, y_batch_dims);
122
123 const auto o_batch_rank = o_batch_dims.rank();
124 for (uint i = 0u; i < o_batch_rank; ++i)
125 {
126 output_shape.dim(i) = o_batch_dims.dim(i);
127 }
128 }
129
130 // shape inference in contracting dimensions
131 const auto adj_x = node->adj_x();
132 const auto adj_y = node->adj_y();
133
134 loco::Dimension x_lhs = adj_x ? x_shape.dim(x_rank - 1) : x_shape.dim(x_rank - 2);
135 loco::Dimension x_rhs = adj_x ? x_shape.dim(x_rank - 2) : x_shape.dim(x_rank - 1);
136 loco::Dimension y_lhs = adj_y ? y_shape.dim(y_rank - 1) : y_shape.dim(y_rank - 2);
137 loco::Dimension y_rhs = adj_y ? y_shape.dim(y_rank - 2) : y_shape.dim(y_rank - 1);
138
139 if (x_rhs.known() && y_lhs.known() && not(x_rhs == y_lhs))
140 INTERNAL_EXN("x_rhs and y_lhs should be same");
141
142 uint32_t out_rank = output_shape.rank();
143 output_shape.dim(out_rank - 2) = x_lhs;
144 output_shape.dim(out_rank - 1) = y_rhs;
145
146 return output_shape;
147}
#define INTERNAL_EXN(msg)
@ brief throw internal exception with message
Definition InternalExn.h:25
The value of one dimension in a tensor shape.
Definition Dimension.h:30
bool known(void) const
Return whether the value is known (or not)
Definition Dimension.h:47
loco::Node * y(void) const
loco::Node * x(void) const
const luci_interpreter::RuntimeShape output_shape

References luci::CircleBatchMatMul::adj_x(), luci::CircleBatchMatMul::adj_y(), luci::sinf::broadcast_shape(), luci::sinf::circle_shape(), INTERNAL_EXN, loco::Dimension::known(), output_shape, luci::CircleBatchMatMul::x(), and luci::CircleBatchMatMul::y().

◆ visit() [3/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleConcatenation node)
final

Definition at line 42 of file CircleConcatenation.cpp.

43{
44 // TODO Support when CircleConcatenation has 0 input
45 assert(node->numValues() > 0);
46
47 auto first_shape = luci::shape_get(node->values(0)).as<loco::TensorShape>();
48 auto axis = node->axis();
49 if (axis < 0)
50 axis += first_shape.rank();
51
52 assert(0 <= axis);
53 assert(first_shape.rank() > static_cast<uint32_t>(axis));
54
56
57 output_shape.rank(first_shape.rank());
58 for (uint32_t i = 0; i < output_shape.rank(); ++i)
59 output_shape.dim(i) = first_shape.dim(i);
60
61 for (uint32_t i = 1; i < node->numValues(); ++i)
62 {
63 auto input_shape = luci::shape_get(node->values(i)).as<loco::TensorShape>();
64 if (input_shape.rank() != output_shape.rank())
65 INTERNAL_EXN_V("Input has incompatible shape", node->name());
66
67 for (uint32_t j = 0; j < output_shape.rank(); ++j)
68 {
69 if (j == static_cast<uint32_t>(axis))
70 {
71 if (output_shape.dim(j).known() and input_shape.dim(j).known())
72 {
73 output_shape.dim(j) = output_shape.dim(j).value() + input_shape.dim(j).value();
74 }
75 else
76 {
77 // If any of inputs is unknown, just mark it as unknown.
78 output_shape.dim(j).unset();
79 }
80 }
81 else
82 {
83 if (output_shape.dim(j).known() and input_shape.dim(j).known())
84 {
85 if (output_shape.dim(j).value() != input_shape.dim(j).value())
86 {
87 INTERNAL_EXN_V("Input has incompatible shape.", node->name());
88 }
89 }
90 else
91 {
92 if (input_shape.dim(j).known())
93 {
94 assert(not output_shape.dim(j).known()); // FIX_ME_UNLESS
95 output_shape.dim(j) = input_shape.dim(j);
96 }
97 // For unknown input_shape, leave output_shape as-is
98 }
99 }
100 }
101 }
102
103 return output_shape;
104}
#define INTERNAL_EXN_V(msg, val)
@ brief throw internal exception with message and value
Definition InternalExn.h:28
ShapeType as(void) const
uint32_t rank(void) const
Definition TensorShape.h:35
uint32_t numValues(void) const
Node * values(uint32_t index) const
loco::NodeShape shape_get(const loco::Node *node)
NodeName name(void) const

References loco::NodeShape::as(), luci::CircleConcatenation::axis(), INTERNAL_EXN_V, luci::CircleNode::name(), luci::CircleConcatenation::numValues(), output_shape, loco::TensorShape::rank(), luci::shape_get(), and luci::CircleConcatenation::values().

◆ visit() [4/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleDiv node)
final

Definition at line 39 of file CircleDiv.cpp.

40{
41 const auto x = loco::must_cast<luci::CircleNode *>(node->x());
42 const auto y = loco::must_cast<luci::CircleNode *>(node->y());
43
44 const auto x_shape = circle_shape(x);
45 const auto y_shape = circle_shape(y);
46
47 auto output_shape = broadcast_shape(x_shape, y_shape);
48
49 return output_shape;
50}
loco::Node * y(void) const
Definition CircleDiv.h:42
loco::Node * x(void) const
Definition CircleDiv.h:39

References luci::sinf::broadcast_shape(), luci::sinf::circle_shape(), output_shape, luci::CircleDiv::x(), and luci::CircleDiv::y().

◆ visit() [5/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleFullyConnected node)
final

Definition at line 45 of file CircleFullyConnected.cpp.

46{
47 auto input_shape = circle_shape(loco::must_cast<CircleNode *>(node->input()));
48 auto weights_shape = circle_shape(loco::must_cast<CircleNode *>(node->weights()));
49
50 loco::TensorShape out_shape;
51
52 // NOTE Some recipes in some repositories are using rank 4 input for FullyConnected.
53 // Until they are all fixed, disable following assert.
54 // TODO Enable following assert after related fixes are applied
55 // https://github.com/tensorflow/tensorflow/blob/ea33c1e7a25d8025e8ee405ad8ab7be261798d76/tensorflow/lite/kernels/fully_connected.cc#L194
56 // LUCI_ASSERT(input_shape.rank() == 2 || input_shape.rank() == 3,
57 // "Input rank of FullyConnected should be 2 or 3");
58
59 // https://github.com/tensorflow/tensorflow/blob/ea33c1e7a25d8025e8ee405ad8ab7be261798d76/tensorflow/lite/kernels/fully_connected.cc#L225
60 LUCI_ASSERT(weights_shape.rank() == 2, "Weights of FullyConnected should be 2");
61 LUCI_ASSERT(weights_shape.dim(0).known() && weights_shape.dim(1).known(),
62 "Weights of FullyConnected should be known")
63 // https://github.com/tensorflow/tensorflow/blob/ea33c1e7a25d8025e8ee405ad8ab7be261798d76/tensorflow/lite/kernels/fully_connected.cc#L353-L367
64
65 /*
66 * **Pre-conditions:**
67 * input_shape.rank() <= 4
68 * * remark: TFLite allows <=3 ranks, but there are rank 4 input recipes in ONE
69 * weights_shape.rank() == 2 and all dimensions are known.
70 * When runtime(input_shape[-1] and weights_shape[-1] are both known), it should be same value.
71 *
72 * **Shape Inference Rule:**
73 * **Input Shape:**
74 * input_shape : (A, B, C, D)
75 * weights_shape : (E, F)
76 * A, B, C, D are "positive numbers" or "unknown".
77 * E, F are always "positive numbers".
78 *
79 * **Output Shape:**
80 * If keep_dims = True : (A, B, C, E)
81 * If keep_dims = False : (G, E)
82 * * G = unknown (if any of A, B, or C is unknown.)
83 * * G = A * B * C (otherwise.)
84 */
85
86 if (node->keep_num_dims())
87 {
88 out_shape.rank(input_shape.rank());
89 for (uint32_t i = 0; i < input_shape.rank(); ++i)
90 out_shape.dim(i) = input_shape.dim(i);
91 out_shape.dim(out_shape.rank() - 1) = weights_shape.dim(0);
92 }
93 else
94 {
95 bool is_dynamic_shape = false;
96
97 for (uint32_t i = 0; i < input_shape.rank() - 1; i++)
98 {
99 if (not input_shape.dim(i).known())
100 {
101 is_dynamic_shape = true;
102 break;
103 }
104 }
105
106 uint32_t batch_size = 1;
107
108 for (uint32_t i = 0; i < input_shape.rank() - 1; i++)
109 {
110 batch_size *= input_shape.dim(i).value();
111 }
112
113 out_shape.rank(2);
114 if (is_dynamic_shape)
115 out_shape.dim(0).unset();
116 else
117 out_shape.dim(0) = batch_size;
118 out_shape.dim(1) = weights_shape.dim(0);
119 }
120
121 return out_shape;
122}
void unset(void)
Definition Dimension.h:59
uint32_t value(void) const
Return the value.
Definition Dimension.h:51
const Dimension & dim(uint32_t axis) const
Definition TensorShape.h:38
loco::Node * weights(void) const
loco::Node * input(void) const
#define LUCI_ASSERT(condition, msg)
Definition Check.h:26

References luci::sinf::circle_shape(), loco::TensorShape::dim(), luci::CircleFullyConnected::input(), luci::CircleFullyConnected::keep_num_dims(), LUCI_ASSERT, loco::TensorShape::rank(), loco::Dimension::unset(), loco::Dimension::value(), and luci::CircleFullyConnected::weights().

◆ visit() [6/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleIfOut node)
final

Definition at line 79 of file CircleIfOut.cpp.

80{
81 auto graphs = get_out_graphs(node);
82 assert(*graphs.then_graph_output->shape() == *graphs.else_graph_output->shape());
83 return *graphs.then_graph_output->shape();
84}

◆ visit() [7/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleLogistic node)
final

Definition at line 33 of file CircleLogistic.cpp.

34{
35 const auto input_x = loco::must_cast<luci::CircleNode *>(node->x());
36 const auto input_shape = circle_shape(input_x);
37 return input_shape;
38}
loco::Node * x(void) const

References luci::sinf::circle_shape(), and luci::CircleLogistic::x().

◆ visit() [8/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleMul node)
final

Definition at line 38 of file CircleMul.cpp.

39{
40 const auto x = loco::must_cast<luci::CircleNode *>(node->x());
41 const auto y = loco::must_cast<luci::CircleNode *>(node->y());
42
43 const auto x_shape = circle_shape(x);
44 const auto y_shape = circle_shape(y);
45
46 return broadcast_shape(x_shape, y_shape);
47}
loco::Node * y(void) const
Definition CircleMul.h:39
loco::Node * x(void) const
Definition CircleMul.h:36

References luci::sinf::broadcast_shape(), luci::sinf::circle_shape(), luci::CircleMul::x(), and luci::CircleMul::y().

◆ visit() [9/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleNeg node)
final

Definition at line 32 of file CircleNeg.cpp.

33{
34 const auto input_x = loco::must_cast<CircleNode *>(node->x());
35 const auto input_shape = circle_shape(input_x);
36 return input_shape;
37}
loco::Node * x(void) const
Definition CircleNeg.h:34

References luci::sinf::circle_shape(), and luci::CircleNeg::x().

◆ visit() [10/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleNode )
inlinefinalvirtual

Default fallback.

Reimplemented from luci::CircleNodeVisitor< loco::TensorShape >.

Definition at line 42 of file CircleShapeInference.h.

43 {
44 loco::NodeShape shape;
46 return shape.as<loco::TensorShape>();
47 }
bool infer(const loco::Node *, loco::NodeShape &) const final
Infer node's shape.

References loco::NodeShape::as(), and luci::CircleShapeInferenceRule::infer().

◆ visit() [11/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleOutput node)
final

Definition at line 26 of file CircleOutput.cpp.

27{
28 const auto from_shape = loco::must_cast<luci::CircleNode *>(node->from());
29 return sinf::circle_shape(from_shape);
30}
loco::Node * from(void) const

References luci::sinf::circle_shape(), and luci::CircleOutput::from().

◆ visit() [12/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CirclePad node)
final

Definition at line 33 of file CirclePad.cpp.

34{
35 auto paddings = loco::must_cast<const luci::CircleNode *>(node->paddings());
36 auto circle_input = loco::must_cast<const luci::CircleNode *>(node->input());
37 auto input_shape = circle_shape(circle_input);
38 return pad_shape(input_shape, paddings);
39}
loco::Node * input(void) const
Definition CirclePad.h:34
loco::Node * paddings(void) const
Definition CirclePad.h:37
loco::TensorShape pad_shape(const loco::TensorShape &input_shape, const luci::CircleNode *paddings)

References luci::sinf::circle_shape(), luci::CirclePad::input(), luci::sinf::pad_shape(), and luci::CirclePad::paddings().

◆ visit() [13/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleQuantize node)
final

Definition at line 33 of file CircleQuantize.cpp.

34{
35 const auto input = loco::must_cast<CircleNode *>(node->input());
36 const auto input_shape = circle_shape(input);
37 return input_shape;
38}
loco::Node * input(void) const

References luci::sinf::circle_shape(), and luci::CircleQuantize::input().

◆ visit() [14/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleRange node)
final

Definition at line 35 of file CircleRange.cpp.

36{
38 output_shape.rank(1);
39
40 auto start_node = dynamic_cast<luci::CircleConst *>(node->start());
41 auto limit_node = dynamic_cast<luci::CircleConst *>(node->limit());
42 auto delta_node = dynamic_cast<luci::CircleConst *>(node->delta());
43
44 if (start_node == nullptr || limit_node == nullptr || delta_node == nullptr)
45 {
46 return output_shape;
47 }
48
49 double start = 0, limit = 0, delta = 0;
50
51#define GET_RANGE_PARAM(DT) \
52 start = start_node->scalar<DT>(); \
53 limit = limit_node->scalar<DT>(); \
54 delta = delta_node->scalar<DT>();
55
56 switch (start_node->dtype())
57 {
58 case loco::DataType::FLOAT32:
59 GET_RANGE_PARAM(loco::DataType::FLOAT32)
60 break;
61 case loco::DataType::S32:
62 GET_RANGE_PARAM(loco::DataType::S32)
63 break;
64 default:
65 INTERNAL_EXN("Range data type not supported");
66 }
67
68#undef GET_RANGE_PARAM
69
70 if (delta == 0)
71 INTERNAL_EXN("Delta can not be zero");
72
73 /*
74 * Pre-condition
75 * 'limit - start' and 'delta' have the same sign.
76 * c1. '(limit - start) >= 0' -> 'delta > 0'
77 * c2. '(limit - start) < 0' -> 'delta < 0'
78 * https://github.com/tensorflow/tensorflow/blob/da82fa9/tensorflow/lite/kernels/range.cc#L49-L50
79 */
80 assert((start >= limit && delta < 0) || (start <= limit && delta > 0));
81 output_shape.dim(0) = ceil((limit - start) / delta);
82
83 return output_shape;
84}
Class to build tensor data.
Definition CircleConst.h:35
loco::Node * delta(void) const
Definition CircleRange.h:40
loco::Node * limit(void) const
Definition CircleRange.h:37
loco::Node * start(void) const
Definition CircleRange.h:34
#define GET_RANGE_PARAM(DT)

References luci::CircleRange::delta(), GET_RANGE_PARAM, INTERNAL_EXN, luci::CircleRange::limit(), output_shape, and luci::CircleRange::start().

◆ visit() [15/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleReshape node)
final

Definition at line 69 of file CircleReshape.cpp.

70{
71 LOGGER(l);
72
73 const loco::DataType S32 = loco::DataType::S32;
74
75 bool is_static_shape = true;
76
77 loco::TensorShape shape_by_input;
78 {
79 LUCI_ASSERT(node->shape(), "2nd input shape() should not be nullptr");
80
81 // Only support node's shape() is CircleConst with S32
82 // TODO support other node with other types
83 auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->shape());
84 if (const_shape_node != nullptr)
85 {
86 LUCI_ASSERT(const_shape_node->dtype() == S32, "Only support int32 CircleConst");
87
88 shape_by_input.rank(const_shape_node->size<S32>());
89
90 for (uint32_t axis = 0; axis < shape_by_input.rank(); ++axis)
91 {
92 if (const_shape_node->at<S32>(axis) < 0)
93 {
94 shape_by_input.dim(axis).unset();
95 }
96 else if (const_shape_node->at<S32>(axis) == 0)
97 {
98 const auto node_tensor = loco::must_cast<luci::CircleNode *>(node->tensor());
99 // set dim value to input
100 if (node_tensor->shape_status() == luci::ShapeStatus::VALID && axis < node_tensor->rank())
101 shape_by_input.dim(axis) = node_tensor->dim(axis);
102 else
103 {
104 // stop to check if this case exist for debugging
105 INTERNAL_EXN("Check Reshape shape with 0");
106 }
107 }
108 else
109 {
110 shape_by_input.dim(axis).set(const_shape_node->at<S32>(axis));
111 }
112 // check valid or stop for debugging
113 LUCI_ASSERT(shape_by_input.dim(axis).value() > 0 || !shape_by_input.dim(axis).known(),
114 "Reshape infer shape is invalid.");
115 }
116 }
117 else
118 {
119 // NOTE assumption is that `shape` and `newShape` having same value.
120 // for non-existing `shape`, we can use `newShape` if it's valid
121 auto new_shape = node->newShape();
122 auto rank = new_shape->rank();
123 auto shape_dummy = dynamic_cast<luci::CircleOutputDummy *>(node->shape());
124 if (shape_dummy && rank > 0)
125 {
126 is_static_shape = true;
127 shape_by_input.rank(rank);
128 for (uint32_t i = 0; i < rank; ++i)
129 {
130 if (new_shape->dim(i) > 0)
131 shape_by_input.dim(i) = static_cast<uint32_t>(new_shape->dim(i));
132 else
133 {
134 is_static_shape = false;
135 shape_by_input.dim(i).unset();
136 }
137 }
138 }
139 else
140 {
141 auto shape_node = loco::must_cast<luci::CircleNode *>(node->shape());
142 assert(shape_node->rank() == 1);
143 // shape_node tensor values will provide new shape, like [2, 3, 4]
144 auto num_elements = shape_node->dim(0).value(); // above example will give 3
145 shape_by_input.rank(num_elements);
146 is_static_shape = false;
147 }
148 }
149 }
150
151 loco::TensorShape shape_by_attr;
152 {
153 shape_by_attr.rank(node->newShape()->rank());
154
155 for (uint32_t axis = 0; axis < shape_by_attr.rank(); ++axis)
156 {
157 shape_by_attr.dim(axis) = node->newShape()->dim(axis);
158 }
159 }
160
161 if (!(shape_by_input == shape_by_attr))
162 {
163 INFO(l) << "CircleReshape: Two new shape information mismatched : " << std::endl;
164 INFO(l) << " shape_by_input : " << shape_by_input << std::endl;
165 INFO(l) << " shape_by_attr : " << shape_by_attr << std::endl;
166 }
167
168 loco::TensorShape output_shape = shape_by_input;
169
170 // One of the dimensions can have special value -1, meaning its actual value should be inferred.
171 const auto input = loco::must_cast<luci::CircleNode *>(node->tensor());
172 const auto input_shape = circle_shape(input);
173 uint32_t input_element_count = 1;
174 uint32_t output_element_count = 1;
175 uint32_t unknown_dim_index = UINT32_MAX;
176 for (uint32_t i = 0; i < input_shape.rank(); ++i)
177 {
178 if (input_shape.dim(i).known())
179 input_element_count *= input_shape.dim(i).value();
180 else
181 is_static_shape = false;
182 }
183
184 if (is_static_shape)
185 {
186 for (uint32_t dim_index = 0; dim_index < output_shape.rank(); ++dim_index)
187 {
188 uint32_t dim_value = output_shape.dim(dim_index).value();
189 if (not output_shape.dim(dim_index).known())
190 {
191 LUCI_ASSERT(unknown_dim_index == UINT32_MAX, "More than one unknown dimension");
192 unknown_dim_index = dim_index;
193 }
194 else
195 {
196 if (!dim_value)
197 {
198 // refer https://github.com/Samsung/ONE/issues/14074#issuecomment-2370795003
199 // set dim value to follow input
200 if (dim_index < input_shape.rank())
201 dim_value = input_shape.dim(dim_index).value();
202 else
203 {
204 // stop to check if this case exist for debugging
205 INTERNAL_EXN("Check Reshape shape with 0");
206 }
207 }
208 output_element_count *= dim_value;
209 }
210 }
211 if (unknown_dim_index != UINT32_MAX)
212 {
213 if (input_element_count % output_element_count != 0)
214 {
215 INTERNAL_EXN("Reshape Op cannot infer unknown dimension from inputs.");
216 }
217 output_shape.dim(unknown_dim_index) = input_element_count / output_element_count;
218 }
219 }
220
221 return output_shape;
222}
#define LOGGER(name)
Definition Log.h:65
#define INFO(name)
Definition Log.h:68
void set(uint32_t value)
Definition Dimension.h:53
Temporary DummyNode used with dangle CircleNode.
uint32_t rank(void) const
int32_t dim(uint32_t n) const
const Shape * newShape(void) const
loco::Node * shape(void) const
loco::Node * tensor(void) const
DataType
"scalar" value type
Definition DataType.h:27
const loco::DataType S32
uint32_t num_elements(const Shape &shape)
The number of elements of a feature map of a given shape.
Definition Shape.h:59

References luci::sinf::circle_shape(), loco::TensorShape::dim(), luci::CircleReshape::Shape::dim(), INFO, INTERNAL_EXN, loco::Dimension::known(), LOGGER, LUCI_ASSERT, luci::CircleReshape::newShape(), output_shape, loco::TensorShape::rank(), luci::CircleReshape::Shape::rank(), luci::sinf::S32, loco::Dimension::set(), luci::CircleReshape::shape(), luci::CircleReshape::tensor(), loco::Dimension::unset(), luci::VALID, and loco::Dimension::value().

◆ visit() [16/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleRsqrt node)
final

Definition at line 33 of file CircleRsqrt.cpp.

34{
35 const auto input_x = loco::must_cast<CircleNode *>(node->x());
36 const auto input_shape = circle_shape(input_x);
37 return input_shape;
38}
loco::Node * x(void) const
Definition CircleRsqrt.h:34

References luci::sinf::circle_shape(), and luci::CircleRsqrt::x().

◆ visit() [17/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleSoftmax node)
final

Definition at line 35 of file CircleSoftmax.cpp.

36{
37 const auto logits = loco::must_cast<luci::CircleNode *>(node->logits());
38
39 return sinf::circle_shape(logits);
40}
loco::Node * logits(void) const

References luci::sinf::circle_shape(), and luci::CircleSoftmax::logits().

◆ visit() [18/18]

loco::TensorShape luci::sinf::Algorithm::visit ( const luci::CircleStridedSlice node)
final

Definition at line 384 of file CircleStridedSlice.cpp.

385{
387
388 auto input_node = loco::must_cast<luci::CircleNode *>(node->input());
389
390 auto begin_node = loco::must_cast<luci::CircleNode *>(node->begin());
391 auto end_node = loco::must_cast<luci::CircleNode *>(node->end());
392 auto strides_node = loco::must_cast<luci::CircleNode *>(node->strides());
393
394 LUCI_ASSERT(begin_node->dtype() == S32, "Only support S32 for begin_node");
395 LUCI_ASSERT(end_node->dtype() == S32, "Only support S32 for end_node");
396 LUCI_ASSERT(strides_node->dtype() == S32, "Only support S32 for strides_node");
397
398 LUCI_ASSERT(begin_node->rank() == 1, "Only support rank 1 for begin_node");
399 LUCI_ASSERT(end_node->rank() == 1, "Only support rank 1 for end_node");
400 LUCI_ASSERT(strides_node->rank() == 1, "Only support rank 1 for strides_node");
401
402 auto begin_const = dynamic_cast<luci::CircleConst *>(node->begin());
403 auto end_const = dynamic_cast<luci::CircleConst *>(node->end());
404 auto strides_const = dynamic_cast<luci::CircleConst *>(node->strides());
405 // TODO support non-const strides_node
406 if (strides_const == nullptr)
407 {
408 INTERNAL_EXN("StridedSlice strides node is not Constant");
409 }
410 if (begin_const == nullptr || end_const == nullptr)
411 {
412 // The dimensions of the output shape are all set to unknown.
413 output_shape.rank(input_node->rank());
414 return output_shape;
415 }
416
418
419 assert(begin_const->size<S32>() <= input_shape.rank());
420 assert(end_const->size<S32>() <= input_shape.rank());
421 assert(strides_const->size<S32>() <= input_shape.rank());
422
423 StridedSliceContext op_context(node);
424 auto op_params = BuildStridedSliceParams(&op_context);
425 auto &effective_input_shape = op_context.effective_input_shape;
426 std::vector<int64_t> output_shape_vector;
427 std::vector<bool> output_known_vector;
428
429 for (int32_t idx = effective_input_shape.rank() - 1; idx >= 0; --idx)
430 {
431 int32_t stride = op_params.strides[idx];
432 LUCI_ASSERT(stride != 0, "stride value has to be non-zero");
433
434 int64_t begin = StartForAxis(op_params, effective_input_shape, idx);
435 int64_t end = StopForAxis(op_params, effective_input_shape, idx, begin);
436
437 // When shrinking an axis, the end position does not matter (and can be
438 // incorrect when negative indexing is used, see Issue #19260). Always use
439 // begin + 1 to generate a length 1 slice, since begin has
440 // already been adjusted for negative indices by GetBeginValueAtIndex.
441 const bool shrink_axis = op_params.shrink_axis_mask & (1 << idx);
442 if (shrink_axis)
443 {
444 end = begin + 1;
445 }
446
447 // This is valid for both positive and negative strides
448 int64_t dim_shape = std::ceil((end - begin) / static_cast<float>(stride));
449 dim_shape = dim_shape < 0 ? 0 : dim_shape;
450 if (!shrink_axis)
451 {
452 output_shape_vector.push_back(dim_shape);
453 output_known_vector.push_back(effective_input_shape.dim(idx).known());
454 }
455 }
456
457 auto shape_size = output_shape_vector.size();
458 output_shape.rank(shape_size);
459 for (uint32_t idx = 0; idx < shape_size; ++idx)
460 {
461 bool known = output_known_vector[shape_size - 1u - idx];
462 if (not known)
463 continue;
464 int64_t dim = output_shape_vector.at(shape_size - 1u - idx);
465 LUCI_ASSERT(0 <= dim && dim < 0xfffffffL, "Dimension size exceeds limit");
466 // reverse copy
467 output_shape.dim(idx) = static_cast<uint32_t>(dim);
468 }
469
470 return output_shape;
471}
loco::Node * input(void) const
loco::Node * begin(void) const
loco::Node * strides(void) const
loco::Node * end(void) const
int64_t StopForAxis(const StridedSliceParams &params, const loco::TensorShape &input_shape, int64_t axis, int64_t start_for_axis)
int64_t StartForAxis(const StridedSliceParams &params, const loco::TensorShape &input_shape, int64_t axis)
StridedSliceParams BuildStridedSliceParams(StridedSliceContext *op_context)
CircleInput * input_node(loco::Graph *g, const loco::GraphInputIndex &index)
Find a Pull node with a given input index.
ShapeIterator end(const Shape &s)
int32_t begin[5]
Definition Slice.cpp:33

References luci::CircleStridedSlice::begin(), begin, luci::sinf::BuildStridedSliceParams(), luci::sinf::circle_shape(), luci::sinf::StridedSliceContext::effective_input_shape, luci::CircleStridedSlice::end(), luci::CircleStridedSlice::input(), luci::input_node(), INTERNAL_EXN, LUCI_ASSERT, output_shape, loco::TensorShape::rank(), luci::sinf::S32, luci::sinf::StartForAxis(), luci::sinf::StopForAxis(), and luci::CircleStridedSlice::strides().


The documentation for this class was generated from the following files: