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