1*c217d954SCole Faust /*
2*c217d954SCole Faust * Copyright (c) 2016-2023 Arm Limited.
3*c217d954SCole Faust *
4*c217d954SCole Faust * SPDX-License-Identifier: MIT
5*c217d954SCole Faust *
6*c217d954SCole Faust * Permission is hereby granted, free of charge, to any person obtaining a copy
7*c217d954SCole Faust * of this software and associated documentation files (the "Software"), to
8*c217d954SCole Faust * deal in the Software without restriction, including without limitation the
9*c217d954SCole Faust * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10*c217d954SCole Faust * sell copies of the Software, and to permit persons to whom the Software is
11*c217d954SCole Faust * furnished to do so, subject to the following conditions:
12*c217d954SCole Faust *
13*c217d954SCole Faust * The above copyright notice and this permission notice shall be included in all
14*c217d954SCole Faust * copies or substantial portions of the Software.
15*c217d954SCole Faust *
16*c217d954SCole Faust * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*c217d954SCole Faust * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*c217d954SCole Faust * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19*c217d954SCole Faust * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*c217d954SCole Faust * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21*c217d954SCole Faust * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22*c217d954SCole Faust * SOFTWARE.
23*c217d954SCole Faust */
24*c217d954SCole Faust #ifndef __UTILS_UTILS_H__
25*c217d954SCole Faust #define __UTILS_UTILS_H__
26*c217d954SCole Faust
27*c217d954SCole Faust /** @dir .
28*c217d954SCole Faust * brief Boiler plate code used by examples. Various utilities to print types, load / store assets, etc.
29*c217d954SCole Faust */
30*c217d954SCole Faust
31*c217d954SCole Faust #include "arm_compute/core/Helpers.h"
32*c217d954SCole Faust #include "arm_compute/core/ITensor.h"
33*c217d954SCole Faust #include "arm_compute/core/Types.h"
34*c217d954SCole Faust #include "arm_compute/core/Window.h"
35*c217d954SCole Faust #include "arm_compute/runtime/Tensor.h"
36*c217d954SCole Faust #pragma GCC diagnostic push
37*c217d954SCole Faust #pragma GCC diagnostic ignored "-Wunused-parameter"
38*c217d954SCole Faust #pragma GCC diagnostic ignored "-Wstrict-overflow"
39*c217d954SCole Faust #include "libnpy/npy.hpp"
40*c217d954SCole Faust #pragma GCC diagnostic pop
41*c217d954SCole Faust #include "support/StringSupport.h"
42*c217d954SCole Faust
43*c217d954SCole Faust #ifdef ARM_COMPUTE_CL
44*c217d954SCole Faust #include "arm_compute/core/CL/OpenCL.h"
45*c217d954SCole Faust #include "arm_compute/runtime/CL/CLTensor.h"
46*c217d954SCole Faust #endif /* ARM_COMPUTE_CL */
47*c217d954SCole Faust
48*c217d954SCole Faust #include <cstdlib>
49*c217d954SCole Faust #include <cstring>
50*c217d954SCole Faust #include <fstream>
51*c217d954SCole Faust #include <iostream>
52*c217d954SCole Faust #include <memory>
53*c217d954SCole Faust #include <random>
54*c217d954SCole Faust #include <string>
55*c217d954SCole Faust #include <tuple>
56*c217d954SCole Faust #include <vector>
57*c217d954SCole Faust
58*c217d954SCole Faust namespace arm_compute
59*c217d954SCole Faust {
60*c217d954SCole Faust namespace utils
61*c217d954SCole Faust {
62*c217d954SCole Faust /** Supported image types */
63*c217d954SCole Faust enum class ImageType
64*c217d954SCole Faust {
65*c217d954SCole Faust UNKNOWN,
66*c217d954SCole Faust PPM,
67*c217d954SCole Faust JPEG
68*c217d954SCole Faust };
69*c217d954SCole Faust
70*c217d954SCole Faust /** Abstract Example class.
71*c217d954SCole Faust *
72*c217d954SCole Faust * All examples have to inherit from this class.
73*c217d954SCole Faust */
74*c217d954SCole Faust class Example
75*c217d954SCole Faust {
76*c217d954SCole Faust public:
77*c217d954SCole Faust /** Setup the example.
78*c217d954SCole Faust *
79*c217d954SCole Faust * @param[in] argc Argument count.
80*c217d954SCole Faust * @param[in] argv Argument values.
81*c217d954SCole Faust *
82*c217d954SCole Faust * @return True in case of no errors in setup else false
83*c217d954SCole Faust */
do_setup(int argc,char ** argv)84*c217d954SCole Faust virtual bool do_setup(int argc, char **argv)
85*c217d954SCole Faust {
86*c217d954SCole Faust ARM_COMPUTE_UNUSED(argc, argv);
87*c217d954SCole Faust return true;
88*c217d954SCole Faust };
89*c217d954SCole Faust /** Run the example. */
do_run()90*c217d954SCole Faust virtual void do_run() {};
91*c217d954SCole Faust /** Teardown the example. */
do_teardown()92*c217d954SCole Faust virtual void do_teardown() {};
93*c217d954SCole Faust
94*c217d954SCole Faust /** Default destructor. */
95*c217d954SCole Faust virtual ~Example() = default;
96*c217d954SCole Faust };
97*c217d954SCole Faust
98*c217d954SCole Faust /** Run an example and handle the potential exceptions it throws
99*c217d954SCole Faust *
100*c217d954SCole Faust * @param[in] argc Number of command line arguments
101*c217d954SCole Faust * @param[in] argv Command line arguments
102*c217d954SCole Faust * @param[in] example Example to run
103*c217d954SCole Faust */
104*c217d954SCole Faust int run_example(int argc, char **argv, std::unique_ptr<Example> example);
105*c217d954SCole Faust
106*c217d954SCole Faust template <typename T>
run_example(int argc,char ** argv)107*c217d954SCole Faust int run_example(int argc, char **argv)
108*c217d954SCole Faust {
109*c217d954SCole Faust return run_example(argc, argv, std::make_unique<T>());
110*c217d954SCole Faust }
111*c217d954SCole Faust
112*c217d954SCole Faust /** Draw a RGB rectangular window for the detected object
113*c217d954SCole Faust *
114*c217d954SCole Faust * @param[in, out] tensor Input tensor where the rectangle will be drawn on. Format supported: RGB888
115*c217d954SCole Faust * @param[in] rect Geometry of the rectangular window
116*c217d954SCole Faust * @param[in] r Red colour to use
117*c217d954SCole Faust * @param[in] g Green colour to use
118*c217d954SCole Faust * @param[in] b Blue colour to use
119*c217d954SCole Faust */
120*c217d954SCole Faust void draw_detection_rectangle(arm_compute::ITensor *tensor, const arm_compute::DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b);
121*c217d954SCole Faust
122*c217d954SCole Faust /** Gets image type given a file
123*c217d954SCole Faust *
124*c217d954SCole Faust * @param[in] filename File to identify its image type
125*c217d954SCole Faust *
126*c217d954SCole Faust * @return Image type
127*c217d954SCole Faust */
128*c217d954SCole Faust ImageType get_image_type_from_file(const std::string &filename);
129*c217d954SCole Faust
130*c217d954SCole Faust /** Parse the ppm header from an input file stream. At the end of the execution,
131*c217d954SCole Faust * the file position pointer will be located at the first pixel stored in the ppm file
132*c217d954SCole Faust *
133*c217d954SCole Faust * @param[in] fs Input file stream to parse
134*c217d954SCole Faust *
135*c217d954SCole Faust * @return The width, height and max value stored in the header of the PPM file
136*c217d954SCole Faust */
137*c217d954SCole Faust std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs);
138*c217d954SCole Faust
139*c217d954SCole Faust /** Parse the npy header from an input file stream. At the end of the execution,
140*c217d954SCole Faust * the file position pointer will be located at the first pixel stored in the npy file //TODO
141*c217d954SCole Faust *
142*c217d954SCole Faust * @param[in] fs Input file stream to parse
143*c217d954SCole Faust *
144*c217d954SCole Faust * @return The width and height stored in the header of the NPY file
145*c217d954SCole Faust */
146*c217d954SCole Faust npy::header_t parse_npy_header(std::ifstream &fs);
147*c217d954SCole Faust
148*c217d954SCole Faust /** Obtain numpy type string from DataType.
149*c217d954SCole Faust *
150*c217d954SCole Faust * @param[in] data_type Data type.
151*c217d954SCole Faust *
152*c217d954SCole Faust * @return numpy type string.
153*c217d954SCole Faust */
get_typestring(DataType data_type)154*c217d954SCole Faust inline std::string get_typestring(DataType data_type)
155*c217d954SCole Faust {
156*c217d954SCole Faust // Check endianness
157*c217d954SCole Faust const unsigned int i = 1;
158*c217d954SCole Faust const char *c = reinterpret_cast<const char *>(&i);
159*c217d954SCole Faust std::string endianness;
160*c217d954SCole Faust if(*c == 1)
161*c217d954SCole Faust {
162*c217d954SCole Faust endianness = std::string("<");
163*c217d954SCole Faust }
164*c217d954SCole Faust else
165*c217d954SCole Faust {
166*c217d954SCole Faust endianness = std::string(">");
167*c217d954SCole Faust }
168*c217d954SCole Faust const std::string no_endianness("|");
169*c217d954SCole Faust
170*c217d954SCole Faust switch(data_type)
171*c217d954SCole Faust {
172*c217d954SCole Faust case DataType::U8:
173*c217d954SCole Faust case DataType::QASYMM8:
174*c217d954SCole Faust return no_endianness + "u" + support::cpp11::to_string(sizeof(uint8_t));
175*c217d954SCole Faust case DataType::S8:
176*c217d954SCole Faust case DataType::QSYMM8:
177*c217d954SCole Faust case DataType::QSYMM8_PER_CHANNEL:
178*c217d954SCole Faust return no_endianness + "i" + support::cpp11::to_string(sizeof(int8_t));
179*c217d954SCole Faust case DataType::U16:
180*c217d954SCole Faust case DataType::QASYMM16:
181*c217d954SCole Faust return endianness + "u" + support::cpp11::to_string(sizeof(uint16_t));
182*c217d954SCole Faust case DataType::S16:
183*c217d954SCole Faust case DataType::QSYMM16:
184*c217d954SCole Faust return endianness + "i" + support::cpp11::to_string(sizeof(int16_t));
185*c217d954SCole Faust case DataType::U32:
186*c217d954SCole Faust return endianness + "u" + support::cpp11::to_string(sizeof(uint32_t));
187*c217d954SCole Faust case DataType::S32:
188*c217d954SCole Faust return endianness + "i" + support::cpp11::to_string(sizeof(int32_t));
189*c217d954SCole Faust case DataType::U64:
190*c217d954SCole Faust return endianness + "u" + support::cpp11::to_string(sizeof(uint64_t));
191*c217d954SCole Faust case DataType::S64:
192*c217d954SCole Faust return endianness + "i" + support::cpp11::to_string(sizeof(int64_t));
193*c217d954SCole Faust case DataType::F16:
194*c217d954SCole Faust return endianness + "f" + support::cpp11::to_string(sizeof(half));
195*c217d954SCole Faust case DataType::F32:
196*c217d954SCole Faust return endianness + "f" + support::cpp11::to_string(sizeof(float));
197*c217d954SCole Faust case DataType::F64:
198*c217d954SCole Faust return endianness + "f" + support::cpp11::to_string(sizeof(double));
199*c217d954SCole Faust case DataType::SIZET:
200*c217d954SCole Faust return endianness + "u" + support::cpp11::to_string(sizeof(size_t));
201*c217d954SCole Faust default:
202*c217d954SCole Faust ARM_COMPUTE_ERROR("Data type not supported");
203*c217d954SCole Faust }
204*c217d954SCole Faust }
205*c217d954SCole Faust
206*c217d954SCole Faust /** Maps a tensor if needed
207*c217d954SCole Faust *
208*c217d954SCole Faust * @param[in] tensor Tensor to be mapped
209*c217d954SCole Faust * @param[in] blocking Specified if map is blocking or not
210*c217d954SCole Faust */
211*c217d954SCole Faust template <typename T>
map(T & tensor,bool blocking)212*c217d954SCole Faust inline void map(T &tensor, bool blocking)
213*c217d954SCole Faust {
214*c217d954SCole Faust ARM_COMPUTE_UNUSED(tensor);
215*c217d954SCole Faust ARM_COMPUTE_UNUSED(blocking);
216*c217d954SCole Faust }
217*c217d954SCole Faust
218*c217d954SCole Faust /** Unmaps a tensor if needed
219*c217d954SCole Faust *
220*c217d954SCole Faust * @param tensor Tensor to be unmapped
221*c217d954SCole Faust */
222*c217d954SCole Faust template <typename T>
unmap(T & tensor)223*c217d954SCole Faust inline void unmap(T &tensor)
224*c217d954SCole Faust {
225*c217d954SCole Faust ARM_COMPUTE_UNUSED(tensor);
226*c217d954SCole Faust }
227*c217d954SCole Faust
228*c217d954SCole Faust #ifdef ARM_COMPUTE_CL
229*c217d954SCole Faust /** Maps a tensor if needed
230*c217d954SCole Faust *
231*c217d954SCole Faust * @param[in] tensor Tensor to be mapped
232*c217d954SCole Faust * @param[in] blocking Specified if map is blocking or not
233*c217d954SCole Faust */
map(CLTensor & tensor,bool blocking)234*c217d954SCole Faust inline void map(CLTensor &tensor, bool blocking)
235*c217d954SCole Faust {
236*c217d954SCole Faust tensor.map(blocking);
237*c217d954SCole Faust }
238*c217d954SCole Faust
239*c217d954SCole Faust /** Unmaps a tensor if needed
240*c217d954SCole Faust *
241*c217d954SCole Faust * @param tensor Tensor to be unmapped
242*c217d954SCole Faust */
unmap(CLTensor & tensor)243*c217d954SCole Faust inline void unmap(CLTensor &tensor)
244*c217d954SCole Faust {
245*c217d954SCole Faust tensor.unmap();
246*c217d954SCole Faust }
247*c217d954SCole Faust #endif /* ARM_COMPUTE_CL */
248*c217d954SCole Faust
249*c217d954SCole Faust /** Specialized class to generate random non-zero FP16 values.
250*c217d954SCole Faust * uniform_real_distribution<half> generates values that get rounded off to zero, causing
251*c217d954SCole Faust * differences between ACL and reference implementation
252*c217d954SCole Faust */
253*c217d954SCole Faust template <typename T>
254*c217d954SCole Faust class uniform_real_distribution_16bit
255*c217d954SCole Faust {
256*c217d954SCole Faust static_assert(std::is_same<T, half>::value || std::is_same<T, bfloat16>::value, "Only half and bfloat16 data types supported");
257*c217d954SCole Faust
258*c217d954SCole Faust public:
259*c217d954SCole Faust using result_type = T;
260*c217d954SCole Faust /** Constructor
261*c217d954SCole Faust *
262*c217d954SCole Faust * @param[in] min Minimum value of the distribution
263*c217d954SCole Faust * @param[in] max Maximum value of the distribution
264*c217d954SCole Faust */
265*c217d954SCole Faust explicit uniform_real_distribution_16bit(float min = 0.f, float max = 1.0)
dist(min,max)266*c217d954SCole Faust : dist(min, max)
267*c217d954SCole Faust {
268*c217d954SCole Faust }
269*c217d954SCole Faust
270*c217d954SCole Faust /** () operator to generate next value
271*c217d954SCole Faust *
272*c217d954SCole Faust * @param[in] gen an uniform random bit generator object
273*c217d954SCole Faust */
operator()274*c217d954SCole Faust T operator()(std::mt19937 &gen)
275*c217d954SCole Faust {
276*c217d954SCole Faust return T(dist(gen));
277*c217d954SCole Faust }
278*c217d954SCole Faust
279*c217d954SCole Faust private:
280*c217d954SCole Faust std::uniform_real_distribution<float> dist;
281*c217d954SCole Faust };
282*c217d954SCole Faust
283*c217d954SCole Faust /** Numpy data loader */
284*c217d954SCole Faust class NPYLoader
285*c217d954SCole Faust {
286*c217d954SCole Faust public:
287*c217d954SCole Faust /** Default constructor */
NPYLoader()288*c217d954SCole Faust NPYLoader()
289*c217d954SCole Faust : _fs(), _shape(), _fortran_order(false), _typestring(), _file_layout(DataLayout::NCHW)
290*c217d954SCole Faust {
291*c217d954SCole Faust }
292*c217d954SCole Faust
293*c217d954SCole Faust /** Open a NPY file and reads its metadata
294*c217d954SCole Faust *
295*c217d954SCole Faust * @param[in] npy_filename File to open
296*c217d954SCole Faust * @param[in] file_layout (Optional) Layout in which the weights are stored in the file.
297*c217d954SCole Faust */
298*c217d954SCole Faust void open(const std::string &npy_filename, DataLayout file_layout = DataLayout::NCHW)
299*c217d954SCole Faust {
300*c217d954SCole Faust ARM_COMPUTE_ERROR_ON(is_open());
301*c217d954SCole Faust try
302*c217d954SCole Faust {
303*c217d954SCole Faust _fs.open(npy_filename, std::ios::in | std::ios::binary);
304*c217d954SCole Faust ARM_COMPUTE_EXIT_ON_MSG_VAR(!_fs.good(), "Failed to load binary data from %s", npy_filename.c_str());
305*c217d954SCole Faust _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
306*c217d954SCole Faust _file_layout = file_layout;
307*c217d954SCole Faust
308*c217d954SCole Faust npy::header_t header = parse_npy_header(_fs);
309*c217d954SCole Faust _shape = header.shape;
310*c217d954SCole Faust _fortran_order = header.fortran_order;
311*c217d954SCole Faust _typestring = header.dtype.str();
312*c217d954SCole Faust }
catch(const std::ifstream::failure & e)313*c217d954SCole Faust catch(const std::ifstream::failure &e)
314*c217d954SCole Faust {
315*c217d954SCole Faust ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", npy_filename.c_str(), e.what());
316*c217d954SCole Faust }
317*c217d954SCole Faust }
318*c217d954SCole Faust /** Return true if a NPY file is currently open */
is_open()319*c217d954SCole Faust bool is_open()
320*c217d954SCole Faust {
321*c217d954SCole Faust return _fs.is_open();
322*c217d954SCole Faust }
323*c217d954SCole Faust
324*c217d954SCole Faust /** Return true if a NPY file is in fortran order */
is_fortran()325*c217d954SCole Faust bool is_fortran()
326*c217d954SCole Faust {
327*c217d954SCole Faust return _fortran_order;
328*c217d954SCole Faust }
329*c217d954SCole Faust
330*c217d954SCole Faust /** Initialise the tensor's metadata with the dimensions of the NPY file currently open
331*c217d954SCole Faust *
332*c217d954SCole Faust * @param[out] tensor Tensor to initialise
333*c217d954SCole Faust * @param[in] dt Data type to use for the tensor
334*c217d954SCole Faust */
335*c217d954SCole Faust template <typename T>
init_tensor(T & tensor,arm_compute::DataType dt)336*c217d954SCole Faust void init_tensor(T &tensor, arm_compute::DataType dt)
337*c217d954SCole Faust {
338*c217d954SCole Faust ARM_COMPUTE_ERROR_ON(!is_open());
339*c217d954SCole Faust ARM_COMPUTE_ERROR_ON(dt != arm_compute::DataType::F32);
340*c217d954SCole Faust
341*c217d954SCole Faust // Use the size of the input NPY tensor
342*c217d954SCole Faust TensorShape shape;
343*c217d954SCole Faust shape.set_num_dimensions(_shape.size());
344*c217d954SCole Faust for(size_t i = 0; i < _shape.size(); ++i)
345*c217d954SCole Faust {
346*c217d954SCole Faust size_t src = i;
347*c217d954SCole Faust if(_fortran_order)
348*c217d954SCole Faust {
349*c217d954SCole Faust src = _shape.size() - 1 - i;
350*c217d954SCole Faust }
351*c217d954SCole Faust shape.set(i, _shape.at(src));
352*c217d954SCole Faust }
353*c217d954SCole Faust
354*c217d954SCole Faust arm_compute::TensorInfo tensor_info(shape, 1, dt);
355*c217d954SCole Faust tensor.allocator()->init(tensor_info);
356*c217d954SCole Faust }
357*c217d954SCole Faust
358*c217d954SCole Faust /** Fill a tensor with the content of the currently open NPY file.
359*c217d954SCole Faust *
360*c217d954SCole Faust * @note If the tensor is a CLTensor, the function maps and unmaps the tensor
361*c217d954SCole Faust *
362*c217d954SCole Faust * @param[in,out] tensor Tensor to fill (Must be allocated, and of matching dimensions with the opened NPY).
363*c217d954SCole Faust */
364*c217d954SCole Faust template <typename T>
fill_tensor(T & tensor)365*c217d954SCole Faust void fill_tensor(T &tensor)
366*c217d954SCole Faust {
367*c217d954SCole Faust ARM_COMPUTE_ERROR_ON(!is_open());
368*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_DATA_TYPE_NOT_IN(&tensor, arm_compute::DataType::QASYMM8, arm_compute::DataType::S32, arm_compute::DataType::F32, arm_compute::DataType::F16);
369*c217d954SCole Faust try
370*c217d954SCole Faust {
371*c217d954SCole Faust // Map buffer if creating a CLTensor
372*c217d954SCole Faust map(tensor, true);
373*c217d954SCole Faust
374*c217d954SCole Faust // Check if the file is large enough to fill the tensor
375*c217d954SCole Faust const size_t current_position = _fs.tellg();
376*c217d954SCole Faust _fs.seekg(0, std::ios_base::end);
377*c217d954SCole Faust const size_t end_position = _fs.tellg();
378*c217d954SCole Faust _fs.seekg(current_position, std::ios_base::beg);
379*c217d954SCole Faust
380*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size() * tensor.info()->element_size(),
381*c217d954SCole Faust "Not enough data in file");
382*c217d954SCole Faust ARM_COMPUTE_UNUSED(end_position);
383*c217d954SCole Faust
384*c217d954SCole Faust // Check if the typestring matches the given one
385*c217d954SCole Faust std::string expect_typestr = get_typestring(tensor.info()->data_type());
386*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_MSG(_typestring != expect_typestr, "Typestrings mismatch");
387*c217d954SCole Faust
388*c217d954SCole Faust bool are_layouts_different = (_file_layout != tensor.info()->data_layout());
389*c217d954SCole Faust // Correct dimensions (Needs to match TensorShape dimension corrections)
390*c217d954SCole Faust if(_shape.size() != tensor.info()->tensor_shape().num_dimensions())
391*c217d954SCole Faust {
392*c217d954SCole Faust for(int i = static_cast<int>(_shape.size()) - 1; i > 0; --i)
393*c217d954SCole Faust {
394*c217d954SCole Faust if(_shape[i] == 1)
395*c217d954SCole Faust {
396*c217d954SCole Faust _shape.pop_back();
397*c217d954SCole Faust }
398*c217d954SCole Faust else
399*c217d954SCole Faust {
400*c217d954SCole Faust break;
401*c217d954SCole Faust }
402*c217d954SCole Faust }
403*c217d954SCole Faust }
404*c217d954SCole Faust
405*c217d954SCole Faust TensorShape permuted_shape = tensor.info()->tensor_shape();
406*c217d954SCole Faust arm_compute::PermutationVector perm;
407*c217d954SCole Faust if(are_layouts_different && tensor.info()->tensor_shape().num_dimensions() > 2)
408*c217d954SCole Faust {
409*c217d954SCole Faust perm = (tensor.info()->data_layout() == arm_compute::DataLayout::NHWC) ? arm_compute::PermutationVector(2U, 0U, 1U) : arm_compute::PermutationVector(1U, 2U, 0U);
410*c217d954SCole Faust arm_compute::PermutationVector perm_vec = (tensor.info()->data_layout() == arm_compute::DataLayout::NCHW) ? arm_compute::PermutationVector(2U, 0U, 1U) : arm_compute::PermutationVector(1U, 2U, 0U);
411*c217d954SCole Faust
412*c217d954SCole Faust arm_compute::permute(permuted_shape, perm_vec);
413*c217d954SCole Faust }
414*c217d954SCole Faust
415*c217d954SCole Faust // Validate tensor shape
416*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_MSG(_shape.size() != tensor.info()->tensor_shape().num_dimensions(), "Tensor ranks mismatch");
417*c217d954SCole Faust for(size_t i = 0; i < _shape.size(); ++i)
418*c217d954SCole Faust {
419*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_MSG(permuted_shape[i] != _shape[i], "Tensor dimensions mismatch");
420*c217d954SCole Faust }
421*c217d954SCole Faust
422*c217d954SCole Faust switch(tensor.info()->data_type())
423*c217d954SCole Faust {
424*c217d954SCole Faust case arm_compute::DataType::QASYMM8:
425*c217d954SCole Faust case arm_compute::DataType::S32:
426*c217d954SCole Faust case arm_compute::DataType::F32:
427*c217d954SCole Faust case arm_compute::DataType::F16:
428*c217d954SCole Faust {
429*c217d954SCole Faust // Read data
430*c217d954SCole Faust if(!are_layouts_different && !_fortran_order && tensor.info()->padding().empty())
431*c217d954SCole Faust {
432*c217d954SCole Faust // If tensor has no padding read directly from stream.
433*c217d954SCole Faust _fs.read(reinterpret_cast<char *>(tensor.buffer()), tensor.info()->total_size());
434*c217d954SCole Faust }
435*c217d954SCole Faust else
436*c217d954SCole Faust {
437*c217d954SCole Faust // If tensor has padding or is in fortran order accessing tensor elements through execution window.
438*c217d954SCole Faust Window window;
439*c217d954SCole Faust const unsigned int num_dims = _shape.size();
440*c217d954SCole Faust if(_fortran_order)
441*c217d954SCole Faust {
442*c217d954SCole Faust for(unsigned int dim = 0; dim < num_dims; dim++)
443*c217d954SCole Faust {
444*c217d954SCole Faust permuted_shape.set(dim, _shape[num_dims - dim - 1]);
445*c217d954SCole Faust perm.set(dim, num_dims - dim - 1);
446*c217d954SCole Faust }
447*c217d954SCole Faust if(are_layouts_different)
448*c217d954SCole Faust {
449*c217d954SCole Faust // Permute only if num_dimensions greater than 2
450*c217d954SCole Faust if(num_dims > 2)
451*c217d954SCole Faust {
452*c217d954SCole Faust if(_file_layout == DataLayout::NHWC) // i.e destination is NCHW --> permute(1,2,0)
453*c217d954SCole Faust {
454*c217d954SCole Faust arm_compute::permute(perm, arm_compute::PermutationVector(1U, 2U, 0U));
455*c217d954SCole Faust }
456*c217d954SCole Faust else
457*c217d954SCole Faust {
458*c217d954SCole Faust arm_compute::permute(perm, arm_compute::PermutationVector(2U, 0U, 1U));
459*c217d954SCole Faust }
460*c217d954SCole Faust }
461*c217d954SCole Faust }
462*c217d954SCole Faust }
463*c217d954SCole Faust window.use_tensor_dimensions(permuted_shape);
464*c217d954SCole Faust
465*c217d954SCole Faust execute_window_loop(window, [&](const Coordinates & id)
466*c217d954SCole Faust {
467*c217d954SCole Faust Coordinates dst(id);
468*c217d954SCole Faust arm_compute::permute(dst, perm);
469*c217d954SCole Faust _fs.read(reinterpret_cast<char *>(tensor.ptr_to_element(dst)), tensor.info()->element_size());
470*c217d954SCole Faust });
471*c217d954SCole Faust }
472*c217d954SCole Faust
473*c217d954SCole Faust break;
474*c217d954SCole Faust }
475*c217d954SCole Faust default:
476*c217d954SCole Faust ARM_COMPUTE_ERROR("Unsupported data type");
477*c217d954SCole Faust }
478*c217d954SCole Faust
479*c217d954SCole Faust // Unmap buffer if creating a CLTensor
480*c217d954SCole Faust unmap(tensor);
481*c217d954SCole Faust }
482*c217d954SCole Faust catch(const std::ifstream::failure &e)
483*c217d954SCole Faust {
484*c217d954SCole Faust ARM_COMPUTE_ERROR_VAR("Loading NPY file: %s", e.what());
485*c217d954SCole Faust }
486*c217d954SCole Faust }
487*c217d954SCole Faust
488*c217d954SCole Faust private:
489*c217d954SCole Faust std::ifstream _fs;
490*c217d954SCole Faust std::vector<unsigned long> _shape;
491*c217d954SCole Faust bool _fortran_order;
492*c217d954SCole Faust std::string _typestring;
493*c217d954SCole Faust DataLayout _file_layout;
494*c217d954SCole Faust };
495*c217d954SCole Faust
496*c217d954SCole Faust /** Template helper function to save a tensor image to a PPM file.
497*c217d954SCole Faust *
498*c217d954SCole Faust * @note Only U8 and RGB888 formats supported.
499*c217d954SCole Faust * @note Only works with 2D tensors.
500*c217d954SCole Faust * @note If the input tensor is a CLTensor, the function maps and unmaps the image
501*c217d954SCole Faust *
502*c217d954SCole Faust * @param[in] tensor The tensor to save as PPM file
503*c217d954SCole Faust * @param[in] ppm_filename Filename of the file to create.
504*c217d954SCole Faust */
505*c217d954SCole Faust template <typename T>
save_to_ppm(T & tensor,const std::string & ppm_filename)506*c217d954SCole Faust void save_to_ppm(T &tensor, const std::string &ppm_filename)
507*c217d954SCole Faust {
508*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::RGB888, arm_compute::Format::U8);
509*c217d954SCole Faust ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
510*c217d954SCole Faust
511*c217d954SCole Faust std::ofstream fs;
512*c217d954SCole Faust
513*c217d954SCole Faust try
514*c217d954SCole Faust {
515*c217d954SCole Faust fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
516*c217d954SCole Faust fs.open(ppm_filename, std::ios::out | std::ios::binary);
517*c217d954SCole Faust
518*c217d954SCole Faust const unsigned int width = tensor.info()->tensor_shape()[0];
519*c217d954SCole Faust const unsigned int height = tensor.info()->tensor_shape()[1];
520*c217d954SCole Faust
521*c217d954SCole Faust fs << "P6\n"
522*c217d954SCole Faust << width << " " << height << " 255\n";
523*c217d954SCole Faust
524*c217d954SCole Faust // Map buffer if creating a CLTensor
525*c217d954SCole Faust map(tensor, true);
526*c217d954SCole Faust
527*c217d954SCole Faust switch(tensor.info()->format())
528*c217d954SCole Faust {
529*c217d954SCole Faust case arm_compute::Format::U8:
530*c217d954SCole Faust {
531*c217d954SCole Faust arm_compute::Window window;
532*c217d954SCole Faust window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
533*c217d954SCole Faust window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
534*c217d954SCole Faust
535*c217d954SCole Faust arm_compute::Iterator in(&tensor, window);
536*c217d954SCole Faust
537*c217d954SCole Faust arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates &)
538*c217d954SCole Faust {
539*c217d954SCole Faust const unsigned char value = *in.ptr();
540*c217d954SCole Faust
541*c217d954SCole Faust fs << value << value << value;
542*c217d954SCole Faust },
543*c217d954SCole Faust in);
544*c217d954SCole Faust
545*c217d954SCole Faust break;
546*c217d954SCole Faust }
547*c217d954SCole Faust case arm_compute::Format::RGB888:
548*c217d954SCole Faust {
549*c217d954SCole Faust arm_compute::Window window;
550*c217d954SCole Faust window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, width));
551*c217d954SCole Faust window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
552*c217d954SCole Faust
553*c217d954SCole Faust arm_compute::Iterator in(&tensor, window);
554*c217d954SCole Faust
555*c217d954SCole Faust arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates &)
556*c217d954SCole Faust {
557*c217d954SCole Faust fs.write(reinterpret_cast<std::fstream::char_type *>(in.ptr()), width * tensor.info()->element_size());
558*c217d954SCole Faust },
559*c217d954SCole Faust in);
560*c217d954SCole Faust
561*c217d954SCole Faust break;
562*c217d954SCole Faust }
563*c217d954SCole Faust default:
564*c217d954SCole Faust ARM_COMPUTE_ERROR("Unsupported format");
565*c217d954SCole Faust }
566*c217d954SCole Faust
567*c217d954SCole Faust // Unmap buffer if creating a CLTensor
568*c217d954SCole Faust unmap(tensor);
569*c217d954SCole Faust }
570*c217d954SCole Faust catch(const std::ofstream::failure &e)
571*c217d954SCole Faust {
572*c217d954SCole Faust ARM_COMPUTE_ERROR_VAR("Writing %s: (%s)", ppm_filename.c_str(), e.what());
573*c217d954SCole Faust }
574*c217d954SCole Faust }
575*c217d954SCole Faust
576*c217d954SCole Faust /** Template helper function to save a tensor image to a NPY file.
577*c217d954SCole Faust *
578*c217d954SCole Faust * @note Only F32 data type supported.
579*c217d954SCole Faust * @note If the input tensor is a CLTensor, the function maps and unmaps the image
580*c217d954SCole Faust *
581*c217d954SCole Faust * @param[in] tensor The tensor to save as NPY file
582*c217d954SCole Faust * @param[in] npy_filename Filename of the file to create.
583*c217d954SCole Faust * @param[in] fortran_order If true, save matrix in fortran order.
584*c217d954SCole Faust */
585*c217d954SCole Faust template <typename T, typename U = float>
save_to_npy(T & tensor,const std::string & npy_filename,bool fortran_order)586*c217d954SCole Faust void save_to_npy(T &tensor, const std::string &npy_filename, bool fortran_order)
587*c217d954SCole Faust {
588*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_DATA_TYPE_NOT_IN(&tensor, arm_compute::DataType::F32, arm_compute::DataType::QASYMM8);
589*c217d954SCole Faust
590*c217d954SCole Faust std::ofstream fs;
591*c217d954SCole Faust try
592*c217d954SCole Faust {
593*c217d954SCole Faust fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
594*c217d954SCole Faust fs.open(npy_filename, std::ios::out | std::ios::binary);
595*c217d954SCole Faust
596*c217d954SCole Faust std::vector<npy::ndarray_len_t> shape(tensor.info()->num_dimensions());
597*c217d954SCole Faust
598*c217d954SCole Faust for(unsigned int i = 0, j = tensor.info()->num_dimensions() - 1; i < tensor.info()->num_dimensions(); ++i, --j)
599*c217d954SCole Faust {
600*c217d954SCole Faust shape[i] = tensor.info()->tensor_shape()[!fortran_order ? j : i];
601*c217d954SCole Faust }
602*c217d954SCole Faust
603*c217d954SCole Faust // Map buffer if creating a CLTensor
604*c217d954SCole Faust map(tensor, true);
605*c217d954SCole Faust
606*c217d954SCole Faust using typestring_type = typename std::conditional<std::is_floating_point<U>::value, float, qasymm8_t>::type;
607*c217d954SCole Faust
608*c217d954SCole Faust std::vector<typestring_type> tmp; /* Used only to get the typestring */
609*c217d954SCole Faust const npy::dtype_t dtype = npy::dtype_map.at(std::type_index(typeid(tmp)));
610*c217d954SCole Faust
611*c217d954SCole Faust std::ofstream stream(npy_filename, std::ofstream::binary);
612*c217d954SCole Faust npy::header_t header{ dtype, fortran_order, shape };
613*c217d954SCole Faust npy::write_header(stream, header);
614*c217d954SCole Faust
615*c217d954SCole Faust arm_compute::Window window;
616*c217d954SCole Faust window.use_tensor_dimensions(tensor.info()->tensor_shape());
617*c217d954SCole Faust
618*c217d954SCole Faust arm_compute::Iterator in(&tensor, window);
619*c217d954SCole Faust
620*c217d954SCole Faust arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates &)
621*c217d954SCole Faust {
622*c217d954SCole Faust stream.write(reinterpret_cast<const char *>(in.ptr()), sizeof(typestring_type));
623*c217d954SCole Faust },
624*c217d954SCole Faust in);
625*c217d954SCole Faust
626*c217d954SCole Faust // Unmap buffer if creating a CLTensor
627*c217d954SCole Faust unmap(tensor);
628*c217d954SCole Faust }
629*c217d954SCole Faust catch(const std::ofstream::failure &e)
630*c217d954SCole Faust {
631*c217d954SCole Faust ARM_COMPUTE_ERROR_VAR("Writing %s: (%s)", npy_filename.c_str(), e.what());
632*c217d954SCole Faust }
633*c217d954SCole Faust }
634*c217d954SCole Faust
635*c217d954SCole Faust /** Load the tensor with pre-trained data from a binary file
636*c217d954SCole Faust *
637*c217d954SCole Faust * @param[in] tensor The tensor to be filled. Data type supported: F32.
638*c217d954SCole Faust * @param[in] filename Filename of the binary file to load from.
639*c217d954SCole Faust */
640*c217d954SCole Faust template <typename T>
load_trained_data(T & tensor,const std::string & filename)641*c217d954SCole Faust void load_trained_data(T &tensor, const std::string &filename)
642*c217d954SCole Faust {
643*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32);
644*c217d954SCole Faust
645*c217d954SCole Faust std::ifstream fs;
646*c217d954SCole Faust
647*c217d954SCole Faust try
648*c217d954SCole Faust {
649*c217d954SCole Faust fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
650*c217d954SCole Faust // Open file
651*c217d954SCole Faust fs.open(filename, std::ios::in | std::ios::binary);
652*c217d954SCole Faust
653*c217d954SCole Faust if(!fs.good())
654*c217d954SCole Faust {
655*c217d954SCole Faust throw std::runtime_error("Could not load binary data: " + filename);
656*c217d954SCole Faust }
657*c217d954SCole Faust
658*c217d954SCole Faust // Map buffer if creating a CLTensor
659*c217d954SCole Faust map(tensor, true);
660*c217d954SCole Faust
661*c217d954SCole Faust Window window;
662*c217d954SCole Faust
663*c217d954SCole Faust window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, 1, 1));
664*c217d954SCole Faust
665*c217d954SCole Faust for(unsigned int d = 1; d < tensor.info()->num_dimensions(); ++d)
666*c217d954SCole Faust {
667*c217d954SCole Faust window.set(d, Window::Dimension(0, tensor.info()->tensor_shape()[d], 1));
668*c217d954SCole Faust }
669*c217d954SCole Faust
670*c217d954SCole Faust arm_compute::Iterator in(&tensor, window);
671*c217d954SCole Faust
672*c217d954SCole Faust execute_window_loop(window, [&](const Coordinates &)
673*c217d954SCole Faust {
674*c217d954SCole Faust fs.read(reinterpret_cast<std::fstream::char_type *>(in.ptr()), tensor.info()->tensor_shape()[0] * tensor.info()->element_size());
675*c217d954SCole Faust },
676*c217d954SCole Faust in);
677*c217d954SCole Faust
678*c217d954SCole Faust // Unmap buffer if creating a CLTensor
679*c217d954SCole Faust unmap(tensor);
680*c217d954SCole Faust }
681*c217d954SCole Faust catch(const std::ofstream::failure &e)
682*c217d954SCole Faust {
683*c217d954SCole Faust ARM_COMPUTE_ERROR_VAR("Writing %s: (%s)", filename.c_str(), e.what());
684*c217d954SCole Faust }
685*c217d954SCole Faust }
686*c217d954SCole Faust
687*c217d954SCole Faust template <typename T, typename TensorType>
fill_tensor_value(TensorType & tensor,T value)688*c217d954SCole Faust void fill_tensor_value(TensorType &tensor, T value)
689*c217d954SCole Faust {
690*c217d954SCole Faust map(tensor, true);
691*c217d954SCole Faust
692*c217d954SCole Faust Window window;
693*c217d954SCole Faust window.use_tensor_dimensions(tensor.info()->tensor_shape());
694*c217d954SCole Faust
695*c217d954SCole Faust Iterator it_tensor(&tensor, window);
696*c217d954SCole Faust execute_window_loop(window, [&](const Coordinates &)
697*c217d954SCole Faust {
698*c217d954SCole Faust *reinterpret_cast<T *>(it_tensor.ptr()) = value;
699*c217d954SCole Faust },
700*c217d954SCole Faust it_tensor);
701*c217d954SCole Faust
702*c217d954SCole Faust unmap(tensor);
703*c217d954SCole Faust }
704*c217d954SCole Faust
705*c217d954SCole Faust template <typename T, typename TensorType>
fill_tensor_zero(TensorType & tensor)706*c217d954SCole Faust void fill_tensor_zero(TensorType &tensor)
707*c217d954SCole Faust {
708*c217d954SCole Faust fill_tensor_value(tensor, T(0));
709*c217d954SCole Faust }
710*c217d954SCole Faust
711*c217d954SCole Faust template <typename T, typename TensorType>
fill_tensor_vector(TensorType & tensor,std::vector<T> vec)712*c217d954SCole Faust void fill_tensor_vector(TensorType &tensor, std::vector<T> vec)
713*c217d954SCole Faust {
714*c217d954SCole Faust ARM_COMPUTE_ERROR_ON(tensor.info()->tensor_shape().total_size() != vec.size());
715*c217d954SCole Faust
716*c217d954SCole Faust map(tensor, true);
717*c217d954SCole Faust
718*c217d954SCole Faust Window window;
719*c217d954SCole Faust window.use_tensor_dimensions(tensor.info()->tensor_shape());
720*c217d954SCole Faust
721*c217d954SCole Faust int i = 0;
722*c217d954SCole Faust Iterator it_tensor(&tensor, window);
723*c217d954SCole Faust execute_window_loop(window, [&](const Coordinates &)
724*c217d954SCole Faust {
725*c217d954SCole Faust *reinterpret_cast<T *>(it_tensor.ptr()) = vec.at(i++);
726*c217d954SCole Faust },
727*c217d954SCole Faust it_tensor);
728*c217d954SCole Faust
729*c217d954SCole Faust unmap(tensor);
730*c217d954SCole Faust }
731*c217d954SCole Faust
732*c217d954SCole Faust template <typename T, typename TensorType>
733*c217d954SCole Faust void fill_random_tensor(TensorType &tensor, std::random_device::result_type seed, T lower_bound = std::numeric_limits<T>::lowest(), T upper_bound = std::numeric_limits<T>::max())
734*c217d954SCole Faust {
735*c217d954SCole Faust constexpr bool is_fp_16bit = std::is_same<T, half>::value || std::is_same<T, bfloat16>::value;
736*c217d954SCole Faust constexpr bool is_integral = std::is_integral<T>::value && !is_fp_16bit;
737*c217d954SCole Faust
738*c217d954SCole Faust using fp_dist_type = typename std::conditional<is_fp_16bit, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
739*c217d954SCole Faust using dist_type = typename std::conditional<is_integral, std::uniform_int_distribution<T>, fp_dist_type>::type;
740*c217d954SCole Faust
741*c217d954SCole Faust std::mt19937 gen(seed);
742*c217d954SCole Faust dist_type dist(lower_bound, upper_bound);
743*c217d954SCole Faust
744*c217d954SCole Faust map(tensor, true);
745*c217d954SCole Faust
746*c217d954SCole Faust Window window;
747*c217d954SCole Faust window.use_tensor_dimensions(tensor.info()->tensor_shape());
748*c217d954SCole Faust
749*c217d954SCole Faust Iterator it(&tensor, window);
750*c217d954SCole Faust execute_window_loop(window, [&](const Coordinates &)
751*c217d954SCole Faust {
752*c217d954SCole Faust *reinterpret_cast<T *>(it.ptr()) = dist(gen);
753*c217d954SCole Faust },
754*c217d954SCole Faust it);
755*c217d954SCole Faust
756*c217d954SCole Faust unmap(tensor);
757*c217d954SCole Faust }
758*c217d954SCole Faust
759*c217d954SCole Faust template <typename T, typename TensorType>
760*c217d954SCole Faust void fill_random_tensor(TensorType &tensor, T lower_bound = std::numeric_limits<T>::lowest(), T upper_bound = std::numeric_limits<T>::max())
761*c217d954SCole Faust {
762*c217d954SCole Faust std::random_device rd;
763*c217d954SCole Faust fill_random_tensor(tensor, rd(), lower_bound, upper_bound);
764*c217d954SCole Faust }
765*c217d954SCole Faust
766*c217d954SCole Faust template <typename T>
init_sgemm_output(T & dst,T & src0,T & src1,arm_compute::DataType dt)767*c217d954SCole Faust void init_sgemm_output(T &dst, T &src0, T &src1, arm_compute::DataType dt)
768*c217d954SCole Faust {
769*c217d954SCole Faust dst.allocator()->init(TensorInfo(TensorShape(src1.info()->dimension(0), src0.info()->dimension(1), src0.info()->dimension(2)), 1, dt));
770*c217d954SCole Faust }
771*c217d954SCole Faust /** This function returns the amount of memory free reading from /proc/meminfo
772*c217d954SCole Faust *
773*c217d954SCole Faust * @return The free memory in kB
774*c217d954SCole Faust */
775*c217d954SCole Faust uint64_t get_mem_free_from_meminfo();
776*c217d954SCole Faust
777*c217d954SCole Faust /** Compare two tensors
778*c217d954SCole Faust *
779*c217d954SCole Faust * @param[in] tensor1 First tensor to be compared.
780*c217d954SCole Faust * @param[in] tensor2 Second tensor to be compared.
781*c217d954SCole Faust * @param[in] tolerance Tolerance used for the comparison.
782*c217d954SCole Faust *
783*c217d954SCole Faust * @return The number of mismatches
784*c217d954SCole Faust */
785*c217d954SCole Faust template <typename T>
compare_tensor(ITensor & tensor1,ITensor & tensor2,T tolerance)786*c217d954SCole Faust int compare_tensor(ITensor &tensor1, ITensor &tensor2, T tolerance)
787*c217d954SCole Faust {
788*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(&tensor1, &tensor2);
789*c217d954SCole Faust ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(&tensor1, &tensor2);
790*c217d954SCole Faust
791*c217d954SCole Faust int num_mismatches = 0;
792*c217d954SCole Faust Window window;
793*c217d954SCole Faust window.use_tensor_dimensions(tensor1.info()->tensor_shape());
794*c217d954SCole Faust
795*c217d954SCole Faust map(tensor1, true);
796*c217d954SCole Faust map(tensor2, true);
797*c217d954SCole Faust
798*c217d954SCole Faust Iterator itensor1(&tensor1, window);
799*c217d954SCole Faust Iterator itensor2(&tensor2, window);
800*c217d954SCole Faust
801*c217d954SCole Faust execute_window_loop(window, [&](const Coordinates &)
802*c217d954SCole Faust {
803*c217d954SCole Faust if(std::abs(*reinterpret_cast<T *>(itensor1.ptr()) - *reinterpret_cast<T *>(itensor2.ptr())) > tolerance)
804*c217d954SCole Faust {
805*c217d954SCole Faust ++num_mismatches;
806*c217d954SCole Faust }
807*c217d954SCole Faust },
808*c217d954SCole Faust itensor1, itensor2);
809*c217d954SCole Faust
810*c217d954SCole Faust unmap(itensor1);
811*c217d954SCole Faust unmap(itensor2);
812*c217d954SCole Faust
813*c217d954SCole Faust return num_mismatches;
814*c217d954SCole Faust }
815*c217d954SCole Faust } // namespace utils
816*c217d954SCole Faust } // namespace arm_compute
817*c217d954SCole Faust #endif /* __UTILS_UTILS_H__*/
818