// Copyright 2022 Google LLC // // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include template class BinaryTest : public ::testing::Test { protected: BinaryTest() { random_device = std::unique_ptr(new std::random_device()); rng = std::mt19937((*random_device)()); shape_dist = std::uniform_int_distribution(0, XNN_MAX_TENSOR_DIMS); dim_dist = std::uniform_int_distribution(1, 9); f32dist = std::uniform_real_distribution(0.01f, 1.0f); i8dist = std::uniform_int_distribution(std::numeric_limits::min(), std::numeric_limits::max()); u8dist = std::uniform_int_distribution(std::numeric_limits::min(), std::numeric_limits::max()); scale_dist = std::uniform_real_distribution(0.1f, 5.0f); } void SetUp() override { std::vector input1_shape = RandomShape(); std::vector input2_shape; std::vector output_shape; // Create input dimensions. // Create input 2 with an equal or larger number of dimensions. const size_t input2_num_dims = std::uniform_int_distribution(input1_shape.size(), XNN_MAX_TENSOR_DIMS)(rng); input2_shape = RandomShape(input2_num_dims); // Ensure that the inputs dimensions match. std::copy_backward(input1_shape.begin(), input1_shape.end(), input2_shape.end()); // Choose a random dimension to broadcast for each input. const size_t input1_broadcast_dim = std::uniform_int_distribution(0, input1_shape.size())(rng); if (input1_broadcast_dim < input1_shape.size()) { input1_shape[input1_broadcast_dim] = 1; } const size_t input2_broadcast_dim = std::uniform_int_distribution(0, input2_shape.size())(rng); if (input2_broadcast_dim < input2_shape.size()) { input2_shape[input2_broadcast_dim] = 1; } // Calculate generalized shapes. std::fill(input1_dims.begin(), input1_dims.end(), 1); std::fill(input2_dims.begin(), input2_dims.end(), 1); std::fill(output_dims.begin(), output_dims.end(), 1); std::copy_backward(input1_shape.cbegin(), input1_shape.cend(), input1_dims.end()); std::copy_backward(input2_shape.cbegin(), input2_shape.cend(), input2_dims.end()); for (size_t i = 0; i < XNN_MAX_TENSOR_DIMS; i++) { if (input1_dims[i] != 1 && input2_dims[i] != 1) { ASSERT_EQ(input1_dims[i], input2_dims[i]) << "i: " << i; } output_dims[i] = std::max(input1_dims[i], input2_dims[i]); } input1 = std::vector(XNN_EXTRA_BYTES / sizeof(T) + NumElements(input1_shape)); input2 = std::vector(XNN_EXTRA_BYTES / sizeof(T) + NumElements(input2_shape)); operator_output = std::vector(NumElements(output_dims)); subgraph_output = std::vector(operator_output.size()); } std::vector RandomShape(size_t num_dims) { std::vector dims(num_dims); std::generate(dims.begin(), dims.end(), [&] { return dim_dist(rng); }); return dims; } std::vector RandomShape() { return RandomShape(shape_dist(rng)); } size_t NumElements(std::vector& dims) { return std::accumulate(dims.begin(), dims.end(), size_t(1), std::multiplies()); } size_t NumElements(std::array& dims) { return std::accumulate(dims.begin(), dims.end(), size_t(1), std::multiplies()); } std::unique_ptr random_device; std::mt19937 rng; std::uniform_int_distribution shape_dist; std::uniform_int_distribution dim_dist; std::uniform_real_distribution f32dist; std::uniform_real_distribution scale_dist; std::uniform_int_distribution i8dist; std::uniform_int_distribution u8dist; float output_min = -std::numeric_limits::infinity(); float output_max = std::numeric_limits::infinity(); std::array input1_dims; std::array input2_dims; std::array output_dims; std::vector input1; std::vector input2; std::vector operator_output; std::vector subgraph_output; };