Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / compiler / luci-eval-driver / src / EvalDriver.cpp
1 /*
2  * Copyright (c) 2021 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 <luci/ImporterEx.h>
18 #include <luci_interpreter/Interpreter.h>
19
20 #include <cstdlib>
21 #include <fstream>
22 #include <iostream>
23 #include <vector>
24 #include <string>
25
26 namespace
27 {
28
29 void readDataFromFile(const std::string &filename, char *data, size_t data_size)
30 {
31   std::ifstream fs(filename, std::ifstream::binary);
32   if (fs.fail())
33     throw std::runtime_error("Cannot open file \"" + filename + "\".\n");
34   if (fs.read(data, data_size).fail())
35     throw std::runtime_error("Failed to read data from file \"" + filename + "\".\n");
36 }
37
38 void writeDataToFile(const std::string &filename, const char *data, size_t data_size)
39 {
40   std::ofstream fs(filename, std::ofstream::binary);
41   if (fs.fail())
42     throw std::runtime_error("Cannot open file \"" + filename + "\".\n");
43   if (fs.write(data, data_size).fail())
44   {
45     throw std::runtime_error("Failed to write data to file \"" + filename + "\".\n");
46   }
47 }
48
49 template <typename NodeT> size_t getTensorSize(const NodeT *node)
50 {
51   uint32_t tensor_size = loco::size(node->dtype());
52   for (uint32_t i = 0; i < node->rank(); ++i)
53     tensor_size *= node->dim(i).value();
54   return tensor_size;
55 }
56
57 } // namespace
58
59 /*
60  * @brief EvalDriver main
61  *
62  *        Driver for testing luci-inerpreter
63  *
64  */
65 int entry(int argc, char **argv)
66 {
67   if (argc != 5)
68   {
69     std::cerr
70       << "Usage: " << argv[0]
71       << " <path/to/circle/model> <num_inputs> <path/to/input/prefix> <path/to/output/file>\n";
72     return EXIT_FAILURE;
73   }
74
75   const char *filename = argv[1];
76   const int32_t num_inputs = atoi(argv[2]);
77   const char *input_prefix = argv[3];
78   const char *output_file = argv[4];
79
80   // Load model from the file
81   luci::ImporterEx importer;
82   std::unique_ptr<luci::Module> module = importer.importVerifyModule(filename);
83   if (module == nullptr)
84   {
85     std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl;
86     return EXIT_FAILURE;
87   }
88
89   // Create interpreter.
90   luci_interpreter::Interpreter interpreter(module.get());
91
92   // Set input.
93   // Data for n'th input is read from ${input_prefix}n
94   // (ex: Add.circle.input0, Add.circle.input1 ..)
95   const auto input_nodes = loco::input_nodes(module->graph());
96   if (num_inputs != input_nodes.size())
97   {
98     // NOTE using num_inputs is actually unnecessary but is kept to preserve interface.
99     std::cerr << "ERROR: invalid num_inputs value; should be " << input_nodes.size() << std::endl;
100     return EXIT_FAILURE;
101   }
102   for (int32_t i = 0; i < num_inputs; i++)
103   {
104     const auto *input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[i]);
105     std::vector<char> input_data(getTensorSize(input_node));
106     readDataFromFile(std::string(input_prefix) + std::to_string(i), input_data.data(),
107                      input_data.size());
108     interpreter.writeInputTensor(input_node, input_data.data(), input_data.size());
109   }
110
111   // Do inference.
112   interpreter.interpret();
113
114   // Get output.
115   const auto output_nodes = loco::output_nodes(module->graph());
116   for (int i = 0; i < module->graph()->outputs()->size(); i++)
117   {
118     const auto *output_node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
119     std::vector<char> output_data(getTensorSize(output_node));
120     interpreter.readOutputTensor(output_node, output_data.data(), output_data.size());
121
122     // Output data is written in ${output_file}
123     // (ex: Add.circle.output0)
124     // Output shape is written in ${output_file}.shape
125     // (ex: Add.circle.output0.shape)
126     writeDataToFile(std::string(output_file) + std::to_string(i), output_data.data(),
127                     output_data.size());
128     // In case of Tensor output is Scalar value.
129     // The output tensor with rank 0 is treated as a scalar with shape (1)
130     if (output_node->rank() == 0)
131     {
132       writeDataToFile(std::string(output_file) + std::to_string(i) + ".shape", "1", 1);
133     }
134     else
135     {
136       auto shape_str = std::to_string(output_node->dim(0).value());
137       for (int j = 1; j < output_node->rank(); j++)
138       {
139         shape_str += ",";
140         shape_str += std::to_string(output_node->dim(j).value());
141       }
142       writeDataToFile(std::string(output_file) + std::to_string(i) + ".shape", shape_str.c_str(),
143                       shape_str.size());
144     }
145   }
146   return EXIT_SUCCESS;
147 }