ONE - On-device Neural Engine
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
CircleReshape.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
18#include "Check.h"
19
21#include "CircleCloneNode.h"
22
23#include <luci/Log.h>
24
25#include <oops/InternalExn.h>
26
27namespace
28{
29
30std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
31{
32 os << "[";
33 for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
34 {
35 if (r)
36 os << ",";
37
38 if (tensor_shape.dim(r).known())
39 os << tensor_shape.dim(r).value();
40 else
41 os << "?";
42 }
43 os << "]";
44 return os;
45}
46
47} // namespace
48
49namespace luci
50{
51
53{
54 auto *cloned = _graph->nodes()->create<luci::CircleReshape>();
55 {
56 uint32_t rank = node->newShape()->rank();
57 cloned->newShape()->rank(rank);
58 for (uint32_t r = 0; r < rank; ++r)
59 {
60 cloned->newShape()->dim(r) = node->newShape()->dim(r);
61 }
62 }
63 return cloned;
64}
65
66namespace sinf
67{
68
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}
223
224} // namespace sinf
225} // namespace luci
#define INTERNAL_EXN(msg)
@ brief throw internal exception with message
Definition InternalExn.h:25
#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
void unset(void)
Definition Dimension.h:59
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
void set(uint32_t value)
Definition Dimension.h:53
const Dimension & dim(uint32_t axis) const
Definition TensorShape.h:38
uint32_t rank(void) const
Definition TensorShape.h:35
Class to build tensor data.
Definition CircleConst.h:35
Temporary DummyNode used with dangle CircleNode.
uint32_t rank(void) const
int32_t dim(uint32_t n) const
RESHAPE in Circle.
const Shape * newShape(void) const
loco::Node * shape(void) const
loco::Node * tensor(void) const
loco::TensorShape visit(const luci::CircleNode *node) final
Default fallback.
const luci_interpreter::RuntimeShape output_shape
#define LUCI_ASSERT(condition, msg)
Definition Check.h:26
DataType
"scalar" value type
Definition DataType.h:27
const loco::DataType S32
loco::TensorShape circle_shape(const luci::CircleNode *node)