Imported Upstream version 1.8.0
[platform/core/ml/nnfw.git] / compiler / common-artifacts / src / TestDataGenerator.cpp
1 /*
2  * Copyright (c) 2020 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 <arser/arser.h>
18 #include <foder/FileLoader.h>
19 #include <luci/Importer.h>
20 #include <luci_interpreter/Interpreter.h>
21 #include <mio/circle/schema_generated.h>
22
23 #include <H5Cpp.h>
24
25 #include <algorithm>
26 #include <iostream>
27 #include <memory>
28 #include <random>
29 #include <string>
30
31 namespace
32 {
33
34 uint32_t element_num(std::vector<hsize_t> &vec)
35 {
36   return static_cast<uint32_t>(
37       std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<uint32_t>()));
38 }
39
40 H5::PredType hdf5_dtype_cast(const loco::DataType loco_dtype)
41 {
42   switch (loco_dtype)
43   {
44     case loco::DataType::U8:
45       return H5::PredType::NATIVE_UINT8;
46     case loco::DataType::S32:
47       return H5::PredType::NATIVE_INT32;
48     case loco::DataType::S64:
49       return H5::PredType::NATIVE_INT64;
50     case loco::DataType::FLOAT32:
51       return H5::PredType::NATIVE_FLOAT;
52     default:
53       throw std::runtime_error("NYI data type.");
54   }
55 }
56
57 template <typename T> void geneate_random_data(std::mt19937 &gen, void *data, uint32_t size)
58 {
59   std::normal_distribution<float> distrib(0, 2); // mean(0), stddev(2)
60   for (uint32_t i = 0; i < size; i++)
61   {
62     static_cast<T *>(data)[i] = static_cast<T>(distrib(gen));
63   }
64 }
65
66 void fill_random_data(void *data, uint32_t size, loco::DataType dtype, uint32_t seed)
67 {
68   std::mt19937 gen(seed); // standard mersenne_twister_engine seeded with rd()
69
70   switch (dtype)
71   {
72     case loco::DataType::U8:
73       geneate_random_data<uint8_t>(gen, data, size);
74       break;
75     case loco::DataType::S32:
76       geneate_random_data<int32_t>(gen, data, size);
77       break;
78     case loco::DataType::S64:
79       geneate_random_data<int64_t>(gen, data, size);
80       break;
81     case loco::DataType::FLOAT32:
82       geneate_random_data<float>(gen, data, size);
83       break;
84     default:
85       break;
86   }
87 }
88
89 } // namespace
90
91 int entry(int argc, char **argv)
92 {
93   arser::Arser arser;
94   arser.add_argument("circle").type(arser::DataType::STR).help("Circle file you want to test");
95   arser.add_argument("--fixed_seed")
96       .required(false)
97       .nargs(0)
98       .help("Put a fixed seed into the random number generator");
99
100   try
101   {
102     arser.parse(argc, argv);
103   }
104   catch (const std::runtime_error &err)
105   {
106     std::cout << err.what() << std::endl;
107     std::cout << arser;
108     return 255;
109   }
110
111   std::string circle_file = arser.get<std::string>("circle");
112   size_t last_dot_index = circle_file.find_last_of(".");
113   std::string prefix = circle_file.substr(0, last_dot_index);
114
115   // load circle file
116   foder::FileLoader file_loader{circle_file};
117   std::vector<char> model_data = file_loader.load();
118   const circle::Model *circle_model = circle::GetModel(model_data.data());
119   if (circle_model == nullptr)
120   {
121     std::cerr << "ERROR: Failed to load circle '" << circle_file << "'" << std::endl;
122     return EXIT_FAILURE;
123   }
124
125   // load luci module
126   std::unique_ptr<luci::Module> module = luci::Importer().importModule(circle_model);
127   luci_interpreter::Interpreter interpreter(module.get());
128
129   /**
130    *  HDF5 layout is like below
131    *
132    *  GROUP "/"
133    *   ㄴGROUP "name"
134    *     ㄴATTRIBUTE "0"
135    *       ㄴDATA (0): "input_01:0"
136    *     ㄴATTRIBUTE "1"
137    *       ㄴDATA (0): "input_02:0"
138    *   ㄴGROUP "value"
139    *     ㄴDATASET "0"
140    *       ㄴDATA ...
141    *     ㄴDATASET "1"
142    *       ㄴDATA ...
143    */
144   // create random data and dump into hdf5 file
145   H5::H5File input_file{prefix + ".input.h5", H5F_ACC_TRUNC};
146   std::unique_ptr<H5::Group> input_name_group =
147       std::make_unique<H5::Group>(input_file.createGroup("name"));
148   std::unique_ptr<H5::Group> input_value_group =
149       std::make_unique<H5::Group>(input_file.createGroup("value"));
150
151   H5::H5File output_file{prefix + ".expected.h5", H5F_ACC_TRUNC};
152   std::unique_ptr<H5::Group> output_name_group =
153       std::make_unique<H5::Group>(output_file.createGroup("name"));
154   std::unique_ptr<H5::Group> output_value_group =
155       std::make_unique<H5::Group>(output_file.createGroup("value"));
156
157   std::random_device rd; // used to obtain a seed for the random number engine
158   uint32_t input_index = 0;
159   for (uint32_t g = 0; g < circle_model->subgraphs()->size(); g++)
160   {
161     const auto input_nodes = loco::input_nodes(module->graph(g));
162     for (const auto &node : input_nodes)
163     {
164       const auto *input_node = dynamic_cast<const luci::CircleInput *>(node);
165       std::string name = input_node->name();
166       if (name.find(":") == std::string::npos)
167         name += ":0";
168
169       // create attribute
170       H5::DataSpace name_dataspace(H5S_SCALAR);
171       H5::StrType name_datatype(H5::PredType::C_S1, name.size());
172
173       auto name_attr = input_name_group->createAttribute(std::to_string(input_index), name_datatype,
174                                                          name_dataspace);
175
176       name_attr.write(name_datatype, name);
177
178       // create value
179       std::vector<hsize_t> dims(input_node->rank());
180       for (uint32_t d = 0; d < input_node->rank(); d++)
181       {
182         dims.at(d) = input_node->dim(d).value();
183         assert(dims.at(d) >= 0);
184       }
185       auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data());
186       auto dtype = hdf5_dtype_cast(input_node->dtype());
187       auto dataset = std::make_unique<H5::DataSet>(
188           input_file.createDataSet("value/" + std::to_string(input_index), dtype, *dataspace));
189
190       auto data_size = ::element_num(dims);
191       auto dtype_size = loco::size(input_node->dtype());
192       auto byte_size = dtype_size * data_size;
193       std::vector<int8_t> data(byte_size);
194
195       // generate random data
196       if (arser["--fixed_seed"])
197         fill_random_data(data.data(), data_size, input_node->dtype(), 0);
198       else
199         fill_random_data(data.data(), data_size, input_node->dtype(), rd());
200
201       dataset->write(data.data(), dtype);
202
203       interpreter.writeInputTensor(input_node, data.data(), byte_size);
204
205       input_index++;
206     }
207   }
208
209   interpreter.interpret();
210
211   // dump output data into hdf5 file
212   uint32_t output_index = 0;
213   for (uint32_t g = 0; g < circle_model->subgraphs()->size(); g++)
214   {
215     const auto output_nodes = loco::output_nodes(module->graph(g));
216     for (const auto &node : output_nodes)
217     {
218       const auto *output_node = dynamic_cast<const luci::CircleOutput *>(node);
219       std::string name = output_node->name();
220       if (name.find(":") == std::string::npos)
221         name += ":0";
222
223       // create attribute
224       H5::DataSpace name_dataspace(H5S_SCALAR);
225       H5::StrType name_datatype(H5::PredType::C_S1, name.size());
226
227       auto name_attr = output_name_group->createAttribute(std::to_string(output_index),
228                                                           name_datatype, name_dataspace);
229
230       name_attr.write(name_datatype, name);
231
232       // create value
233       std::vector<hsize_t> dims(output_node->rank());
234       for (uint32_t d = 0; d < output_node->rank(); d++)
235       {
236         dims.at(d) = output_node->dim(d).value();
237         assert(dims.at(d) >= 0);
238       }
239       auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data());
240       auto dtype = hdf5_dtype_cast(output_node->dtype());
241       auto dataset = std::make_unique<H5::DataSet>(
242           output_file.createDataSet("value/" + std::to_string(output_index), dtype, *dataspace));
243
244       uint32_t tensor_bytesize = loco::size(output_node->dtype());
245       tensor_bytesize *= ::element_num(dims);
246       std::vector<int8_t> output_data(tensor_bytesize);
247       interpreter.readOutputTensor(output_node, output_data.data(), output_data.size());
248
249       dataset->write(output_data.data(), dtype);
250
251       output_index++;
252     }
253   }
254
255   return EXIT_SUCCESS;
256 }