blob: f535f163033513d1d8434c2fb7bb03c4b14b7a88 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Georgios Pinitas587708b2018-12-31 15:43:52 +00002 * Copyright (c) 2017-2019 ARM Limited.
Anthony Barbier6ff3b192017-09-04 18:44:23 +01003 *
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#ifndef __ARM_COMPUTE_TEST_TENSOR_LIBRARY_H__
25#define __ARM_COMPUTE_TEST_TENSOR_LIBRARY_H__
26
Anthony Barbier6ff3b192017-09-04 18:44:23 +010027#include "arm_compute/core/Coordinates.h"
28#include "arm_compute/core/Error.h"
29#include "arm_compute/core/Helpers.h"
30#include "arm_compute/core/TensorInfo.h"
31#include "arm_compute/core/TensorShape.h"
32#include "arm_compute/core/Types.h"
33#include "arm_compute/core/Window.h"
Georgios Pinitas587708b2018-12-31 15:43:52 +000034#include "arm_compute/core/utils/misc/Random.h"
Michalis Spyrou6bff1952019-10-02 17:22:11 +010035#pragma GCC diagnostic push
36#pragma GCC diagnostic ignored "-Wunused-parameter"
SiCong Li86b53332017-08-23 11:02:43 +010037#include "libnpy/npy.hpp"
Michalis Spyrou6bff1952019-10-02 17:22:11 +010038#pragma GCC diagnostic pop
Moritz Pflanzere49e2662017-07-21 15:55:28 +010039#include "tests/RawTensor.h"
40#include "tests/TensorCache.h"
41#include "tests/Utils.h"
Anthony Barbierf6705ec2017-09-28 12:01:10 +010042#include "tests/framework/Exceptions.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010043
44#include <algorithm>
45#include <cstddef>
46#include <fstream>
47#include <random>
48#include <string>
49#include <type_traits>
SiCong Li86b53332017-08-23 11:02:43 +010050#include <vector>
Anthony Barbier6ff3b192017-09-04 18:44:23 +010051
52namespace arm_compute
53{
54namespace test
55{
56/** Factory class to create and fill tensors.
57 *
58 * Allows to initialise tensors from loaded images or by specifying the shape
59 * explicitly. Furthermore, provides methods to fill tensors with the content of
60 * loaded images or with random values.
61 */
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +010062class AssetsLibrary final
Anthony Barbier6ff3b192017-09-04 18:44:23 +010063{
64public:
Georgios Pinitas587708b2018-12-31 15:43:52 +000065 using RangePair = std::pair<float, float>;
66
67public:
John Richardson70f946b2017-10-02 16:52:16 +010068 /** Initialises the library with a @p path to the assets directory.
Anthony Barbier6ff3b192017-09-04 18:44:23 +010069 * Furthermore, sets the seed for the random generator to @p seed.
70 *
John Richardson70f946b2017-10-02 16:52:16 +010071 * @param[in] path Path to load assets from.
Anthony Barbier6ff3b192017-09-04 18:44:23 +010072 * @param[in] seed Seed used to initialise the random number generator.
73 */
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +010074 AssetsLibrary(std::string path, std::random_device::result_type seed);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010075
Alex Gildayc357c472018-03-21 13:54:09 +000076 /** Path to assets directory used to initialise library.
77 *
78 * @return the path to the assets directory.
79 */
John Richardson70f946b2017-10-02 16:52:16 +010080 std::string path() const;
81
Alex Gildayc357c472018-03-21 13:54:09 +000082 /** Seed that is used to fill tensors with random values.
83 *
84 * @return the initial random seed.
85 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +010086 std::random_device::result_type seed() const;
87
Giorgio Arenafda46182017-06-16 13:57:33 +010088 /** Provides a tensor shape for the specified image.
89 *
90 * @param[in] name Image file used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +000091 *
92 * @return the tensor shape for the specified image.
Giorgio Arenafda46182017-06-16 13:57:33 +010093 */
94 TensorShape get_image_shape(const std::string &name);
95
Alex Gildayc357c472018-03-21 13:54:09 +000096 /** Provides a constant raw tensor for the specified image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +010097 *
98 * @param[in] name Image file used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +000099 *
100 * @return a raw tensor for the specified image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100101 */
102 const RawTensor &get(const std::string &name) const;
103
104 /** Provides a raw tensor for the specified image.
105 *
106 * @param[in] name Image file used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +0000107 *
108 * @return a raw tensor for the specified image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100109 */
110 RawTensor get(const std::string &name);
111
112 /** Creates an uninitialised raw tensor with the given @p data_type and @p
113 * num_channels. The shape is derived from the specified image.
114 *
115 * @param[in] name Image file used to initialise the tensor.
116 * @param[in] data_type Data type used to initialise the tensor.
117 * @param[in] num_channels Number of channels used to initialise the tensor.
Alex Gildayc357c472018-03-21 13:54:09 +0000118 *
119 * @return a raw tensor for the specified image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100120 */
121 RawTensor get(const std::string &name, DataType data_type, int num_channels = 1) const;
122
123 /** Provides a contant raw tensor for the specified image after it has been
124 * converted to @p format.
125 *
126 * @param[in] name Image file used to look up the raw tensor.
127 * @param[in] format Format used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +0000128 *
129 * @return a raw tensor for the specified image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100130 */
131 const RawTensor &get(const std::string &name, Format format) const;
132
133 /** Provides a raw tensor for the specified image after it has been
134 * converted to @p format.
135 *
136 * @param[in] name Image file used to look up the raw tensor.
137 * @param[in] format Format used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +0000138 *
139 * @return a raw tensor for the specified image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100140 */
141 RawTensor get(const std::string &name, Format format);
142
143 /** Provides a contant raw tensor for the specified channel after it has
144 * been extracted form the given image.
145 *
146 * @param[in] name Image file used to look up the raw tensor.
147 * @param[in] channel Channel used to look up the raw tensor.
148 *
149 * @note The channel has to be unambiguous so that the format can be
150 * inferred automatically.
Alex Gildayc357c472018-03-21 13:54:09 +0000151 *
152 * @return a raw tensor for the specified image channel.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100153 */
154 const RawTensor &get(const std::string &name, Channel channel) const;
155
156 /** Provides a raw tensor for the specified channel after it has been
157 * extracted form the given image.
158 *
159 * @param[in] name Image file used to look up the raw tensor.
160 * @param[in] channel Channel used to look up the raw tensor.
161 *
162 * @note The channel has to be unambiguous so that the format can be
163 * inferred automatically.
Alex Gildayc357c472018-03-21 13:54:09 +0000164 *
165 * @return a raw tensor for the specified image channel.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100166 */
167 RawTensor get(const std::string &name, Channel channel);
168
169 /** Provides a constant raw tensor for the specified channel after it has
170 * been extracted form the given image formatted to @p format.
171 *
172 * @param[in] name Image file used to look up the raw tensor.
173 * @param[in] format Format used to look up the raw tensor.
174 * @param[in] channel Channel used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +0000175 *
176 * @return a raw tensor for the specified image channel.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100177 */
178 const RawTensor &get(const std::string &name, Format format, Channel channel) const;
179
180 /** Provides a raw tensor for the specified channel after it has been
181 * extracted form the given image formatted to @p format.
182 *
183 * @param[in] name Image file used to look up the raw tensor.
184 * @param[in] format Format used to look up the raw tensor.
185 * @param[in] channel Channel used to look up the raw tensor.
Alex Gildayc357c472018-03-21 13:54:09 +0000186 *
187 * @return a raw tensor for the specified image channel.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100188 */
189 RawTensor get(const std::string &name, Format format, Channel channel);
190
Giorgio Arenaa2611812017-07-21 10:08:48 +0100191 /** Puts garbage values all around the tensor for testing purposes
192 *
193 * @param[in, out] tensor To be filled tensor.
194 * @param[in] distribution Distribution used to fill the tensor's surroundings.
195 * @param[in] seed_offset The offset will be added to the global seed before initialising the random generator.
196 */
197 template <typename T, typename D>
198 void fill_borders_with_garbage(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const;
199
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100200 /** Fills the specified @p tensor with random values drawn from @p
201 * distribution.
202 *
203 * @param[in, out] tensor To be filled tensor.
204 * @param[in] distribution Distribution used to fill the tensor.
205 * @param[in] seed_offset The offset will be added to the global seed before initialising the random generator.
206 *
207 * @note The @p distribution has to provide operator(Generator &) which
208 * will be used to draw samples.
209 */
210 template <typename T, typename D>
211 void fill(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const;
212
Pablo Telloe96e4f02018-12-21 16:47:23 +0000213 template <typename T, typename D>
214 void fill_boxes(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const;
215
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100216 /** Fills the specified @p raw tensor with random values drawn from @p
217 * distribution.
218 *
219 * @param[in, out] raw To be filled raw.
220 * @param[in] distribution Distribution used to fill the tensor.
221 * @param[in] seed_offset The offset will be added to the global seed before initialising the random generator.
222 *
223 * @note The @p distribution has to provide operator(Generator &) which
224 * will be used to draw samples.
225 */
226 template <typename D>
227 void fill(RawTensor &raw, D &&distribution, std::random_device::result_type seed_offset) const;
228
229 /** Fills the specified @p tensor with the content of the specified image
230 * converted to the given format.
231 *
232 * @param[in, out] tensor To be filled tensor.
233 * @param[in] name Image file used to fill the tensor.
234 * @param[in] format Format of the image used to fill the tensor.
235 *
236 * @warning No check is performed that the specified format actually
237 * matches the format of the tensor.
238 */
239 template <typename T>
240 void fill(T &&tensor, const std::string &name, Format format) const;
241
242 /** Fills the raw tensor with the content of the specified image
243 * converted to the given format.
244 *
245 * @param[in, out] raw To be filled raw tensor.
246 * @param[in] name Image file used to fill the tensor.
247 * @param[in] format Format of the image used to fill the tensor.
248 *
249 * @warning No check is performed that the specified format actually
250 * matches the format of the tensor.
251 */
252 void fill(RawTensor &raw, const std::string &name, Format format) const;
253
254 /** Fills the specified @p tensor with the content of the specified channel
255 * extracted from the given image.
256 *
257 * @param[in, out] tensor To be filled tensor.
258 * @param[in] name Image file used to fill the tensor.
259 * @param[in] channel Channel of the image used to fill the tensor.
260 *
261 * @note The channel has to be unambiguous so that the format can be
262 * inferred automatically.
263 *
264 * @warning No check is performed that the specified format actually
265 * matches the format of the tensor.
266 */
267 template <typename T>
268 void fill(T &&tensor, const std::string &name, Channel channel) const;
269
270 /** Fills the raw tensor with the content of the specified channel
271 * extracted from the given image.
272 *
273 * @param[in, out] raw To be filled raw tensor.
274 * @param[in] name Image file used to fill the tensor.
275 * @param[in] channel Channel of the image used to fill the tensor.
276 *
277 * @note The channel has to be unambiguous so that the format can be
278 * inferred automatically.
279 *
280 * @warning No check is performed that the specified format actually
281 * matches the format of the tensor.
282 */
283 void fill(RawTensor &raw, const std::string &name, Channel channel) const;
284
285 /** Fills the specified @p tensor with the content of the specified channel
286 * extracted from the given image after it has been converted to the given
287 * format.
288 *
289 * @param[in, out] tensor To be filled tensor.
290 * @param[in] name Image file used to fill the tensor.
291 * @param[in] format Format of the image used to fill the tensor.
292 * @param[in] channel Channel of the image used to fill the tensor.
293 *
294 * @warning No check is performed that the specified format actually
295 * matches the format of the tensor.
296 */
297 template <typename T>
298 void fill(T &&tensor, const std::string &name, Format format, Channel channel) const;
299
300 /** Fills the raw tensor with the content of the specified channel
301 * extracted from the given image after it has been converted to the given
302 * format.
303 *
304 * @param[in, out] raw To be filled raw tensor.
305 * @param[in] name Image file used to fill the tensor.
306 * @param[in] format Format of the image used to fill the tensor.
307 * @param[in] channel Channel of the image used to fill the tensor.
308 *
309 * @warning No check is performed that the specified format actually
310 * matches the format of the tensor.
311 */
312 void fill(RawTensor &raw, const std::string &name, Format format, Channel channel) const;
313
Alex Gilday345ab182018-01-09 11:40:19 +0000314 /** Fills the specified @p tensor with the content of the raw tensor.
315 *
316 * @param[in, out] tensor To be filled tensor.
317 * @param[in] raw Raw tensor used to fill the tensor.
318 *
319 * @warning No check is performed that the specified format actually
320 * matches the format of the tensor.
321 */
322 template <typename T>
323 void fill(T &&tensor, RawTensor raw) const;
324
Georgios Pinitas587708b2018-12-31 15:43:52 +0000325 /** Fill a tensor with uniform distribution
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100326 *
327 * @param[in, out] tensor To be filled tensor.
328 * @param[in] seed_offset The offset will be added to the global seed before initialising the random generator.
329 */
330 template <typename T>
331 void fill_tensor_uniform(T &&tensor, std::random_device::result_type seed_offset) const;
332
Georgios Pinitas587708b2018-12-31 15:43:52 +0000333 /** Fill a tensor with uniform distribution
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100334 *
335 * @param[in, out] tensor To be filled tensor.
336 * @param[in] seed_offset The offset will be added to the global seed before initialising the random generator.
337 * @param[in] low lowest value in the range (inclusive)
338 * @param[in] high highest value in the range (inclusive)
339 *
340 * @note @p low and @p high must be of the same type as the data type of @p tensor
341 */
342 template <typename T, typename D>
343 void fill_tensor_uniform(T &&tensor, std::random_device::result_type seed_offset, D low, D high) const;
344
Georgios Pinitas587708b2018-12-31 15:43:52 +0000345 /** Fill a tensor with uniform distribution across the specified range
346 *
347 * @param[in, out] tensor To be filled tensor.
348 * @param[in] seed_offset The offset will be added to the global seed before initialising the random generator.
349 * @param[in] excluded_range_pairs Ranges to exclude from the generator
350 */
351 template <typename T>
352 void fill_tensor_uniform_ranged(T &&tensor,
353 std::random_device::result_type seed_offset,
354 const std::vector<AssetsLibrary::RangePair> &excluded_range_pairs) const;
355
SiCong Li86b53332017-08-23 11:02:43 +0100356 /** Fills the specified @p tensor with data loaded from .npy (numpy binary) in specified path.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100357 *
358 * @param[in, out] tensor To be filled tensor.
359 * @param[in] name Data file.
SiCong Li86b53332017-08-23 11:02:43 +0100360 *
361 * @note The numpy array stored in the binary .npy file must be row-major in the sense that it
362 * must store elements within a row consecutively in the memory, then rows within a 2D slice,
363 * then 2D slices within a 3D slice and so on. Note that it imposes no restrictions on what
364 * indexing convention is used in the numpy array. That is, the numpy array can be either fortran
365 * style or C style as long as it adheres to the rule above.
366 *
367 * More concretely, the orders of dimensions for each style are as follows:
368 * C-style (numpy default):
369 * array[HigherDims..., Z, Y, X]
370 * Fortran style:
371 * array[X, Y, Z, HigherDims...]
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100372 */
373 template <typename T>
374 void fill_layer_data(T &&tensor, std::string name) const;
375
Michele Di Giorgio4d336302018-03-02 09:43:54 +0000376 /** Fill a tensor with a constant value
377 *
378 * @param[in, out] tensor To be filled tensor.
379 * @param[in] value Value to be assigned to all elements of the input tensor.
380 *
381 * @note @p value must be of the same type as the data type of @p tensor
382 */
383 template <typename T, typename D>
384 void fill_tensor_value(T &&tensor, D value) const;
385
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100386private:
387 // Function prototype to convert between image formats.
388 using Converter = void (*)(const RawTensor &src, RawTensor &dst);
389 // Function prototype to extract a channel from an image.
390 using Extractor = void (*)(const RawTensor &src, RawTensor &dst);
391 // Function prototype to load an image file.
392 using Loader = RawTensor (*)(const std::string &path);
393
394 const Converter &get_converter(Format src, Format dst) const;
395 const Converter &get_converter(DataType src, Format dst) const;
396 const Converter &get_converter(Format src, DataType dst) const;
397 const Converter &get_converter(DataType src, DataType dst) const;
398 const Extractor &get_extractor(Format format, Channel) const;
399 const Loader &get_loader(const std::string &extension) const;
400
401 /** Creates a raw tensor from the specified image.
402 *
403 * @param[in] name To be loaded image file.
404 *
405 * @note If use_single_image is true @p name is ignored and the user image
406 * is loaded instead.
407 */
408 RawTensor load_image(const std::string &name) const;
409
410 /** Provides a raw tensor for the specified image and format.
411 *
412 * @param[in] name Image file used to look up the raw tensor.
413 * @param[in] format Format used to look up the raw tensor.
414 *
415 * If the tensor has already been requested before the cached version will
416 * be returned. Otherwise the tensor will be added to the cache.
417 *
418 * @note If use_single_image is true @p name is ignored and the user image
419 * is loaded instead.
420 */
421 const RawTensor &find_or_create_raw_tensor(const std::string &name, Format format) const;
422
423 /** Provides a raw tensor for the specified image, format and channel.
424 *
425 * @param[in] name Image file used to look up the raw tensor.
426 * @param[in] format Format used to look up the raw tensor.
427 * @param[in] channel Channel used to look up the raw tensor.
428 *
429 * If the tensor has already been requested before the cached version will
430 * be returned. Otherwise the tensor will be added to the cache.
431 *
432 * @note If use_single_image is true @p name is ignored and the user image
433 * is loaded instead.
434 */
435 const RawTensor &find_or_create_raw_tensor(const std::string &name, Format format, Channel channel) const;
436
437 mutable TensorCache _cache{};
Georgios Pinitas421405b2018-10-26 19:05:32 +0100438 mutable arm_compute::Mutex _format_lock{};
439 mutable arm_compute::Mutex _channel_lock{};
Anthony Barbierac69aa12017-07-03 17:39:37 +0100440 const std::string _library_path;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100441 std::random_device::result_type _seed;
442};
443
Georgios Pinitas587708b2018-12-31 15:43:52 +0000444namespace detail
445{
446template <typename T>
447inline std::vector<std::pair<T, T>> convert_range_pair(const std::vector<AssetsLibrary::RangePair> &excluded_range_pairs)
448{
449 std::vector<std::pair<T, T>> converted;
450 std::transform(excluded_range_pairs.begin(),
451 excluded_range_pairs.end(),
452 std::back_inserter(converted),
453 [](const AssetsLibrary::RangePair & p)
454 {
455 return std::pair<T, T>(static_cast<T>(p.first), static_cast<T>(p.second));
456 });
457 return converted;
458}
459} // namespace detail
460
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100461template <typename T, typename D>
Giorgio Arenaa2611812017-07-21 10:08:48 +0100462void AssetsLibrary::fill_borders_with_garbage(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const
463{
464 const PaddingSize padding_size = tensor.padding();
465
466 Window window;
467 window.set(0, Window::Dimension(-padding_size.left, tensor.shape()[0] + padding_size.right, 1));
Gian Marco5420b282017-11-29 10:41:38 +0000468 if(tensor.shape().num_dimensions() > 1)
469 {
470 window.set(1, Window::Dimension(-padding_size.top, tensor.shape()[1] + padding_size.bottom, 1));
471 }
Giorgio Arenaa2611812017-07-21 10:08:48 +0100472
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100473 std::mt19937 gen(_seed + seed_offset);
Giorgio Arenaa2611812017-07-21 10:08:48 +0100474
475 execute_window_loop(window, [&](const Coordinates & id)
476 {
477 TensorShape shape = tensor.shape();
478
479 // If outside of valid region
480 if(id.x() < 0 || id.x() >= static_cast<int>(shape.x()) || id.y() < 0 || id.y() >= static_cast<int>(shape.y()))
481 {
482 using ResultType = typename std::remove_reference<D>::type::result_type;
483 const ResultType value = distribution(gen);
484 void *const out_ptr = tensor(id);
485 store_value_with_data_type(out_ptr, value, tensor.data_type());
486 }
487 });
488}
489
490template <typename T, typename D>
Pablo Telloe96e4f02018-12-21 16:47:23 +0000491void AssetsLibrary::fill_boxes(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const
492{
493 using ResultType = typename std::remove_reference<D>::type::result_type;
Michalis Spyroufae513c2019-10-16 17:41:33 +0100494 std::mt19937 gen(_seed + seed_offset);
495 TensorShape shape(tensor.shape());
496 const uint32_t num_boxes = tensor.num_elements() / 4;
Pablo Telloe96e4f02018-12-21 16:47:23 +0000497 // Iterate over all elements
498 std::uniform_real_distribution<> size_dist(0.f, 1.f);
Michalis Spyroufae513c2019-10-16 17:41:33 +0100499 for(uint32_t element_idx = 0; element_idx < num_boxes * 4; element_idx += 4)
Pablo Telloe96e4f02018-12-21 16:47:23 +0000500 {
501 const ResultType delta = size_dist(gen);
502 const ResultType epsilon = size_dist(gen);
503 const ResultType left = distribution(gen);
504 const ResultType top = distribution(gen);
505 const ResultType right = left + delta;
506 const ResultType bottom = top + epsilon;
507 const std::tuple<ResultType, ResultType, ResultType, ResultType> box(left, top, right, bottom);
508 Coordinates x1 = index2coord(shape, element_idx);
509 Coordinates y1 = index2coord(shape, element_idx + 1);
510 Coordinates x2 = index2coord(shape, element_idx + 2);
511 Coordinates y2 = index2coord(shape, element_idx + 3);
512 ResultType &target_value_x1 = reinterpret_cast<ResultType *>(tensor(x1))[0];
513 ResultType &target_value_y1 = reinterpret_cast<ResultType *>(tensor(y1))[0];
514 ResultType &target_value_x2 = reinterpret_cast<ResultType *>(tensor(x2))[0];
515 ResultType &target_value_y2 = reinterpret_cast<ResultType *>(tensor(y2))[0];
516 store_value_with_data_type(&target_value_x1, std::get<0>(box), tensor.data_type());
517 store_value_with_data_type(&target_value_y1, std::get<1>(box), tensor.data_type());
518 store_value_with_data_type(&target_value_x2, std::get<2>(box), tensor.data_type());
519 store_value_with_data_type(&target_value_y2, std::get<3>(box), tensor.data_type());
520 }
521 fill_borders_with_garbage(tensor, distribution, seed_offset);
522}
523
524template <typename T, typename D>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100525void AssetsLibrary::fill(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100526{
Ioan-Cristian Szabo9414f642017-10-27 17:35:40 +0100527 using ResultType = typename std::remove_reference<D>::type::result_type;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100528
529 std::mt19937 gen(_seed + seed_offset);
530
Giorgio Arena563494c2018-04-30 17:29:41 +0100531 const bool is_nhwc = tensor.data_layout() == DataLayout::NHWC;
532 TensorShape shape(tensor.shape());
533
534 if(is_nhwc)
535 {
536 // Ensure that the equivalent tensors will be filled for both data layouts
537 permute(shape, PermutationVector(1U, 2U, 0U));
538 }
539
Ioan-Cristian Szabo9414f642017-10-27 17:35:40 +0100540 // Iterate over all elements
Michalis Spyroufae513c2019-10-16 17:41:33 +0100541 const uint32_t num_elements = tensor.num_elements();
542 for(uint32_t element_idx = 0; element_idx < num_elements; ++element_idx)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100543 {
Giorgio Arena563494c2018-04-30 17:29:41 +0100544 Coordinates id = index2coord(shape, element_idx);
545
546 if(is_nhwc)
547 {
548 // Write in the correct id for permuted shapes
549 permute(id, PermutationVector(2U, 0U, 1U));
550 }
Ioan-Cristian Szabo9414f642017-10-27 17:35:40 +0100551
552 // Iterate over all channels
553 for(int channel = 0; channel < tensor.num_channels(); ++channel)
554 {
555 const ResultType value = distribution(gen);
Kohei Takahashicedb78f2018-08-23 10:23:52 +0900556 ResultType &target_value = reinterpret_cast<ResultType *>(tensor(id))[channel];
Ioan-Cristian Szabo9414f642017-10-27 17:35:40 +0100557
558 store_value_with_data_type(&target_value, value, tensor.data_type());
559 }
560 }
Giorgio Arenaa2611812017-07-21 10:08:48 +0100561
562 fill_borders_with_garbage(tensor, distribution, seed_offset);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100563}
564
565template <typename D>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100566void AssetsLibrary::fill(RawTensor &raw, D &&distribution, std::random_device::result_type seed_offset) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100567{
568 std::mt19937 gen(_seed + seed_offset);
569
570 for(size_t offset = 0; offset < raw.size(); offset += raw.element_size())
571 {
572 using ResultType = typename std::remove_reference<D>::type::result_type;
573 const ResultType value = distribution(gen);
Georgios Pinitas587708b2018-12-31 15:43:52 +0000574
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100575 store_value_with_data_type(raw.data() + offset, value, raw.data_type());
576 }
577}
578
579template <typename T>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100580void AssetsLibrary::fill(T &&tensor, const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100581{
582 const RawTensor &raw = get(name, format);
583
584 for(size_t offset = 0; offset < raw.size(); offset += raw.element_size())
585 {
586 const Coordinates id = index2coord(raw.shape(), offset / raw.element_size());
587
Moritz Pflanzer82e70a12017-08-08 16:20:45 +0100588 const RawTensor::value_type *const raw_ptr = raw.data() + offset;
589 const auto out_ptr = static_cast<RawTensor::value_type *>(tensor(id));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100590 std::copy_n(raw_ptr, raw.element_size(), out_ptr);
591 }
592}
593
594template <typename T>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100595void AssetsLibrary::fill(T &&tensor, const std::string &name, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100596{
597 fill(std::forward<T>(tensor), name, get_format_for_channel(channel), channel);
598}
599
600template <typename T>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100601void AssetsLibrary::fill(T &&tensor, const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100602{
603 const RawTensor &raw = get(name, format, channel);
604
605 for(size_t offset = 0; offset < raw.size(); offset += raw.element_size())
606 {
607 const Coordinates id = index2coord(raw.shape(), offset / raw.element_size());
608
Moritz Pflanzer82e70a12017-08-08 16:20:45 +0100609 const RawTensor::value_type *const raw_ptr = raw.data() + offset;
610 const auto out_ptr = static_cast<RawTensor::value_type *>(tensor(id));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100611 std::copy_n(raw_ptr, raw.element_size(), out_ptr);
612 }
613}
614
615template <typename T>
Alex Gilday345ab182018-01-09 11:40:19 +0000616void AssetsLibrary::fill(T &&tensor, RawTensor raw) const
617{
618 for(size_t offset = 0; offset < raw.size(); offset += raw.element_size())
619 {
620 const Coordinates id = index2coord(raw.shape(), offset / raw.element_size());
621
622 const RawTensor::value_type *const raw_ptr = raw.data() + offset;
623 const auto out_ptr = static_cast<RawTensor::value_type *>(tensor(id));
624 std::copy_n(raw_ptr, raw.element_size(), out_ptr);
625 }
626}
627
628template <typename T>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100629void AssetsLibrary::fill_tensor_uniform(T &&tensor, std::random_device::result_type seed_offset) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100630{
631 switch(tensor.data_type())
632 {
633 case DataType::U8:
Anton Lokhmotovaf6204c2017-11-08 09:34:19 +0000634 case DataType::QASYMM8:
Michalis Spyrou29a01c92019-08-22 11:44:04 +0100635 case DataType::QASYMM8_PER_CHANNEL:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100636 {
637 std::uniform_int_distribution<uint8_t> distribution_u8(std::numeric_limits<uint8_t>::lowest(), std::numeric_limits<uint8_t>::max());
638 fill(tensor, distribution_u8, seed_offset);
639 break;
640 }
641 case DataType::S8:
Georgios Pinitas3d13af82019-06-04 13:04:16 +0100642 case DataType::QSYMM8:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100643 {
644 std::uniform_int_distribution<int8_t> distribution_s8(std::numeric_limits<int8_t>::lowest(), std::numeric_limits<int8_t>::max());
645 fill(tensor, distribution_s8, seed_offset);
646 break;
647 }
648 case DataType::U16:
649 {
650 std::uniform_int_distribution<uint16_t> distribution_u16(std::numeric_limits<uint16_t>::lowest(), std::numeric_limits<uint16_t>::max());
651 fill(tensor, distribution_u16, seed_offset);
652 break;
653 }
654 case DataType::S16:
Manuel Bottini3689fcd2019-06-14 17:18:12 +0100655 case DataType::QSYMM16:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100656 {
657 std::uniform_int_distribution<int16_t> distribution_s16(std::numeric_limits<int16_t>::lowest(), std::numeric_limits<int16_t>::max());
658 fill(tensor, distribution_s16, seed_offset);
659 break;
660 }
661 case DataType::U32:
662 {
663 std::uniform_int_distribution<uint32_t> distribution_u32(std::numeric_limits<uint32_t>::lowest(), std::numeric_limits<uint32_t>::max());
664 fill(tensor, distribution_u32, seed_offset);
665 break;
666 }
667 case DataType::S32:
668 {
669 std::uniform_int_distribution<int32_t> distribution_s32(std::numeric_limits<int32_t>::lowest(), std::numeric_limits<int32_t>::max());
670 fill(tensor, distribution_s32, seed_offset);
671 break;
672 }
673 case DataType::U64:
674 {
675 std::uniform_int_distribution<uint64_t> distribution_u64(std::numeric_limits<uint64_t>::lowest(), std::numeric_limits<uint64_t>::max());
676 fill(tensor, distribution_u64, seed_offset);
677 break;
678 }
679 case DataType::S64:
680 {
681 std::uniform_int_distribution<int64_t> distribution_s64(std::numeric_limits<int64_t>::lowest(), std::numeric_limits<int64_t>::max());
682 fill(tensor, distribution_s64, seed_offset);
683 break;
684 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100685 case DataType::F16:
SiCong Li02dfb2c2017-07-27 17:59:20 +0100686 {
687 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
688 std::uniform_real_distribution<float> distribution_f16(-100.f, 100.f);
689 fill(tensor, distribution_f16, seed_offset);
690 break;
691 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100692 case DataType::F32:
693 {
694 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
695 std::uniform_real_distribution<float> distribution_f32(-1000.f, 1000.f);
696 fill(tensor, distribution_f32, seed_offset);
697 break;
698 }
699 case DataType::F64:
700 {
701 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
702 std::uniform_real_distribution<double> distribution_f64(-1000.f, 1000.f);
703 fill(tensor, distribution_f64, seed_offset);
704 break;
705 }
706 case DataType::SIZET:
707 {
708 std::uniform_int_distribution<size_t> distribution_sizet(std::numeric_limits<size_t>::lowest(), std::numeric_limits<size_t>::max());
709 fill(tensor, distribution_sizet, seed_offset);
710 break;
711 }
712 default:
713 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
714 }
715}
716
Georgios Pinitas587708b2018-12-31 15:43:52 +0000717template <typename T>
718void AssetsLibrary::fill_tensor_uniform_ranged(T &&tensor,
719 std::random_device::result_type seed_offset,
720 const std::vector<AssetsLibrary::RangePair> &excluded_range_pairs) const
721{
722 using namespace arm_compute::utils::random;
723
724 switch(tensor.data_type())
725 {
726 case DataType::U8:
727 case DataType::QASYMM8:
728 {
729 const auto converted_pairs = detail::convert_range_pair<uint8_t>(excluded_range_pairs);
730 RangedUniformDistribution<uint8_t> distribution_u8(std::numeric_limits<uint8_t>::lowest(),
731 std::numeric_limits<uint8_t>::max(),
732 converted_pairs);
733 fill(tensor, distribution_u8, seed_offset);
734 break;
735 }
736 case DataType::S8:
Georgios Pinitas3d13af82019-06-04 13:04:16 +0100737 case DataType::QSYMM8:
Georgios Pinitas587708b2018-12-31 15:43:52 +0000738 {
739 const auto converted_pairs = detail::convert_range_pair<int8_t>(excluded_range_pairs);
740 RangedUniformDistribution<int8_t> distribution_s8(std::numeric_limits<int8_t>::lowest(),
741 std::numeric_limits<int8_t>::max(),
742 converted_pairs);
743 fill(tensor, distribution_s8, seed_offset);
744 break;
745 }
746 case DataType::U16:
747 {
748 const auto converted_pairs = detail::convert_range_pair<uint16_t>(excluded_range_pairs);
749 RangedUniformDistribution<uint16_t> distribution_u16(std::numeric_limits<uint16_t>::lowest(),
750 std::numeric_limits<uint16_t>::max(),
751 converted_pairs);
752 fill(tensor, distribution_u16, seed_offset);
753 break;
754 }
755 case DataType::S16:
Manuel Bottini3689fcd2019-06-14 17:18:12 +0100756 case DataType::QSYMM16:
Georgios Pinitas587708b2018-12-31 15:43:52 +0000757 {
758 const auto converted_pairs = detail::convert_range_pair<int16_t>(excluded_range_pairs);
759 RangedUniformDistribution<int16_t> distribution_s16(std::numeric_limits<int16_t>::lowest(),
760 std::numeric_limits<int16_t>::max(),
761 converted_pairs);
762 fill(tensor, distribution_s16, seed_offset);
763 break;
764 }
765 case DataType::U32:
766 {
767 const auto converted_pairs = detail::convert_range_pair<uint32_t>(excluded_range_pairs);
768 RangedUniformDistribution<uint32_t> distribution_u32(std::numeric_limits<uint32_t>::lowest(),
769 std::numeric_limits<uint32_t>::max(),
770 converted_pairs);
771 fill(tensor, distribution_u32, seed_offset);
772 break;
773 }
774 case DataType::S32:
775 {
776 const auto converted_pairs = detail::convert_range_pair<int32_t>(excluded_range_pairs);
777 RangedUniformDistribution<int32_t> distribution_s32(std::numeric_limits<int32_t>::lowest(),
778 std::numeric_limits<int32_t>::max(),
779 converted_pairs);
780 fill(tensor, distribution_s32, seed_offset);
781 break;
782 }
783 case DataType::F16:
784 {
785 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
786 const auto converted_pairs = detail::convert_range_pair<float>(excluded_range_pairs);
787 RangedUniformDistribution<float> distribution_f16(-100.f, 100.f, converted_pairs);
788 fill(tensor, distribution_f16, seed_offset);
789 break;
790 }
791 case DataType::F32:
792 {
793 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
794 const auto converted_pairs = detail::convert_range_pair<float>(excluded_range_pairs);
795 RangedUniformDistribution<float> distribution_f32(-1000.f, 1000.f, converted_pairs);
796 fill(tensor, distribution_f32, seed_offset);
797 break;
798 }
799 default:
800 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
801 }
802}
803
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100804template <typename T, typename D>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100805void AssetsLibrary::fill_tensor_uniform(T &&tensor, std::random_device::result_type seed_offset, D low, D high) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100806{
807 switch(tensor.data_type())
808 {
809 case DataType::U8:
Anton Lokhmotovaf6204c2017-11-08 09:34:19 +0000810 case DataType::QASYMM8:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100811 {
812 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint8_t, D>::value));
813 std::uniform_int_distribution<uint8_t> distribution_u8(low, high);
814 fill(tensor, distribution_u8, seed_offset);
815 break;
816 }
817 case DataType::S8:
Georgios Pinitas3d13af82019-06-04 13:04:16 +0100818 case DataType::QSYMM8:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100819 {
820 ARM_COMPUTE_ERROR_ON(!(std::is_same<int8_t, D>::value));
821 std::uniform_int_distribution<int8_t> distribution_s8(low, high);
822 fill(tensor, distribution_s8, seed_offset);
823 break;
824 }
825 case DataType::U16:
826 {
827 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint16_t, D>::value));
828 std::uniform_int_distribution<uint16_t> distribution_u16(low, high);
829 fill(tensor, distribution_u16, seed_offset);
830 break;
831 }
832 case DataType::S16:
Manuel Bottini3689fcd2019-06-14 17:18:12 +0100833 case DataType::QSYMM16:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100834 {
835 ARM_COMPUTE_ERROR_ON(!(std::is_same<int16_t, D>::value));
836 std::uniform_int_distribution<int16_t> distribution_s16(low, high);
837 fill(tensor, distribution_s16, seed_offset);
838 break;
839 }
840 case DataType::U32:
841 {
842 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint32_t, D>::value));
843 std::uniform_int_distribution<uint32_t> distribution_u32(low, high);
844 fill(tensor, distribution_u32, seed_offset);
845 break;
846 }
847 case DataType::S32:
848 {
849 ARM_COMPUTE_ERROR_ON(!(std::is_same<int32_t, D>::value));
850 std::uniform_int_distribution<int32_t> distribution_s32(low, high);
851 fill(tensor, distribution_s32, seed_offset);
852 break;
853 }
854 case DataType::U64:
855 {
856 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint64_t, D>::value));
857 std::uniform_int_distribution<uint64_t> distribution_u64(low, high);
858 fill(tensor, distribution_u64, seed_offset);
859 break;
860 }
861 case DataType::S64:
862 {
863 ARM_COMPUTE_ERROR_ON(!(std::is_same<int64_t, D>::value));
864 std::uniform_int_distribution<int64_t> distribution_s64(low, high);
865 fill(tensor, distribution_s64, seed_offset);
866 break;
867 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100868 case DataType::F16:
869 {
Moritz Pflanzere49e2662017-07-21 15:55:28 +0100870 std::uniform_real_distribution<float> distribution_f16(low, high);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100871 fill(tensor, distribution_f16, seed_offset);
872 break;
873 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100874 case DataType::F32:
875 {
876 ARM_COMPUTE_ERROR_ON(!(std::is_same<float, D>::value));
877 std::uniform_real_distribution<float> distribution_f32(low, high);
878 fill(tensor, distribution_f32, seed_offset);
879 break;
880 }
881 case DataType::F64:
882 {
883 ARM_COMPUTE_ERROR_ON(!(std::is_same<double, D>::value));
884 std::uniform_real_distribution<double> distribution_f64(low, high);
885 fill(tensor, distribution_f64, seed_offset);
886 break;
887 }
888 case DataType::SIZET:
889 {
890 ARM_COMPUTE_ERROR_ON(!(std::is_same<size_t, D>::value));
891 std::uniform_int_distribution<size_t> distribution_sizet(low, high);
892 fill(tensor, distribution_sizet, seed_offset);
893 break;
894 }
895 default:
896 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
897 }
898}
899
900template <typename T>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100901void AssetsLibrary::fill_layer_data(T &&tensor, std::string name) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100902{
903#ifdef _WIN32
904 const std::string path_separator("\\");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100905#else /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100906 const std::string path_separator("/");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100907#endif /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100908 const std::string path = _library_path + path_separator + name;
909
SiCong Li86b53332017-08-23 11:02:43 +0100910 std::vector<unsigned long> shape;
911
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100912 // Open file
SiCong Li86b53332017-08-23 11:02:43 +0100913 std::ifstream stream(path, std::ios::in | std::ios::binary);
Anthony Barbierf6705ec2017-09-28 12:01:10 +0100914 if(!stream.good())
915 {
916 throw framework::FileNotFound("Could not load npy file: " + path);
917 }
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000918 std::string header = npy::read_header(stream);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100919
SiCong Li86b53332017-08-23 11:02:43 +0100920 // Parse header
921 bool fortran_order = false;
922 std::string typestr;
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000923 npy::parse_header(header, typestr, fortran_order, shape);
SiCong Li86b53332017-08-23 11:02:43 +0100924
925 // Check if the typestring matches the given one
926 std::string expect_typestr = get_typestring(tensor.data_type());
927 ARM_COMPUTE_ERROR_ON_MSG(typestr != expect_typestr, "Typestrings mismatch");
928
929 // Validate tensor shape
930 ARM_COMPUTE_ERROR_ON_MSG(shape.size() != tensor.shape().num_dimensions(), "Tensor ranks mismatch");
931 if(fortran_order)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100932 {
SiCong Li86b53332017-08-23 11:02:43 +0100933 for(size_t i = 0; i < shape.size(); ++i)
934 {
935 ARM_COMPUTE_ERROR_ON_MSG(tensor.shape()[i] != shape[i], "Tensor dimensions mismatch");
936 }
937 }
938 else
939 {
940 for(size_t i = 0; i < shape.size(); ++i)
941 {
942 ARM_COMPUTE_ERROR_ON_MSG(tensor.shape()[i] != shape[shape.size() - i - 1], "Tensor dimensions mismatch");
943 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100944 }
945
SiCong Li86b53332017-08-23 11:02:43 +0100946 // Read data
947 if(tensor.padding().empty())
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100948 {
SiCong Li86b53332017-08-23 11:02:43 +0100949 // If tensor has no padding read directly from stream.
950 stream.read(reinterpret_cast<char *>(tensor.data()), tensor.size());
951 }
952 else
953 {
954 // If tensor has padding accessing tensor elements through execution window.
955 Window window;
956 window.use_tensor_dimensions(tensor.shape());
957
SiCong Li86b53332017-08-23 11:02:43 +0100958 execute_window_loop(window, [&](const Coordinates & id)
959 {
960 stream.read(reinterpret_cast<char *>(tensor(id)), tensor.element_size());
961 });
962 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100963}
Michele Di Giorgio4d336302018-03-02 09:43:54 +0000964
965template <typename T, typename D>
966void AssetsLibrary::fill_tensor_value(T &&tensor, D value) const
967{
968 fill_tensor_uniform(tensor, 0, value, value);
969}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100970} // namespace test
971} // namespace arm_compute
Anthony Barbierac69aa12017-07-03 17:39:37 +0100972#endif /* __ARM_COMPUTE_TEST_TENSOR_LIBRARY_H__ */