Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / compiler / circle-eval-diff / src / InputDataLoader.cpp
1 /*
2  * Copyright (c) 2022 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 "InputDataLoader.h"
18
19 #include <dio_hdf5/HDF5Importer.h>
20 #include <loco/IR/Graph.h>
21 #include <luci/IR/CircleNodes.h>
22
23 #include <cstring>
24 #include <dirent.h>
25 #include <fstream>
26 #include <vector>
27
28 using DataType = loco::DataType;
29 using Shape = std::vector<loco::Dimension>;
30
31 namespace circle_eval_diff
32 {
33
34 // Check the type and the shape of CircleInput
35 void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, const Shape &shape)
36 {
37   // Type check
38   if (dtype != input_node->dtype())
39     throw std::runtime_error("Wrong input type.");
40
41   if (shape.size() != input_node->rank())
42     throw std::runtime_error("Input rank mismatch.");
43
44   for (uint32_t i = 0; i < shape.size(); i++)
45   {
46     if (not(shape.at(i) == input_node->dim(i)))
47       throw std::runtime_error("Input shape mismatch.");
48   }
49 }
50
51 std::vector<size_t> getEachByteSizeOf(const std::vector<loco::Node *> &nodes)
52 {
53   std::vector<size_t> vec;
54
55   for (const auto node : nodes)
56   {
57     const auto input_node = loco::must_cast<const luci::CircleInput *>(node);
58     const auto dtype_size = loco::size(input_node->dtype());
59     size_t element_size = 1;
60
61     for (uint32_t index = 0; index < input_node->rank(); index++)
62     {
63       element_size *= input_node->dim(index).value();
64     }
65
66     vec.push_back(element_size * dtype_size);
67   }
68
69   return vec;
70 }
71
72 size_t getTotalByteSizeOf(const std::vector<loco::Node *> &nodes)
73 {
74   size_t total_byte_size = 0;
75
76   for (const auto node : nodes)
77   {
78     const auto input_node = loco::must_cast<const luci::CircleInput *>(node);
79     size_t byte_size = loco::size(input_node->dtype());
80
81     for (uint32_t index = 0; index < input_node->rank(); index++)
82     {
83       byte_size *= input_node->dim(index).value();
84     }
85
86     total_byte_size += byte_size;
87   }
88
89   return total_byte_size;
90 }
91
92 } // namespace circle_eval_diff
93
94 namespace circle_eval_diff
95 {
96
97 HDF5Loader::HDF5Loader(const std::string &file_path, const std::vector<loco::Node *> &input_nodes)
98   : _input_nodes{input_nodes}
99 {
100   try
101   {
102     using HDF5Importer = dio::hdf5::HDF5Importer;
103
104     _hdf5 = std::make_unique<HDF5Importer>(file_path);
105     _hdf5->importGroup("value");
106   }
107   catch (const H5::Exception &e)
108   {
109     H5::Exception::printErrorStack();
110     throw std::runtime_error("HDF5 error occurred.");
111   }
112 }
113
114 uint32_t HDF5Loader::size(void) const { return _hdf5->numData(); }
115
116 InputDataLoader::Data HDF5Loader::get(uint32_t data_idx) const
117 {
118   Data data;
119   data.resize(_input_nodes.size());
120
121   for (uint32_t input_idx = 0; input_idx < _input_nodes.size(); input_idx++)
122   {
123     auto input_node = loco::must_cast<luci::CircleInput *>(_input_nodes.at(input_idx));
124     assert(input_node->index() == input_idx);
125
126     data.at(input_idx) = *createEmptyTensor(input_node).get();
127
128     auto input_buffer = data.at(input_idx).buffer();
129     const auto input_buffer_bytes = data.at(input_idx).byte_size();
130
131     try
132     {
133       if (_hdf5->isRawData())
134       {
135         _hdf5->readTensor(data_idx, input_idx, input_buffer, input_buffer_bytes);
136       }
137       else
138       {
139         DataType dtype;
140         Shape shape;
141         _hdf5->readTensor(data_idx, input_idx, &dtype, &shape, input_buffer, input_buffer_bytes);
142
143         // Check the type and the shape of the input data is valid
144         verifyTypeShape(input_node, dtype, shape);
145       }
146     }
147     catch (const H5::Exception &e)
148     {
149       H5::Exception::printErrorStack();
150       throw std::runtime_error("HDF5 error occurred.");
151     }
152   }
153
154   return data;
155 }
156
157 DirectoryLoader::DirectoryLoader(const std::string &dir_path,
158                                  const std::vector<loco::Node *> &input_nodes)
159   : _input_nodes{input_nodes}
160 {
161   DIR *dir = opendir(dir_path.c_str());
162   if (not dir)
163   {
164     throw std::runtime_error("Cannot open directory \"" + dir_path + "\".");
165   }
166
167   struct dirent *entry = nullptr;
168   const auto input_total_bytes = getTotalByteSizeOf(input_nodes);
169   while ((entry = readdir(dir)))
170   {
171     // Skip if the entry is not a regular file
172     if (entry->d_type != DT_REG)
173       continue;
174
175     _data_paths.push_back(dir_path + "/" + entry->d_name);
176   }
177
178   closedir(dir);
179 }
180
181 uint32_t DirectoryLoader::size(void) const { return _data_paths.size(); }
182
183 InputDataLoader::Data DirectoryLoader::get(uint32_t data_idx) const
184 {
185   // Read raw data
186   const auto input_total_bytes = getTotalByteSizeOf(_input_nodes);
187   std::vector<char> input_data(input_total_bytes);
188   const auto raw_data_path = _data_paths.at(data_idx);
189   std::ifstream fs(raw_data_path, std::ifstream::binary);
190
191   if (fs.fail())
192   {
193     throw std::runtime_error("Cannot open file \"" + raw_data_path + "\".");
194   }
195   if (fs.read(input_data.data(), input_total_bytes).fail())
196   {
197     throw std::runtime_error("Failed to read raw data from file \"" + raw_data_path + "\".");
198   }
199
200   // Make Tensor from raw data
201   auto input_data_cur = input_data.data();
202
203   Data data;
204   data.resize(_input_nodes.size());
205   std::vector<size_t> input_bytes = getEachByteSizeOf(_input_nodes);
206   for (uint32_t index = 0; index < _input_nodes.size(); index++)
207   {
208     const auto input_node = loco::must_cast<const luci::CircleInput *>(_input_nodes.at(index));
209     auto &tensor = data.at(index);
210     tensor = *createEmptyTensor(input_node).get();
211     auto buffer = tensor.buffer();
212     std::memcpy(buffer, input_data_cur, input_bytes.at(index));
213     input_data_cur += input_bytes.at(index);
214   }
215
216   return data;
217 }
218
219 std::unique_ptr<InputDataLoader> makeDataLoader(const std::string &file_path,
220                                                 const InputFormat &format,
221                                                 const std::vector<loco::Node *> &input_nodes)
222 {
223   switch (format)
224   {
225     case InputFormat::H5:
226     {
227       return std::make_unique<HDF5Loader>(file_path, input_nodes);
228     }
229     case InputFormat::DIR:
230     {
231       return std::make_unique<DirectoryLoader>(file_path, input_nodes);
232     }
233     default:
234       throw std::runtime_error{"Unsupported input format."};
235   }
236 }
237
238 } // namespace circle_eval_diff