1 // Copyright 2020 Google LLC 2 // 3 // This source code is licensed under the BSD-style license found in the 4 // LICENSE file in the root directory of this source tree. 5 6 #pragma once 7 8 #include <xnnpack.h> 9 #include <xnnpack/subgraph.h> 10 11 #include <algorithm> 12 #include <cassert> 13 #include <cmath> 14 #include <cstddef> 15 #include <cstdlib> 16 #include <functional> 17 #include <random> 18 #include <vector> 19 20 #include <gtest/gtest.h> 21 22 enum xnn_tensor_type { 23 kStaticDense, 24 kStaticSparse, 25 kDynamic, 26 }; 27 28 class SubgraphTester { 29 public: SubgraphTester(uint32_t external_value_ids)30 explicit SubgraphTester(uint32_t external_value_ids) { 31 xnn_status status = xnn_initialize(nullptr); 32 EXPECT_EQ(status, xnn_status_success); 33 34 xnn_subgraph_t subgraph_ptr = nullptr; 35 status = xnn_create_subgraph(external_value_ids, 0 /* flags */, &subgraph_ptr); 36 EXPECT_EQ(status, xnn_status_success); 37 subgraph_.reset(subgraph_ptr); 38 39 std::random_device random_device; 40 rng_ = std::mt19937(random_device()); 41 } 42 add_tensor(const std::vector<size_t> & dims,xnn_tensor_type tensor_type,uint32_t external_id)43 inline SubgraphTester& add_tensor(const std::vector<size_t>& dims, 44 xnn_tensor_type tensor_type, 45 uint32_t external_id) { 46 void* data = nullptr; 47 if (tensor_type == kStaticDense || tensor_type == kStaticSparse) { 48 const size_t num_elements = std::accumulate(std::begin(dims), std::end(dims), 1, std::multiplies<size_t>()); 49 static_data_.emplace_back(num_elements); 50 std::vector<float>& weights = static_data_.back(); 51 auto f32rng = std::bind(std::uniform_real_distribution<float>(-1.0f, +1.0f), std::ref(rng_)); 52 if (tensor_type == kStaticDense) { 53 std::generate(weights.begin(), weights.end(), std::ref(f32rng)); 54 } else { 55 // Create tensor with 90% sparsity in two steps: 56 // 1. Generate non-zero elements in the beginning of the vector 57 // 2. Randomize positions of non-zero elements 58 const size_t num_nonzero_elements = num_elements / 10; 59 std::generate(weights.begin(), weights.begin() + num_nonzero_elements, std::ref(f32rng)); 60 std::shuffle(weights.begin(), weights.end(), rng_); 61 } 62 data = weights.data(); 63 } 64 uint32_t id_out = 0; 65 const xnn_status status = 66 xnn_define_tensor_value(subgraph_.get(), xnn_datatype_fp32, dims.size(), 67 dims.data(), data, external_id, 0 /* flags */, &id_out); 68 EXPECT_EQ(status, xnn_status_success); 69 EXPECT_EQ(id_out, external_id); 70 71 return *this; 72 } 73 add_conv(uint32_t input_padding_top,uint32_t input_padding_right,uint32_t input_padding_bottom,uint32_t input_padding_left,uint32_t kernel_height,uint32_t kernel_width,uint32_t subsampling_height,uint32_t subsampling_width,uint32_t dilation_height,uint32_t dilation_width,uint32_t groups,size_t group_input_channels,size_t group_output_channels,uint32_t input_id,uint32_t filter_id,uint32_t bias_id,uint32_t output_id)74 inline SubgraphTester& add_conv( 75 uint32_t input_padding_top, uint32_t input_padding_right, 76 uint32_t input_padding_bottom, uint32_t input_padding_left, 77 uint32_t kernel_height, uint32_t kernel_width, 78 uint32_t subsampling_height, uint32_t subsampling_width, 79 uint32_t dilation_height, uint32_t dilation_width, uint32_t groups, 80 size_t group_input_channels, size_t group_output_channels, 81 uint32_t input_id, uint32_t filter_id, uint32_t bias_id, 82 uint32_t output_id) 83 { 84 const xnn_status status = xnn_define_convolution_2d( 85 subgraph_.get(), input_padding_top, input_padding_right, 86 input_padding_bottom, input_padding_left, kernel_height, kernel_width, 87 subsampling_height, subsampling_width, dilation_height, dilation_width, 88 groups, group_input_channels, group_output_channels, 89 -std::numeric_limits<float>::infinity(), 90 std::numeric_limits<float>::infinity(), input_id, filter_id, bias_id, 91 output_id, 0 /* flags */); 92 EXPECT_EQ(status, xnn_status_success); 93 94 return *this; 95 } 96 add_depthwise_conv(uint32_t input_padding_top,uint32_t input_padding_right,uint32_t input_padding_bottom,uint32_t input_padding_left,uint32_t kernel_height,uint32_t kernel_width,uint32_t subsampling_height,uint32_t subsampling_width,uint32_t dilation_height,uint32_t dilation_width,uint32_t depth_multiplier,size_t input_channels,uint32_t input_id,uint32_t filter_id,uint32_t bias_id,uint32_t output_id)97 inline SubgraphTester& add_depthwise_conv( 98 uint32_t input_padding_top, uint32_t input_padding_right, 99 uint32_t input_padding_bottom, uint32_t input_padding_left, 100 uint32_t kernel_height, uint32_t kernel_width, 101 uint32_t subsampling_height, uint32_t subsampling_width, 102 uint32_t dilation_height, uint32_t dilation_width, 103 uint32_t depth_multiplier, size_t input_channels, uint32_t input_id, 104 uint32_t filter_id, uint32_t bias_id, uint32_t output_id) 105 { 106 const xnn_status status = xnn_define_depthwise_convolution_2d( 107 subgraph_.get(), input_padding_top, input_padding_right, 108 input_padding_bottom, input_padding_left, kernel_height, kernel_width, 109 subsampling_height, subsampling_width, dilation_height, dilation_width, 110 depth_multiplier, input_channels, 111 -std::numeric_limits<float>::infinity(), 112 std::numeric_limits<float>::infinity(), input_id, filter_id, bias_id, 113 output_id, 0 /* flags */); 114 EXPECT_EQ(status, xnn_status_success); 115 116 return *this; 117 } 118 add_addition(uint32_t input_id1,uint32_t input_id2,uint32_t output_id)119 inline SubgraphTester& add_addition(uint32_t input_id1, uint32_t input_id2, uint32_t output_id) 120 { 121 const xnn_status status = 122 xnn_define_add2(subgraph_.get(), -std::numeric_limits<float>::infinity(), 123 std::numeric_limits<float>::infinity(), input_id1, 124 input_id2, output_id, 0 /* flags */); 125 EXPECT_EQ(status, xnn_status_success); 126 127 return *this; 128 } 129 add_global_average_pooling(uint32_t input_id,uint32_t output_id)130 inline SubgraphTester& add_global_average_pooling(uint32_t input_id, uint32_t output_id) 131 { 132 const xnn_status status = xnn_define_global_average_pooling_2d( 133 subgraph_.get(), -std::numeric_limits<float>::infinity(), 134 std::numeric_limits<float>::infinity(), input_id, output_id, 0 /* flags */); 135 EXPECT_EQ(status, xnn_status_success); 136 137 return *this; 138 } 139 optimize()140 inline SubgraphTester& optimize() { 141 const xnn_status status = xnn_subgraph_optimize(subgraph_.get(), 0 /* flags */); 142 EXPECT_EQ(status, xnn_status_success); 143 144 return *this; 145 } 146 rewrite()147 inline SubgraphTester& rewrite() { 148 xnn_subgraph_rewrite_for_nchw(subgraph_.get()); 149 150 return *this; 151 } 152 get_layout(uint32_t value_id)153 inline xnn_layout_type get_layout(uint32_t value_id) const { 154 return subgraph_->values[value_id].layout; 155 } 156 157 private: 158 std::vector<std::vector<float>> static_data_; 159 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> subgraph_{nullptr, xnn_delete_subgraph}; 160 std::mt19937 rng_; 161 }; 162