2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
34 uint32_t element_num(std::vector<hsize_t> &vec)
36 return static_cast<uint32_t>(
37 std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<uint32_t>()));
40 H5::PredType hdf5_dtype_cast(const loco::DataType loco_dtype)
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;
53 throw std::runtime_error("NYI data type.");
57 template <typename T> void geneate_random_data(std::mt19937 &gen, void *data, uint32_t size)
59 std::normal_distribution<float> distrib(0, 2); // mean(0), stddev(2)
60 for (uint32_t i = 0; i < size; i++)
62 static_cast<T *>(data)[i] = static_cast<T>(distrib(gen));
66 void fill_random_data(void *data, uint32_t size, loco::DataType dtype, uint32_t seed)
68 std::mt19937 gen(seed); // standard mersenne_twister_engine seeded with rd()
72 case loco::DataType::U8:
73 geneate_random_data<uint8_t>(gen, data, size);
75 case loco::DataType::S32:
76 geneate_random_data<int32_t>(gen, data, size);
78 case loco::DataType::S64:
79 geneate_random_data<int64_t>(gen, data, size);
81 case loco::DataType::FLOAT32:
82 geneate_random_data<float>(gen, data, size);
91 int entry(int argc, char **argv)
94 arser.add_argument("circle").type(arser::DataType::STR).help("Circle file you want to test");
95 arser.add_argument("--fixed_seed")
98 .help("Put a fixed seed into the random number generator");
102 arser.parse(argc, argv);
104 catch (const std::runtime_error &err)
106 std::cout << err.what() << std::endl;
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);
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)
121 std::cerr << "ERROR: Failed to load circle '" << circle_file << "'" << std::endl;
126 std::unique_ptr<luci::Module> module = luci::Importer().importModule(circle_model);
127 luci_interpreter::Interpreter interpreter(module.get());
130 * HDF5 layout is like below
135 * ㄴDATA (0): "input_01:0"
137 * ㄴDATA (0): "input_02:0"
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"));
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"));
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++)
161 const auto input_nodes = loco::input_nodes(module->graph(g));
162 for (const auto &node : input_nodes)
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)
170 H5::DataSpace name_dataspace(H5S_SCALAR);
171 H5::StrType name_datatype(H5::PredType::C_S1, name.size());
173 auto name_attr = input_name_group->createAttribute(std::to_string(input_index), name_datatype,
176 name_attr.write(name_datatype, name);
179 std::vector<hsize_t> dims(input_node->rank());
180 for (uint32_t d = 0; d < input_node->rank(); d++)
182 dims.at(d) = input_node->dim(d).value();
183 assert(dims.at(d) >= 0);
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));
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);
195 // generate random data
196 if (arser["--fixed_seed"])
197 fill_random_data(data.data(), data_size, input_node->dtype(), 0);
199 fill_random_data(data.data(), data_size, input_node->dtype(), rd());
201 dataset->write(data.data(), dtype);
203 interpreter.writeInputTensor(input_node, data.data(), byte_size);
209 interpreter.interpret();
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++)
215 const auto output_nodes = loco::output_nodes(module->graph(g));
216 for (const auto &node : output_nodes)
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)
224 H5::DataSpace name_dataspace(H5S_SCALAR);
225 H5::StrType name_datatype(H5::PredType::C_S1, name.size());
227 auto name_attr = output_name_group->createAttribute(std::to_string(output_index),
228 name_datatype, name_dataspace);
230 name_attr.write(name_datatype, name);
233 std::vector<hsize_t> dims(output_node->rank());
234 for (uint32_t d = 0; d < output_node->rank(); d++)
236 dims.at(d) = output_node->dim(d).value();
237 assert(dims.at(d) >= 0);
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));
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());
249 dataset->write(output_data.data(), dtype);