blob: f8635ea57695bcad25dfdb79c2cb19cf8b00baec [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:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100635 {
636 std::uniform_int_distribution<uint8_t> distribution_u8(std::numeric_limits<uint8_t>::lowest(), std::numeric_limits<uint8_t>::max());
637 fill(tensor, distribution_u8, seed_offset);
638 break;
639 }
640 case DataType::S8:
Georgios Pinitas3d13af82019-06-04 13:04:16 +0100641 case DataType::QSYMM8:
Georgios Pinitas8217c8e2019-11-11 18:24:22 +0000642 case DataType::QSYMM8_PER_CHANNEL:
Georgios Pinitasdbdea0d2019-10-16 19:21:40 +0100643 case DataType::QASYMM8_SIGNED:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100644 {
645 std::uniform_int_distribution<int8_t> distribution_s8(std::numeric_limits<int8_t>::lowest(), std::numeric_limits<int8_t>::max());
646 fill(tensor, distribution_s8, seed_offset);
647 break;
648 }
649 case DataType::U16:
650 {
651 std::uniform_int_distribution<uint16_t> distribution_u16(std::numeric_limits<uint16_t>::lowest(), std::numeric_limits<uint16_t>::max());
652 fill(tensor, distribution_u16, seed_offset);
653 break;
654 }
655 case DataType::S16:
Manuel Bottini3689fcd2019-06-14 17:18:12 +0100656 case DataType::QSYMM16:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100657 {
658 std::uniform_int_distribution<int16_t> distribution_s16(std::numeric_limits<int16_t>::lowest(), std::numeric_limits<int16_t>::max());
659 fill(tensor, distribution_s16, seed_offset);
660 break;
661 }
662 case DataType::U32:
663 {
664 std::uniform_int_distribution<uint32_t> distribution_u32(std::numeric_limits<uint32_t>::lowest(), std::numeric_limits<uint32_t>::max());
665 fill(tensor, distribution_u32, seed_offset);
666 break;
667 }
668 case DataType::S32:
669 {
670 std::uniform_int_distribution<int32_t> distribution_s32(std::numeric_limits<int32_t>::lowest(), std::numeric_limits<int32_t>::max());
671 fill(tensor, distribution_s32, seed_offset);
672 break;
673 }
674 case DataType::U64:
675 {
676 std::uniform_int_distribution<uint64_t> distribution_u64(std::numeric_limits<uint64_t>::lowest(), std::numeric_limits<uint64_t>::max());
677 fill(tensor, distribution_u64, seed_offset);
678 break;
679 }
680 case DataType::S64:
681 {
682 std::uniform_int_distribution<int64_t> distribution_s64(std::numeric_limits<int64_t>::lowest(), std::numeric_limits<int64_t>::max());
683 fill(tensor, distribution_s64, seed_offset);
684 break;
685 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100686 case DataType::F16:
SiCong Li02dfb2c2017-07-27 17:59:20 +0100687 {
688 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
689 std::uniform_real_distribution<float> distribution_f16(-100.f, 100.f);
690 fill(tensor, distribution_f16, seed_offset);
691 break;
692 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100693 case DataType::F32:
694 {
695 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
696 std::uniform_real_distribution<float> distribution_f32(-1000.f, 1000.f);
697 fill(tensor, distribution_f32, seed_offset);
698 break;
699 }
700 case DataType::F64:
701 {
702 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
703 std::uniform_real_distribution<double> distribution_f64(-1000.f, 1000.f);
704 fill(tensor, distribution_f64, seed_offset);
705 break;
706 }
707 case DataType::SIZET:
708 {
709 std::uniform_int_distribution<size_t> distribution_sizet(std::numeric_limits<size_t>::lowest(), std::numeric_limits<size_t>::max());
710 fill(tensor, distribution_sizet, seed_offset);
711 break;
712 }
713 default:
714 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
715 }
716}
717
Georgios Pinitas587708b2018-12-31 15:43:52 +0000718template <typename T>
719void AssetsLibrary::fill_tensor_uniform_ranged(T &&tensor,
720 std::random_device::result_type seed_offset,
721 const std::vector<AssetsLibrary::RangePair> &excluded_range_pairs) const
722{
723 using namespace arm_compute::utils::random;
724
725 switch(tensor.data_type())
726 {
727 case DataType::U8:
728 case DataType::QASYMM8:
729 {
730 const auto converted_pairs = detail::convert_range_pair<uint8_t>(excluded_range_pairs);
731 RangedUniformDistribution<uint8_t> distribution_u8(std::numeric_limits<uint8_t>::lowest(),
732 std::numeric_limits<uint8_t>::max(),
733 converted_pairs);
734 fill(tensor, distribution_u8, seed_offset);
735 break;
736 }
737 case DataType::S8:
Georgios Pinitas3d13af82019-06-04 13:04:16 +0100738 case DataType::QSYMM8:
Georgios Pinitas587708b2018-12-31 15:43:52 +0000739 {
740 const auto converted_pairs = detail::convert_range_pair<int8_t>(excluded_range_pairs);
741 RangedUniformDistribution<int8_t> distribution_s8(std::numeric_limits<int8_t>::lowest(),
742 std::numeric_limits<int8_t>::max(),
743 converted_pairs);
744 fill(tensor, distribution_s8, seed_offset);
745 break;
746 }
747 case DataType::U16:
748 {
749 const auto converted_pairs = detail::convert_range_pair<uint16_t>(excluded_range_pairs);
750 RangedUniformDistribution<uint16_t> distribution_u16(std::numeric_limits<uint16_t>::lowest(),
751 std::numeric_limits<uint16_t>::max(),
752 converted_pairs);
753 fill(tensor, distribution_u16, seed_offset);
754 break;
755 }
756 case DataType::S16:
Manuel Bottini3689fcd2019-06-14 17:18:12 +0100757 case DataType::QSYMM16:
Georgios Pinitas587708b2018-12-31 15:43:52 +0000758 {
759 const auto converted_pairs = detail::convert_range_pair<int16_t>(excluded_range_pairs);
760 RangedUniformDistribution<int16_t> distribution_s16(std::numeric_limits<int16_t>::lowest(),
761 std::numeric_limits<int16_t>::max(),
762 converted_pairs);
763 fill(tensor, distribution_s16, seed_offset);
764 break;
765 }
766 case DataType::U32:
767 {
768 const auto converted_pairs = detail::convert_range_pair<uint32_t>(excluded_range_pairs);
769 RangedUniformDistribution<uint32_t> distribution_u32(std::numeric_limits<uint32_t>::lowest(),
770 std::numeric_limits<uint32_t>::max(),
771 converted_pairs);
772 fill(tensor, distribution_u32, seed_offset);
773 break;
774 }
775 case DataType::S32:
776 {
777 const auto converted_pairs = detail::convert_range_pair<int32_t>(excluded_range_pairs);
778 RangedUniformDistribution<int32_t> distribution_s32(std::numeric_limits<int32_t>::lowest(),
779 std::numeric_limits<int32_t>::max(),
780 converted_pairs);
781 fill(tensor, distribution_s32, seed_offset);
782 break;
783 }
784 case DataType::F16:
785 {
786 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
787 const auto converted_pairs = detail::convert_range_pair<float>(excluded_range_pairs);
788 RangedUniformDistribution<float> distribution_f16(-100.f, 100.f, converted_pairs);
789 fill(tensor, distribution_f16, seed_offset);
790 break;
791 }
792 case DataType::F32:
793 {
794 // It doesn't make sense to check [-inf, inf], so hard code it to a big number
795 const auto converted_pairs = detail::convert_range_pair<float>(excluded_range_pairs);
796 RangedUniformDistribution<float> distribution_f32(-1000.f, 1000.f, converted_pairs);
797 fill(tensor, distribution_f32, seed_offset);
798 break;
799 }
800 default:
801 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
802 }
803}
804
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100805template <typename T, typename D>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100806void 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 +0100807{
808 switch(tensor.data_type())
809 {
810 case DataType::U8:
Anton Lokhmotovaf6204c2017-11-08 09:34:19 +0000811 case DataType::QASYMM8:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100812 {
813 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint8_t, D>::value));
814 std::uniform_int_distribution<uint8_t> distribution_u8(low, high);
815 fill(tensor, distribution_u8, seed_offset);
816 break;
817 }
818 case DataType::S8:
Georgios Pinitas3d13af82019-06-04 13:04:16 +0100819 case DataType::QSYMM8:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100820 {
821 ARM_COMPUTE_ERROR_ON(!(std::is_same<int8_t, D>::value));
822 std::uniform_int_distribution<int8_t> distribution_s8(low, high);
823 fill(tensor, distribution_s8, seed_offset);
824 break;
825 }
826 case DataType::U16:
827 {
828 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint16_t, D>::value));
829 std::uniform_int_distribution<uint16_t> distribution_u16(low, high);
830 fill(tensor, distribution_u16, seed_offset);
831 break;
832 }
833 case DataType::S16:
Manuel Bottini3689fcd2019-06-14 17:18:12 +0100834 case DataType::QSYMM16:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100835 {
836 ARM_COMPUTE_ERROR_ON(!(std::is_same<int16_t, D>::value));
837 std::uniform_int_distribution<int16_t> distribution_s16(low, high);
838 fill(tensor, distribution_s16, seed_offset);
839 break;
840 }
841 case DataType::U32:
842 {
843 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint32_t, D>::value));
844 std::uniform_int_distribution<uint32_t> distribution_u32(low, high);
845 fill(tensor, distribution_u32, seed_offset);
846 break;
847 }
848 case DataType::S32:
849 {
850 ARM_COMPUTE_ERROR_ON(!(std::is_same<int32_t, D>::value));
851 std::uniform_int_distribution<int32_t> distribution_s32(low, high);
852 fill(tensor, distribution_s32, seed_offset);
853 break;
854 }
855 case DataType::U64:
856 {
857 ARM_COMPUTE_ERROR_ON(!(std::is_same<uint64_t, D>::value));
858 std::uniform_int_distribution<uint64_t> distribution_u64(low, high);
859 fill(tensor, distribution_u64, seed_offset);
860 break;
861 }
862 case DataType::S64:
863 {
864 ARM_COMPUTE_ERROR_ON(!(std::is_same<int64_t, D>::value));
865 std::uniform_int_distribution<int64_t> distribution_s64(low, high);
866 fill(tensor, distribution_s64, seed_offset);
867 break;
868 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100869 case DataType::F16:
870 {
Moritz Pflanzere49e2662017-07-21 15:55:28 +0100871 std::uniform_real_distribution<float> distribution_f16(low, high);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100872 fill(tensor, distribution_f16, seed_offset);
873 break;
874 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100875 case DataType::F32:
876 {
877 ARM_COMPUTE_ERROR_ON(!(std::is_same<float, D>::value));
878 std::uniform_real_distribution<float> distribution_f32(low, high);
879 fill(tensor, distribution_f32, seed_offset);
880 break;
881 }
882 case DataType::F64:
883 {
884 ARM_COMPUTE_ERROR_ON(!(std::is_same<double, D>::value));
885 std::uniform_real_distribution<double> distribution_f64(low, high);
886 fill(tensor, distribution_f64, seed_offset);
887 break;
888 }
889 case DataType::SIZET:
890 {
891 ARM_COMPUTE_ERROR_ON(!(std::is_same<size_t, D>::value));
892 std::uniform_int_distribution<size_t> distribution_sizet(low, high);
893 fill(tensor, distribution_sizet, seed_offset);
894 break;
895 }
896 default:
897 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
898 }
899}
900
901template <typename T>
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100902void AssetsLibrary::fill_layer_data(T &&tensor, std::string name) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100903{
904#ifdef _WIN32
905 const std::string path_separator("\\");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100906#else /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100907 const std::string path_separator("/");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100908#endif /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100909 const std::string path = _library_path + path_separator + name;
910
SiCong Li86b53332017-08-23 11:02:43 +0100911 std::vector<unsigned long> shape;
912
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100913 // Open file
SiCong Li86b53332017-08-23 11:02:43 +0100914 std::ifstream stream(path, std::ios::in | std::ios::binary);
Anthony Barbierf6705ec2017-09-28 12:01:10 +0100915 if(!stream.good())
916 {
917 throw framework::FileNotFound("Could not load npy file: " + path);
918 }
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000919 std::string header = npy::read_header(stream);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100920
SiCong Li86b53332017-08-23 11:02:43 +0100921 // Parse header
922 bool fortran_order = false;
923 std::string typestr;
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000924 npy::parse_header(header, typestr, fortran_order, shape);
SiCong Li86b53332017-08-23 11:02:43 +0100925
926 // Check if the typestring matches the given one
927 std::string expect_typestr = get_typestring(tensor.data_type());
928 ARM_COMPUTE_ERROR_ON_MSG(typestr != expect_typestr, "Typestrings mismatch");
929
930 // Validate tensor shape
931 ARM_COMPUTE_ERROR_ON_MSG(shape.size() != tensor.shape().num_dimensions(), "Tensor ranks mismatch");
932 if(fortran_order)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100933 {
SiCong Li86b53332017-08-23 11:02:43 +0100934 for(size_t i = 0; i < shape.size(); ++i)
935 {
936 ARM_COMPUTE_ERROR_ON_MSG(tensor.shape()[i] != shape[i], "Tensor dimensions mismatch");
937 }
938 }
939 else
940 {
941 for(size_t i = 0; i < shape.size(); ++i)
942 {
943 ARM_COMPUTE_ERROR_ON_MSG(tensor.shape()[i] != shape[shape.size() - i - 1], "Tensor dimensions mismatch");
944 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100945 }
946
SiCong Li86b53332017-08-23 11:02:43 +0100947 // Read data
948 if(tensor.padding().empty())
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100949 {
SiCong Li86b53332017-08-23 11:02:43 +0100950 // If tensor has no padding read directly from stream.
951 stream.read(reinterpret_cast<char *>(tensor.data()), tensor.size());
952 }
953 else
954 {
955 // If tensor has padding accessing tensor elements through execution window.
956 Window window;
957 window.use_tensor_dimensions(tensor.shape());
958
SiCong Li86b53332017-08-23 11:02:43 +0100959 execute_window_loop(window, [&](const Coordinates & id)
960 {
961 stream.read(reinterpret_cast<char *>(tensor(id)), tensor.element_size());
962 });
963 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100964}
Michele Di Giorgio4d336302018-03-02 09:43:54 +0000965
966template <typename T, typename D>
967void AssetsLibrary::fill_tensor_value(T &&tensor, D value) const
968{
969 fill_tensor_uniform(tensor, 0, value, value);
970}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100971} // namespace test
972} // namespace arm_compute
Anthony Barbierac69aa12017-07-03 17:39:37 +0100973#endif /* __ARM_COMPUTE_TEST_TENSOR_LIBRARY_H__ */