f15b2ba91e3efd388b9e7f8a6e287f3e86b98cd1
[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     try
130     {
131       if (_hdf5->isRawData())
132       {
133         _hdf5->readTensor(data_idx, input_idx, input_buffer);
134       }
135       else
136       {
137         DataType dtype;
138         Shape shape;
139         _hdf5->readTensor(data_idx, input_idx, &dtype, &shape, input_buffer);
140
141         // Check the type and the shape of the input data is valid
142         verifyTypeShape(input_node, dtype, shape);
143       }
144     }
145     catch (const H5::Exception &e)
146     {
147       H5::Exception::printErrorStack();
148       throw std::runtime_error("HDF5 error occurred.");
149     }
150   }
151
152   return data;
153 }
154
155 DirectoryLoader::DirectoryLoader(const std::string &dir_path,
156                                  const std::vector<loco::Node *> &input_nodes)
157   : _input_nodes{input_nodes}
158 {
159   DIR *dir = opendir(dir_path.c_str());
160   if (not dir)
161   {
162     throw std::runtime_error("Cannot open directory \"" + dir_path + "\".");
163   }
164
165   struct dirent *entry = nullptr;
166   const auto input_total_bytes = getTotalByteSizeOf(input_nodes);
167   while (entry = readdir(dir))
168   {
169     // Skip if the entry is not a regular file
170     if (entry->d_type != DT_REG)
171       continue;
172
173     _data_paths.push_back(dir_path + "/" + entry->d_name);
174   }
175
176   closedir(dir);
177 }
178
179 uint32_t DirectoryLoader::size(void) const { return _data_paths.size(); }
180
181 InputDataLoader::Data DirectoryLoader::get(uint32_t data_idx) const
182 {
183   // Read raw data
184   const auto input_total_bytes = getTotalByteSizeOf(_input_nodes);
185   std::vector<char> input_data(input_total_bytes);
186   const auto raw_data_path = _data_paths.at(data_idx);
187   std::ifstream fs(raw_data_path, std::ifstream::binary);
188
189   if (fs.fail())
190   {
191     throw std::runtime_error("Cannot open file \"" + raw_data_path + "\".");
192   }
193   if (fs.read(input_data.data(), input_total_bytes).fail())
194   {
195     throw std::runtime_error("Failed to read raw data from file \"" + raw_data_path + "\".");
196   }
197
198   // Make Tensor from raw data
199   auto input_data_cur = input_data.data();
200
201   Data data;
202   data.resize(_input_nodes.size());
203   std::vector<size_t> input_bytes = getEachByteSizeOf(_input_nodes);
204   for (uint32_t index = 0; index < _input_nodes.size(); index++)
205   {
206     const auto input_node = loco::must_cast<const luci::CircleInput *>(_input_nodes.at(index));
207     auto &tensor = data.at(index);
208     tensor = *createEmptyTensor(input_node).get();
209     auto buffer = tensor.buffer();
210     std::memcpy(buffer, input_data_cur, input_bytes.at(index));
211     input_data_cur += input_bytes.at(index);
212   }
213
214   return data;
215 }
216
217 std::unique_ptr<InputDataLoader> makeDataLoader(const std::string &file_path,
218                                                 const InputFormat &format,
219                                                 const std::vector<loco::Node *> &input_nodes)
220 {
221   switch (format)
222   {
223     case InputFormat::H5:
224     {
225       return std::make_unique<HDF5Loader>(file_path, input_nodes);
226     }
227     case InputFormat::DIR:
228     {
229       return std::make_unique<DirectoryLoader>(file_path, input_nodes);
230     }
231     default:
232       throw std::runtime_error{"Unsupported input format."};
233   }
234 }
235
236 } // namespace circle_eval_diff