arm_compute v17.10
[platform/upstream/armcl.git] / utils / GraphUtils.cpp
1 /*
2  * Copyright (c) 2017 ARM Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 #include "utils/GraphUtils.h"
26 #include "utils/Utils.h"
27
28 #ifdef ARM_COMPUTE_CL
29 #include "arm_compute/core/CL/OpenCL.h"
30 #include "arm_compute/runtime/CL/CLTensor.h"
31 #endif /* ARM_COMPUTE_CL */
32
33 #include "arm_compute/core/Error.h"
34 #include "arm_compute/core/PixelValue.h"
35 #include "libnpy/npy.hpp"
36
37 #include <random>
38 #include <sstream>
39
40 using namespace arm_compute::graph_utils;
41
42 PPMWriter::PPMWriter(std::string name, unsigned int maximum)
43     : _name(std::move(name)), _iterator(0), _maximum(maximum)
44 {
45 }
46
47 bool PPMWriter::access_tensor(ITensor &tensor)
48 {
49     std::stringstream ss;
50     ss << _name << _iterator << ".ppm";
51     if(dynamic_cast<Tensor *>(&tensor) != nullptr)
52     {
53         arm_compute::utils::save_to_ppm(dynamic_cast<Tensor &>(tensor), ss.str());
54     }
55 #ifdef ARM_COMPUTE_CL
56     else if(dynamic_cast<CLTensor *>(&tensor) != nullptr)
57     {
58         arm_compute::utils::save_to_ppm(dynamic_cast<CLTensor &>(tensor), ss.str());
59     }
60 #endif /* ARM_COMPUTE_CL */
61
62     _iterator++;
63     if(_maximum == 0)
64     {
65         return true;
66     }
67     return _iterator < _maximum;
68 }
69
70 DummyAccessor::DummyAccessor(unsigned int maximum)
71     : _iterator(0), _maximum(maximum)
72 {
73 }
74
75 bool DummyAccessor::access_tensor(ITensor &tensor)
76 {
77     ARM_COMPUTE_UNUSED(tensor);
78     bool ret = _maximum == 0 || _iterator < _maximum;
79     if(_iterator == _maximum)
80     {
81         _iterator = 0;
82     }
83     else
84     {
85         _iterator++;
86     }
87     return ret;
88 }
89
90 RandomAccessor::RandomAccessor(PixelValue lower, PixelValue upper, std::random_device::result_type seed)
91     : _lower(lower), _upper(upper), _seed(seed)
92 {
93 }
94
95 template <typename T, typename D>
96 void RandomAccessor::fill(ITensor &tensor, D &&distribution)
97 {
98     std::mt19937 gen(_seed);
99
100     if(tensor.info()->padding().empty())
101     {
102         for(size_t offset = 0; offset < tensor.info()->total_size(); offset += tensor.info()->element_size())
103         {
104             const T value                                    = distribution(gen);
105             *reinterpret_cast<T *>(tensor.buffer() + offset) = value;
106         }
107     }
108     else
109     {
110         // If tensor has padding accessing tensor elements through execution window.
111         Window window;
112         window.use_tensor_dimensions(tensor.info()->tensor_shape());
113
114         execute_window_loop(window, [&](const Coordinates & id)
115         {
116             const T value                                     = distribution(gen);
117             *reinterpret_cast<T *>(tensor.ptr_to_element(id)) = value;
118         });
119     }
120 }
121
122 bool RandomAccessor::access_tensor(ITensor &tensor)
123 {
124     switch(tensor.info()->data_type())
125     {
126         case DataType::U8:
127         {
128             std::uniform_int_distribution<uint8_t> distribution_u8(_lower.get<uint8_t>(), _upper.get<uint8_t>());
129             fill<uint8_t>(tensor, distribution_u8);
130             break;
131         }
132         case DataType::S8:
133         case DataType::QS8:
134         {
135             std::uniform_int_distribution<int8_t> distribution_s8(_lower.get<int8_t>(), _upper.get<int8_t>());
136             fill<int8_t>(tensor, distribution_s8);
137             break;
138         }
139         case DataType::U16:
140         {
141             std::uniform_int_distribution<uint16_t> distribution_u16(_lower.get<uint16_t>(), _upper.get<uint16_t>());
142             fill<uint16_t>(tensor, distribution_u16);
143             break;
144         }
145         case DataType::S16:
146         case DataType::QS16:
147         {
148             std::uniform_int_distribution<int16_t> distribution_s16(_lower.get<int16_t>(), _upper.get<int16_t>());
149             fill<int16_t>(tensor, distribution_s16);
150             break;
151         }
152         case DataType::U32:
153         {
154             std::uniform_int_distribution<uint32_t> distribution_u32(_lower.get<uint32_t>(), _upper.get<uint32_t>());
155             fill<uint32_t>(tensor, distribution_u32);
156             break;
157         }
158         case DataType::S32:
159         {
160             std::uniform_int_distribution<int32_t> distribution_s32(_lower.get<int32_t>(), _upper.get<int32_t>());
161             fill<int32_t>(tensor, distribution_s32);
162             break;
163         }
164         case DataType::U64:
165         {
166             std::uniform_int_distribution<uint64_t> distribution_u64(_lower.get<uint64_t>(), _upper.get<uint64_t>());
167             fill<uint64_t>(tensor, distribution_u64);
168             break;
169         }
170         case DataType::S64:
171         {
172             std::uniform_int_distribution<int64_t> distribution_s64(_lower.get<int64_t>(), _upper.get<int64_t>());
173             fill<int64_t>(tensor, distribution_s64);
174             break;
175         }
176         case DataType::F16:
177         {
178             std::uniform_real_distribution<float> distribution_f16(_lower.get<float>(), _upper.get<float>());
179             fill<float>(tensor, distribution_f16);
180             break;
181         }
182         case DataType::F32:
183         {
184             std::uniform_real_distribution<float> distribution_f32(_lower.get<float>(), _upper.get<float>());
185             fill<float>(tensor, distribution_f32);
186             break;
187         }
188         case DataType::F64:
189         {
190             std::uniform_real_distribution<double> distribution_f64(_lower.get<double>(), _upper.get<double>());
191             fill<double>(tensor, distribution_f64);
192             break;
193         }
194         default:
195             ARM_COMPUTE_ERROR("NOT SUPPORTED!");
196     }
197     return true;
198 }
199
200 NumPyBinLoader::NumPyBinLoader(std::string filename)
201     : _filename(std::move(filename))
202 {
203 }
204
205 bool NumPyBinLoader::access_tensor(ITensor &tensor)
206 {
207     const TensorShape          tensor_shape = tensor.info()->tensor_shape();
208     std::vector<unsigned long> shape;
209
210     // Open file
211     std::ifstream stream(_filename, std::ios::in | std::ios::binary);
212     ARM_COMPUTE_ERROR_ON_MSG(!stream.good(), "Failed to load binary data");
213     // Check magic bytes and version number
214     unsigned char v_major = 0;
215     unsigned char v_minor = 0;
216     npy::read_magic(stream, &v_major, &v_minor);
217
218     // Read header
219     std::string header;
220     if(v_major == 1 && v_minor == 0)
221     {
222         header = npy::read_header_1_0(stream);
223     }
224     else if(v_major == 2 && v_minor == 0)
225     {
226         header = npy::read_header_2_0(stream);
227     }
228     else
229     {
230         ARM_COMPUTE_ERROR("Unsupported file format version");
231     }
232
233     // Parse header
234     bool        fortran_order = false;
235     std::string typestr;
236     npy::ParseHeader(header, typestr, &fortran_order, shape);
237
238     // Check if the typestring matches the given one
239     std::string expect_typestr = arm_compute::utils::get_typestring(tensor.info()->data_type());
240     ARM_COMPUTE_ERROR_ON_MSG(typestr != expect_typestr, "Typestrings mismatch");
241
242     // Validate tensor shape
243     ARM_COMPUTE_ERROR_ON_MSG(shape.size() != tensor_shape.num_dimensions(), "Tensor ranks mismatch");
244     if(fortran_order)
245     {
246         for(size_t i = 0; i < shape.size(); ++i)
247         {
248             ARM_COMPUTE_ERROR_ON_MSG(tensor_shape[i] != shape[i], "Tensor dimensions mismatch");
249         }
250     }
251     else
252     {
253         for(size_t i = 0; i < shape.size(); ++i)
254         {
255             ARM_COMPUTE_ERROR_ON_MSG(tensor_shape[i] != shape[shape.size() - i - 1], "Tensor dimensions mismatch");
256         }
257     }
258
259     // Read data
260     if(tensor.info()->padding().empty())
261     {
262         // If tensor has no padding read directly from stream.
263         stream.read(reinterpret_cast<char *>(tensor.buffer()), tensor.info()->total_size());
264     }
265     else
266     {
267         // If tensor has padding accessing tensor elements through execution window.
268         Window window;
269         window.use_tensor_dimensions(tensor_shape);
270
271         execute_window_loop(window, [&](const Coordinates & id)
272         {
273             stream.read(reinterpret_cast<char *>(tensor.ptr_to_element(id)), tensor.info()->element_size());
274         });
275     }
276     return true;
277 }