ONE - On-device Neural Engine
Loading...
Searching...
No Matches
Concatenation.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018 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
17#include "Concatenation.h"
18
19#include "IRBuilder.h"
20#include "GraphBuilder.h"
21
22#include <coco/IR/Module.h>
24
26#include <schema_generated.h>
27
28#include <array>
29#include <cassert>
30
31using namespace nncc::core::ADT;
32
33namespace
34{
35
39coco::ConcatF::Axis as_ConcatF_axis(uint32_t axis)
40{
41 // NOTE The feature map (in TensorFlow) is a rank-4 (NHWC) tensor
42 assert(axis < 4);
43
45
46 switch (axis)
47 {
48 case 0:
50 break;
51 case 1:
53 break;
54 case 2:
56 break;
57 case 3:
59 break;
60 default:
61 break;
62 }
63
64 return res;
65}
66
70std::array<uint32_t, 4> as_dims(const coco::FeatureShape &shape)
71{
72 std::array<uint32_t, 4> res;
73
74 res[0] = shape.batch();
75 res[1] = shape.height();
76 res[2] = shape.width();
77 res[3] = shape.depth();
78
79 return res;
80}
81
86{
87 assert(shape.rank() == 4);
88
89 auto const B = shape.dim(0);
90 auto const C = shape.dim(3);
91 auto const H = shape.dim(1);
92 auto const W = shape.dim(2);
93
94 return coco::FeatureShape{B, C, H, W};
95}
96
97} // namespace
98
99namespace tflimport
100{
101
102void ConcatenationGraphBuilder::build(const tflite::Operator *op,
103 GraphBuilderContext *context) const
104{
105 assert(context != nullptr);
106
107 coco::Module *m = context->m();
108 coco::Data *d = context->d();
109 coco::Block *blk = context->block();
110 TensorContext &tensor_context = context->tensor();
111 TensorBags &bags = context->bags();
112 IndexVector opinputs = as_index_vector(op->inputs());
113 IndexVector opoutputs = as_index_vector(op->outputs());
114
115 // these are fixed in tflite
116 // input index 0 ~ N : any number of input features
117 // output index 0 : one output feature
118 assert(opinputs.size() > 0);
119 assert(opoutputs.size() == 1);
120
121 // Default parameter values are referenced from schema_generated.h
122 int32_t concat_axis = 0;
123 tflite::ActivationFunctionType activation = tflite::ActivationFunctionType_NONE;
124
125 if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions())
126 {
127 activation = concatenation_params->fused_activation_function();
128 concat_axis = concatenation_params->axis();
129
130 const int32_t rank = static_cast<int32_t>(tensor_context.shape(opinputs.at(0)).rank());
131 if (concat_axis < 0)
132 {
133 concat_axis += rank;
134 }
135 assert(concat_axis >= 0);
136 assert(concat_axis < rank);
137 }
138 assert(as_ConcatF_axis(concat_axis) != coco::ConcatF::Axis::Unknown);
139 assert(activation == tflite::ActivationFunctionType_NONE);
140
141 // Construct a vector of input objects
142 std::vector<coco::FeatureObject *> input_objects;
143
144 for (auto &input_index : opinputs)
145 {
146 const tensor::Shape &input_shape = tensor_context.shape(input_index);
147 coco::FeatureObject *input_obj = m->entity()->object()->create<coco::FeatureObject>();
148 coco::Bag *input_bag = bags.bag(input_index);
149 input_obj->bag(input_bag);
150 input_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(input_shape)));
151
152 input_objects.emplace_back(input_obj);
153 }
154
155 coco::FeatureObject *last_feature = input_objects.at(0);
156
157 assert(last_feature != nullptr);
158 assert(last_feature->bag() != nullptr);
159
160 // Update coco IR
161 //
162 // Given a sequence of input features %in[0] / %in[1] / ... / %in[N]
163 // the below code constructs a sequence of eval instructions
164 // - Load is omitted for simplicity
165 //
166 // %tmp = eval(ConcatF(%in[0], %in[1]))
167 // %tmp = eval(ConcatF(%tmp, %in[2]))
168 // ...
169 // %tmp = eval(ConcatF(%tmp, %in[N]))
170 // %out[0] = copy(%tmp)
171 //
172 for (uint32_t n = 1; n < input_objects.size(); ++n)
173 {
174 auto const left_feature = last_feature;
175 auto const left_shape = left_feature->layout()->shape();
176
177 auto right_feature = input_objects.at(n);
178 auto right_shape = right_feature->layout()->shape();
179
180 // Compute output dimensionalities
181 auto compute_out_dims = [&left_shape, &right_shape, concat_axis](void) {
182 std::array<uint32_t, 4> out_dims;
183
184 const auto left_dims = as_dims(left_shape);
185 const auto right_dims = as_dims(right_shape);
186
187 for (uint32_t axis = 0; axis < 4 /* FEATURE MAP RANK */; ++axis)
188 {
189 // The dimensionality of all the axises except 'concat' axis SHOULD BE INDETICAL
190 assert((concat_axis == axis) || (left_dims[axis] == right_dims[axis]));
191
192 out_dims[axis] = left_dims[axis];
193 if (axis == concat_axis)
194 {
195 out_dims[axis] += right_dims[axis];
196 }
197 }
198
199 return out_dims;
200 };
201
202 const auto out_dims = compute_out_dims();
203
204 const uint32_t B = out_dims[0 /* BATCH */];
205 const uint32_t C = out_dims[3 /* DEPTH */];
206 const uint32_t H = out_dims[1 /* HEIGHT */];
207 const uint32_t W = out_dims[2 /* WIDTH */];
208
209 const coco::FeatureShape out_shape{B, C, H, W};
210
211 auto out_bag = m->entity()->bag()->create(B * num_elements(out_shape));
212 auto out_feature = m->entity()->object()->create<coco::FeatureObject>();
213
214 out_feature->bag(out_bag);
215 out_feature->layout(coco::FeatureLayouts::BHWC::create(out_shape));
216
217 auto left_load = op_builder(m).load(left_feature).pop();
218 auto right_load = op_builder(m).load(right_feature).pop();
219
220 auto concat_f = m->entity()->op()->create<coco::ConcatF>();
221
222 concat_f->axis(as_ConcatF_axis(concat_axis));
223 concat_f->left(left_load);
224 concat_f->right(right_load);
225
226 auto eval = instr_builder(m).eval(out_feature, concat_f);
227
228 // Append the constructed Shuffle instruction
229 blk->instr()->append(eval);
230
231 // Update 'last_feature'
232 last_feature = out_feature;
233 }
234
235 // Insert copy instruction from last_feature to output operand
236 int const ofm_idx = opoutputs.at(0);
237 auto const ofm_shape = tensor_context.shape(ofm_idx);
238
239 auto ofm_bag = bags.bag(ofm_idx);
240 auto ofm_obj = m->entity()->object()->create<coco::FeatureObject>();
241
242 ofm_obj->bag(ofm_bag);
243 ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape)));
244
245 // Create a Copy instruction from last into ofm
246 auto copy_ins = instr_builder(m).copy(ofm_obj, last_feature);
247
248 // Append the instruction
249 blk->instr()->append(copy_ins);
250}
251
252} // namespace tflimport
OpBuilder op_builder(coco::Module *m)
Definition IRBuilder.h:144
InstrBuilder instr_builder(coco::Module *m)
Definition IRBuilder.h:174
coco::Copy * copy(coco::Object *into, coco::Object *from) const
Create "Copy" instruction with given two "Object".
Definition IRBuilder.h:161
coco::Eval * eval(coco::Object *out, coco::Op *op) const
Create "Eval" instruction with a given "Object" and "Op".
Definition IRBuilder.h:162
OpBuilder & load(coco::Object *obj)
Create "Load" op and push it onto the internal stack.
Definition IRBuilder.h:70
coco::Op * pop(void)
Pop op from the internal stack.
Definition IRBuilder.h:116
A collection of (abstracted) elements of the same type.
Definition Bag.h:48
A unit of (grouped) instructions.
Definition Block.h:40
InstrList * instr(void)
Definition Block.h:65
Concatenate two feature maps.
Definition Ops.h:363
const Axis & axis(void) const
Definition Ops.h:386
void append(Child *child)
static std::unique_ptr< BHWC > create(const nncc::core::ADT::feature::Shape &shape)
FeatureMap values (used in CNN)
const FeatureLayout * layout(void) const
The shape of a feature map.
uint32_t batch(void) const
Top-level element of coco IR which represents a neural network.
Definition Module.h:34
coco::Bag * bag(void) const
Definition Object.h:74
uint32_t depth(void) const
Definition Shape.h:44
uint32_t width(void) const
Definition Shape.h:46
uint32_t height(void) const
Definition Shape.h:45
uint32_t & dim(uint32_t axis)
Definition Shape.cpp:42
uint32_t rank(void) const
Definition Shape.cpp:35
void build(const tflite::Operator *op, GraphBuilderContext *) const override
Class to store context to build IR from tflite.
Definition Context.h:133
TensorContext & tensor()
Definition Context.h:152
Pre-creates coco:Bags for each operands(tensors)
Definition TensorBags.h:38
coco::Bag * bag(int32_t tensor_id)
Definition TensorBags.h:52
Extracts and holds operand(tensor) information such as name, shape, and type.
Definition Context.h:39
const tensor::Shape & shape(uint32_t tensor_id)
Definition Context.h:44
C
Definition infer.py:52
loco::FeatureShape as_feature_shape(const loco::NodeShape &nodeshape, const TFDataLayout &data_layout)
Dims< uint32_t > as_dims(const nncc::core::ADT::tensor::Shape &)
Definition dims.cpp:24
std::vector< int32_t > IndexVector
Definition Convert.h:29
IndexVector as_index_vector(const flatbuffers::Vector< int32_t > *array)
Converts flatbuffers::Vector to IndexVector.
Definition Convert.cpp:28
Core coco entity for constant weights.
Definition Data.h:31
virtual const FeatureShape & shape(void) const =0