• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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 #include <algorithm>
7 #include <array>
8 #include <cstddef>
9 #include <cstdint>
10 #include <limits>
11 #include <memory>
12 #include <random>
13 
14 #include <xnnpack.h>
15 #include <xnnpack/node-type.h>
16 #include <xnnpack/operator.h>
17 #include <xnnpack/subgraph.h>
18 
19 #include "subgraph-unary-tester.h"
20 #include <gtest/gtest.h>
21 
22 using LeakyReLUTestF32 = UnaryTest<float>;
23 using LeakyReLUTestQS8 = UnaryTest<int8_t>;
24 using LeakyReLUTestQU8 = UnaryTest<uint8_t>;
25 
TEST_F(LeakyReLUTestF32,define)26 TEST_F(LeakyReLUTestF32, define)
27 {
28   const float negative_slope = std::uniform_real_distribution<float>(0.5f, 1.0f)(rng);
29 
30   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
31 
32   xnn_subgraph_t subgraph = nullptr;
33   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(/*external_value_ids=*/2, /*flags=*/0, &subgraph));
34   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
35 
36   input_id = XNN_INVALID_NODE_ID;
37   ASSERT_EQ(
38     xnn_status_success, xnn_define_tensor_value(
39                           subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, 0,
40                           /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
41   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
42 
43   output_id = XNN_INVALID_NODE_ID;
44   ASSERT_EQ(
45     xnn_status_success, xnn_define_tensor_value(
46                           subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, 1,
47                           /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
48   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
49 
50   ASSERT_EQ(xnn_status_success, xnn_define_leaky_relu(subgraph, negative_slope, input_id, output_id, /*flags=*/0));
51 
52   ASSERT_EQ(subgraph->num_nodes, 1);
53   const struct xnn_node* node = &subgraph->nodes[0];
54   ASSERT_EQ(node->type, xnn_node_type_leaky_relu);
55   ASSERT_EQ(node->compute_type, xnn_compute_type_fp32);
56   ASSERT_EQ(node->params.leaky_relu.negative_slope, negative_slope);
57   ASSERT_EQ(node->num_inputs, 1);
58   ASSERT_EQ(node->inputs[0], input_id);
59   ASSERT_EQ(node->num_outputs, 1);
60   ASSERT_EQ(node->outputs[0], output_id);
61   ASSERT_EQ(node->flags, 0);
62 }
63 
TEST_F(LeakyReLUTestQS8,define)64 TEST_F(LeakyReLUTestQS8, define)
65 {
66   const float negative_slope = std::uniform_real_distribution<float>(0.5f, 1.0f)(rng);
67 
68   const int32_t input_zero_point = i8dist(rng);
69   const float input_scale = scale_dist(rng);
70   const int32_t output_zero_point = i8dist(rng);
71   const float output_scale = scale_dist(rng);
72 
73   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
74 
75   xnn_subgraph_t subgraph = nullptr;
76   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(/*external_value_ids=*/2, /*flags=*/0, &subgraph));
77   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
78 
79   input_id = XNN_INVALID_NODE_ID;
80   ASSERT_EQ(
81     xnn_status_success, xnn_define_quantized_tensor_value(
82                           subgraph, xnn_datatype_qint8, input_zero_point, input_scale, dims.size(), dims.data(),
83                           nullptr, 0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
84   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
85 
86   output_id = XNN_INVALID_NODE_ID;
87   ASSERT_EQ(
88     xnn_status_success, xnn_define_quantized_tensor_value(
89                           subgraph, xnn_datatype_qint8, output_zero_point, output_scale, dims.size(), dims.data(),
90                           nullptr, 1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
91   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
92 
93   ASSERT_EQ(xnn_status_success, xnn_define_leaky_relu(subgraph, negative_slope, input_id, output_id, /*flags=*/0));
94 
95   ASSERT_EQ(subgraph->num_nodes, 1);
96   const struct xnn_node* node = &subgraph->nodes[0];
97   ASSERT_EQ(node->type, xnn_node_type_leaky_relu);
98   ASSERT_EQ(node->compute_type, xnn_compute_type_qs8);
99   ASSERT_EQ(node->params.leaky_relu.negative_slope, negative_slope);
100   ASSERT_EQ(node->num_inputs, 1);
101   ASSERT_EQ(node->inputs[0], input_id);
102   ASSERT_EQ(node->num_outputs, 1);
103   ASSERT_EQ(node->outputs[0], output_id);
104   ASSERT_EQ(node->flags, 0);
105 }
106 
TEST_F(LeakyReLUTestQU8,define)107 TEST_F(LeakyReLUTestQU8, define)
108 {
109   const int32_t input_zero_point = u8dist(rng);
110   const float input_scale = scale_dist(rng);
111   const int32_t output_zero_point = u8dist(rng);
112   const float output_scale = scale_dist(rng);
113   const float negative_slope = std::uniform_real_distribution<float>(0.1f, 10.0f)(rng);
114 
115   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
116 
117   xnn_subgraph_t subgraph = nullptr;
118   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(/*external_value_ids=*/2, /*flags=*/0, &subgraph));
119   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
120 
121   input_id = XNN_INVALID_NODE_ID;
122   ASSERT_EQ(
123     xnn_status_success, xnn_define_quantized_tensor_value(
124                           subgraph, xnn_datatype_quint8, input_zero_point, input_scale, dims.size(), dims.data(),
125                           nullptr, 0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
126   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
127 
128   output_id = XNN_INVALID_NODE_ID;
129   ASSERT_EQ(
130     xnn_status_success, xnn_define_quantized_tensor_value(
131                           subgraph, xnn_datatype_quint8, output_zero_point, output_scale, dims.size(), dims.data(),
132                           nullptr, 1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
133   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
134 
135   ASSERT_EQ(xnn_status_success, xnn_define_leaky_relu(subgraph, negative_slope, input_id, output_id, /*flags=*/0));
136 
137   ASSERT_EQ(subgraph->num_nodes, 1);
138   const struct xnn_node* node = &subgraph->nodes[0];
139   ASSERT_EQ(node->type, xnn_node_type_leaky_relu);
140   ASSERT_EQ(node->compute_type, xnn_compute_type_qu8);
141   ASSERT_EQ(node->params.leaky_relu.negative_slope, negative_slope);
142   ASSERT_EQ(node->num_inputs, 1);
143   ASSERT_EQ(node->inputs[0], input_id);
144   ASSERT_EQ(node->num_outputs, 1);
145   ASSERT_EQ(node->outputs[0], output_id);
146   ASSERT_EQ(node->flags, 0);
147 }
148 
TEST_F(LeakyReLUTestF32,matches_operator_api)149 TEST_F(LeakyReLUTestF32, matches_operator_api)
150 {
151   const float negative_slope = std::uniform_real_distribution<float>(0.1f, 10.0f)(rng);
152   std::uniform_real_distribution<float> f32dist(-255.0f, 255.0f);
153   std::generate(input.begin(), input.end(), [&]() { return f32dist(rng); });
154   std::fill(operator_output.begin(), operator_output.end(), nanf(""));
155   std::fill(subgraph_output.begin(), subgraph_output.end(), nanf(""));
156 
157   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
158 
159   // Call operator API.
160   xnn_operator_t op = nullptr;
161   const xnn_status status = xnn_create_leaky_relu_nc_f32(channels, channels, channels, negative_slope, /*flags=*/0, &op);
162   if (status == xnn_status_unsupported_hardware) {
163     GTEST_SKIP();
164   }
165 
166   ASSERT_EQ(xnn_status_success, status);
167   ASSERT_NE(nullptr, op);
168   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
169 
170   ASSERT_EQ(
171     xnn_status_success,
172     xnn_setup_leaky_relu_nc_f32(op, batch_size, input.data(), operator_output.data(), /*threadpool=*/nullptr));
173 
174   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
175 
176   // Call subgraph API.
177   xnn_subgraph_t subgraph = nullptr;
178   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(/*external_value_ids=*/2, /*flags=*/0, &subgraph));
179   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
180   input_id = XNN_INVALID_NODE_ID;
181   ASSERT_EQ(
182     xnn_status_success, xnn_define_tensor_value(
183                           subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, /*external_id=*/0,
184                           /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
185   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
186 
187   output_id = XNN_INVALID_NODE_ID;
188   ASSERT_EQ(
189     xnn_status_success, xnn_define_tensor_value(
190                           subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, /*external_id=*/1,
191                           /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
192   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
193 
194   xnn_runtime_t runtime = nullptr;
195   ASSERT_EQ(xnn_status_success, xnn_define_leaky_relu(subgraph, negative_slope, input_id, output_id, /*flags=*/0));
196   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
197   ASSERT_NE(nullptr, runtime);
198   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
199   std::array<xnn_external_value, 2> external = {
200     xnn_external_value{input_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
201   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
202   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
203 
204   ASSERT_EQ(subgraph_output, operator_output);
205 }
206 
TEST_F(LeakyReLUTestQS8,matches_operator_api)207 TEST_F(LeakyReLUTestQS8, matches_operator_api)
208 {
209   const float negative_slope = std::uniform_real_distribution<float>(0.5f, 1.0f)(rng);
210 
211   const int32_t input_zero_point = i8dist(rng);
212   const float input_scale = scale_dist(rng);
213   const int32_t output_zero_point = i8dist(rng);
214   const float output_scale = scale_dist(rng);
215 
216   std::generate(input.begin(), input.end(), [&]() { return i8dist(rng); });
217   std::fill(operator_output.begin(), operator_output.end(), INT8_C(0xA5));
218   std::fill(subgraph_output.begin(), subgraph_output.end(), INT8_C(0x5A));
219 
220   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
221 
222   // Call operator API.
223   xnn_operator_t op = nullptr;
224   const xnn_status status = xnn_create_leaky_relu_nc_qs8(
225     channels, channels, channels, negative_slope,
226     input_zero_point, input_scale, output_zero_point, output_scale,
227     /*flags=*/0, &op);
228   if (status == xnn_status_unsupported_hardware) {
229     GTEST_SKIP();
230   }
231 
232   ASSERT_EQ(xnn_status_success, status);
233   ASSERT_NE(nullptr, op);
234   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
235 
236   ASSERT_EQ(
237     xnn_status_success,
238     xnn_setup_leaky_relu_nc_qs8(op, batch_size, input.data(), operator_output.data(), /*threadpool=*/nullptr));
239 
240   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
241 
242   // Call subgraph API.
243   xnn_subgraph_t subgraph = nullptr;
244   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(/*external_value_ids=*/2, /*flags=*/0, &subgraph));
245   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
246   input_id = XNN_INVALID_NODE_ID;
247   ASSERT_EQ(
248     xnn_status_success, xnn_define_quantized_tensor_value(
249                           subgraph, xnn_datatype_qint8, input_zero_point, input_scale, dims.size(), dims.data(),
250                           nullptr, 0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
251   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
252 
253   output_id = XNN_INVALID_NODE_ID;
254   ASSERT_EQ(
255     xnn_status_success, xnn_define_quantized_tensor_value(
256                           subgraph, xnn_datatype_qint8, output_zero_point, output_scale, dims.size(), dims.data(),
257                           nullptr, 1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
258   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
259 
260   xnn_runtime_t runtime = nullptr;
261   ASSERT_EQ(xnn_status_success, xnn_define_leaky_relu(subgraph, negative_slope, input_id, output_id, /*flags=*/0));
262   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
263   ASSERT_NE(nullptr, runtime);
264   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
265   std::array<xnn_external_value, 2> external = {
266     xnn_external_value{input_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
267   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
268   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
269 
270   ASSERT_EQ(subgraph_output, operator_output);
271 }
272 
TEST_F(LeakyReLUTestQU8,matches_operator_api)273 TEST_F(LeakyReLUTestQU8, matches_operator_api)
274 {
275   const float negative_slope = std::uniform_real_distribution<float>(0.5f, 1.0f)(rng);
276 
277   const int32_t input_zero_point = u8dist(rng);
278   const float input_scale = scale_dist(rng);
279   const int32_t output_zero_point = u8dist(rng);
280   const float output_scale = scale_dist(rng);
281 
282   std::generate(input.begin(), input.end(), [&]() { return u8dist(rng); });
283   std::fill(operator_output.begin(), operator_output.end(), UINT8_C(0xA5));
284   std::fill(subgraph_output.begin(), subgraph_output.end(), UINT8_C(0x5A));
285 
286   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
287 
288   // Call operator API.
289   xnn_operator_t op = nullptr;
290   const xnn_status status = xnn_create_leaky_relu_nc_qu8(
291     channels, channels, channels, negative_slope,
292     input_zero_point, input_scale, output_zero_point, output_scale,
293     /*flags=*/0, &op);
294   if (status == xnn_status_unsupported_hardware) {
295     GTEST_SKIP();
296   }
297 
298   ASSERT_EQ(xnn_status_success, status);
299   ASSERT_NE(nullptr, op);
300   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
301 
302   ASSERT_EQ(
303     xnn_status_success,
304     xnn_setup_leaky_relu_nc_qu8(op, batch_size, input.data(), operator_output.data(), /*threadpool=*/nullptr));
305 
306   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
307 
308   // Call subgraph API.
309   xnn_subgraph_t subgraph = nullptr;
310   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(/*external_value_ids=*/2, /*flags=*/0, &subgraph));
311   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
312   input_id = XNN_INVALID_NODE_ID;
313   ASSERT_EQ(
314     xnn_status_success, xnn_define_quantized_tensor_value(
315                           subgraph, xnn_datatype_quint8, input_zero_point, input_scale, dims.size(), dims.data(),
316                           nullptr, 0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
317   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
318 
319   output_id = XNN_INVALID_NODE_ID;
320   ASSERT_EQ(
321     xnn_status_success, xnn_define_quantized_tensor_value(
322                           subgraph, xnn_datatype_quint8, output_zero_point, output_scale, dims.size(), dims.data(),
323                           nullptr, 1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
324   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
325 
326   xnn_runtime_t runtime = nullptr;
327   ASSERT_EQ(xnn_status_success, xnn_define_leaky_relu(subgraph, negative_slope, input_id, output_id, /*flags=*/0));
328   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
329   ASSERT_NE(nullptr, runtime);
330   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
331   std::array<xnn_external_value, 2> external = {
332     xnn_external_value{input_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
333   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
334   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
335 
336   ASSERT_EQ(subgraph_output, operator_output);
337 }
338