0927d488c46e21bc2585436220298ed2669984df
[platform/core/ml/nnfw.git] / runtime / neurun / core / include / backend / IConstantInitializer.h
1 /*
2  * Copyright (c) 2019 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 #ifndef __NEURUN_BACKEND_ICONSTANT_INITIALIZER_H__
18 #define __NEURUN_BACKEND_ICONSTANT_INITIALIZER_H__
19
20 #include <unordered_map>
21 #include <functional>
22
23 #include "ITensorBuilder.h"
24 #include "ir/Layout.h"
25 #include "model/Operand.h"
26 #include "model/Operands.h"
27 #include "model/OperationVisitor.h"
28 #include "model/Subgraph.h"
29 #include "util/logging.h"
30 #include "util/Utils.h"
31
32 namespace
33 {
34 template <typename T>
35 static void Init(const neurun::model::Operand &model_obj, neurun::backend::operand::ITensor &obj,
36                  const bool copy,
37                  const neurun::ir::Layout frontend_layout = neurun::ir::Layout::UNKNOWN)
38 {
39   const auto shape = model_obj.shape();
40   auto base = reinterpret_cast<const T *>(model_obj.data().base());
41
42   obj.access([&](::neurun::backend::operand::ITensor &tensor) {
43     switch (shape.rank())
44     {
45       case 0:
46       {
47         assert(model_obj.data().size() == sizeof(T));
48         const auto value = *reinterpret_cast<const T *>(base);
49         T *into = reinterpret_cast<T *>(tensor.buffer());
50         *into = value;
51         break;
52       }
53       case 1:
54       {
55         auto vec_size = shape.dim(0);
56         for (int32_t n = 0; n < vec_size; ++n)
57         {
58           const T *from = reinterpret_cast<const T *>(base) + n;
59           const auto value = *from;
60
61           T *into = reinterpret_cast<T *>(tensor.buffer()) + n;
62
63           *into = value;
64         }
65         break;
66       }
67       case 2:
68       {
69         const int32_t copy_len = shape.dim(1);
70
71         for (auto i = 0; i < shape.dim(0); ++i)
72         {
73           neurun::util::Coordinates coords{i, 0};
74           memcpy(tensor.buffer() + tensor.calcOffset(coords), base + i * copy_len,
75                  copy_len * sizeof(T));
76         }
77         break;
78       }
79       case 3:
80       {
81         const int32_t width = shape.dim(1);
82         const int32_t copy_len = shape.dim(2);
83
84         for (auto i = 0; i < shape.dim(0); ++i)
85         {
86           for (auto j = 0; j < shape.dim(1); ++j)
87           {
88             neurun::util::Coordinates coords{i, j, 0};
89             memcpy(tensor.buffer() + tensor.calcOffset(coords),
90                    base + i * width * copy_len + j * copy_len, copy_len * sizeof(T));
91           }
92         }
93         break;
94       }
95       case 4:
96       {
97         const int32_t height = shape.dim(1);
98         const int32_t width = shape.dim(2);
99         const int32_t copy_len = shape.dim(3);
100         for (auto i = 0; i < shape.dim(0); ++i)
101         {
102           for (auto j = 0; j < shape.dim(1); ++j)
103           {
104             for (auto k = 0; k < shape.dim(2); ++k)
105             {
106               if (copy)
107               {
108                 neurun::util::Coordinates coords{i, j, k, 0};
109                 memcpy(tensor.buffer() + tensor.calcOffset(coords),
110                        base + i * height * width * copy_len + j * width * copy_len + k * copy_len,
111                        copy_len * sizeof(T));
112               }
113               else
114               {
115                 for (auto l = 0; l < shape.dim(3); ++l)
116                 {
117                   const auto coords = neurun::util::convertCoordinates(
118                       {i, j, k, l}, frontend_layout, tensor.layout());
119                   T *into = reinterpret_cast<T *>(tensor.buffer() + tensor.calcOffset(coords));
120                   T value = *(base + i * height * width * copy_len + j * width * copy_len +
121                               k * copy_len + l);
122                   *into = value;
123                 }
124               }
125             }
126           }
127         }
128         break;
129       }
130       default:
131         throw std::runtime_error{"Not yet supported"};
132     }
133   });
134 }
135
136 template <typename T>
137 void copyInit(const neurun::model::Operand &model_obj, neurun::backend::operand::ITensor &obj)
138 {
139   Init<T>(model_obj, obj, true);
140 }
141
142 template <typename T>
143 void permuteInit(const neurun::model::Operand &model_obj, neurun::backend::operand::ITensor &obj,
144                  const neurun::ir::Layout frontend_layout)
145 {
146   const bool copy = frontend_layout == obj.layout();
147   Init<T>(model_obj, obj, copy, frontend_layout);
148 }
149
150 } // namespace
151
152 namespace neurun
153 {
154 namespace backend
155 {
156
157 class IConstantInitializer : model::OperationVisitor
158 {
159 public:
160   virtual ~IConstantInitializer() = default;
161
162 public:
163   void run()
164   {
165     assert(tensor_builder().get());
166     for (const auto &it : _init_map)
167     {
168       const auto &ind = it.first;
169       const auto &fn = it.second;
170
171       const auto &model_obj = operands().at(ind);
172       auto tensor_obj = tensor_builder()->tensorAt(ind);
173       fn(model_obj, *tensor_obj);
174       VERBOSE(FillOperandData) << "Fill data for operand " << ind.value() << std::endl;
175     }
176     _init_map.clear();
177   }
178
179 public:
180   using Initializer = std::function<void(const model::Operand &, backend::operand::ITensor &)>;
181
182   void generate(const model::Subgraph &subg, const model::Operands &operands)
183   {
184     _current_subg_layout = subg.getLayout();
185     subg.accept(*this);
186     for (const auto &e : subg.operations())
187     {
188       for (const auto &ind : e.node->getInputs())
189       {
190         const auto &obj = operands.at(ind);
191         if (obj.isConstant() && !exist(ind))
192         {
193           registerPermuteInitializer(ind, obj);
194         }
195       }
196     }
197   }
198
199 protected:
200 #define OP(InternalName) \
201   virtual void visit(const model::operation::InternalName &) override { /* DO NOTHING */}
202 #include "model/Operations.lst"
203 #undef OP
204
205 protected:
206   virtual const model::Operands &operands() const = 0;
207   virtual std::shared_ptr<ITensorBuilder> tensor_builder() const = 0;
208
209 protected:
210   void registerCopyInitializer(const model::OperandIndex &index, const model::Operand &obj)
211   {
212     // For only CONSTANTS
213     // TODO Add to check if tensor has been allocated
214     if (!obj.isConstant())
215       return;
216
217     const auto type = obj.typeInfo().type();
218     using neurun::model::DataType;
219
220     switch (type)
221     {
222       case DataType::FLOAT32:
223         _init_map[index] = copyInit<float>;
224         break;
225       case DataType::INT32:
226         _init_map[index] = copyInit<int32_t>;
227         break;
228       case DataType::UINT32:
229         _init_map[index] = copyInit<uint32_t>;
230         break;
231       case DataType::BOOL8:
232       case DataType::QUANT8_ASYMM:
233         _init_map[index] = copyInit<uint8_t>;
234         break;
235       default:
236         throw std::runtime_error("Not supported, yet");
237         break;
238     }
239   }
240
241 protected:
242   void registerPermuteInitializer(const model::OperandIndex &index, const model::Operand &obj)
243   {
244     // For only CONSTANTS
245     // TODO Add to check if tensor has been allocated
246     if (!obj.isConstant())
247       return;
248
249     const auto type = obj.typeInfo().type();
250     using neurun::model::DataType;
251     using namespace std::placeholders;
252
253     switch (type)
254     {
255       case DataType::FLOAT32:
256         _init_map[index] = std::bind(permuteInit<float>, _1, _2, _current_subg_layout);
257         break;
258       case DataType::INT32:
259         _init_map[index] = std::bind(permuteInit<int32_t>, _1, _2, _current_subg_layout);
260         break;
261       case DataType::UINT32:
262         _init_map[index] = std::bind(permuteInit<uint32_t>, _1, _2, _current_subg_layout);
263         break;
264       case DataType::BOOL8:
265       case DataType::QUANT8_ASYMM:
266         _init_map[index] = std::bind(permuteInit<uint8_t>, _1, _2, _current_subg_layout);
267         break;
268       default:
269         throw std::runtime_error("Not supported, yet");
270         break;
271     }
272   }
273
274 private:
275   bool exist(const model::OperandIndex &ind) { return _init_map.find(ind) != _init_map.end(); }
276
277 protected:
278   std::unordered_map<model::OperandIndex, Initializer> _init_map;
279   ir::Layout _current_subg_layout;
280 };
281
282 } // namespace backend
283 } // namespace neurun
284
285 #endif // __NEURUN_BACKEND_ICONSTANT_INITIALIZER_H__