xref: /aosp_15_r20/external/XNNPACK/test/add2.cc (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
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 <stddef.h>
7 #include <stdint.h>
8 
9 #include <algorithm>
10 #include <array>
11 #include <functional>
12 #include <limits>
13 #include <memory>
14 #include <numeric>
15 #include <random>
16 #include <vector>
17 
18 #include <xnnpack.h>
19 #include <xnnpack/node-type.h>
20 #include <xnnpack/operator.h>
21 #include <xnnpack/subgraph.h>
22 
23 #include "subgraph-binary-tester.h"
24 #include <gtest/gtest.h>
25 
26 using Add2TestQS8 = BinaryTest<int8_t>;
27 using Add2TestQU8 = BinaryTest<uint8_t>;
28 using Add2TestF32 = BinaryTest<float>;
29 
TEST_F(Add2TestQS8,define)30 TEST_F(Add2TestQS8, define)
31 {
32   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
33 
34   xnn_subgraph_t subgraph = nullptr;
35   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(3, /*flags=*/0, &subgraph));
36   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
37 
38   const int32_t zero_point = i8dist(rng);
39   const float scale = scale_dist(rng);
40 
41   uint32_t input1_id = XNN_INVALID_NODE_ID;
42   ASSERT_EQ(
43     xnn_status_success, xnn_define_quantized_tensor_value(
44                           subgraph, xnn_datatype_qint8, zero_point, scale, input1_dims.size(), input1_dims.data(), nullptr,
45                           /*external_id=*/0, /*flags=*/0, &input1_id));
46   ASSERT_NE(input1_id, XNN_INVALID_NODE_ID);
47 
48   uint32_t input2_id = XNN_INVALID_NODE_ID;
49   ASSERT_EQ(
50     xnn_status_success, xnn_define_quantized_tensor_value(
51                           subgraph, xnn_datatype_qint8, zero_point, scale, input2_dims.size(), input2_dims.data(), nullptr,
52                           /*external_id=*/0, /*flags=*/0, &input2_id));
53   ASSERT_NE(input2_id, XNN_INVALID_NODE_ID);
54 
55   uint32_t output_id = XNN_INVALID_NODE_ID;
56   ASSERT_EQ(
57     xnn_status_success, xnn_define_quantized_tensor_value(
58                           subgraph, xnn_datatype_qint8, zero_point, scale, output_dims.size(), output_dims.data(), nullptr,
59                           XNN_INVALID_VALUE_ID, /*flags=*/0, &output_id));
60   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
61 
62   ASSERT_EQ(
63     xnn_status_success,
64     xnn_define_add2(subgraph, output_min, output_max, input1_id, input2_id, output_id, /*flags=*/0));
65 
66   ASSERT_EQ(subgraph->num_nodes, 1);
67   const struct xnn_node* node = &subgraph->nodes[0];
68   ASSERT_EQ(node->type, xnn_node_type_add2);
69   ASSERT_EQ(node->compute_type, xnn_compute_type_qs8);
70   ASSERT_EQ(node->activation.output_min, output_min);
71   ASSERT_EQ(node->activation.output_max, output_max);
72   ASSERT_EQ(node->num_inputs, 2);
73   ASSERT_EQ(node->inputs[0], input1_id);
74   ASSERT_EQ(node->inputs[1], input2_id);
75   ASSERT_EQ(node->num_outputs, 1);
76   ASSERT_EQ(node->outputs[0], output_id);
77   ASSERT_EQ(node->flags, 0);
78 }
79 
TEST_F(Add2TestQU8,define)80 TEST_F(Add2TestQU8, define)
81 {
82   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
83 
84   xnn_subgraph_t subgraph = nullptr;
85   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(3, /*flags=*/0, &subgraph));
86   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
87 
88   const int32_t zero_point = u8dist(rng);
89   const float scale = scale_dist(rng);
90 
91   uint32_t input1_id = XNN_INVALID_NODE_ID;
92   ASSERT_EQ(
93     xnn_status_success, xnn_define_quantized_tensor_value(
94                           subgraph, xnn_datatype_quint8, zero_point, scale, input1_dims.size(), input1_dims.data(), nullptr,
95                           /*external_id=*/0, /*flags=*/0, &input1_id));
96   ASSERT_NE(input1_id, XNN_INVALID_NODE_ID);
97 
98   uint32_t input2_id = XNN_INVALID_NODE_ID;
99   ASSERT_EQ(
100     xnn_status_success, xnn_define_quantized_tensor_value(
101                           subgraph, xnn_datatype_quint8, zero_point, scale, input2_dims.size(), input2_dims.data(), nullptr,
102                           /*external_id=*/0, /*flags=*/0, &input2_id));
103   ASSERT_NE(input2_id, XNN_INVALID_NODE_ID);
104 
105   uint32_t output_id = XNN_INVALID_NODE_ID;
106   ASSERT_EQ(
107     xnn_status_success, xnn_define_quantized_tensor_value(
108                           subgraph, xnn_datatype_quint8, zero_point, scale, output_dims.size(), output_dims.data(), nullptr,
109                           XNN_INVALID_VALUE_ID, /*flags=*/0, &output_id));
110   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
111 
112   ASSERT_EQ(
113     xnn_status_success,
114     xnn_define_add2(subgraph, output_min, output_max, input1_id, input2_id, output_id, /*flags=*/0));
115 
116   ASSERT_EQ(subgraph->num_nodes, 1);
117   const struct xnn_node* node = &subgraph->nodes[0];
118   ASSERT_EQ(node->type, xnn_node_type_add2);
119   ASSERT_EQ(node->compute_type, xnn_compute_type_qu8);
120   ASSERT_EQ(node->activation.output_min, output_min);
121   ASSERT_EQ(node->activation.output_max, output_max);
122   ASSERT_EQ(node->num_inputs, 2);
123   ASSERT_EQ(node->inputs[0], input1_id);
124   ASSERT_EQ(node->inputs[1], input2_id);
125   ASSERT_EQ(node->num_outputs, 1);
126   ASSERT_EQ(node->outputs[0], output_id);
127   ASSERT_EQ(node->flags, 0);
128 }
129 
TEST_F(Add2TestF32,define)130 TEST_F(Add2TestF32, define)
131 {
132   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
133 
134   xnn_subgraph_t subgraph = nullptr;
135   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(3, /*flags=*/0, &subgraph));
136   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
137 
138   uint32_t input1_id = XNN_INVALID_NODE_ID;
139   ASSERT_EQ(
140     xnn_status_success, xnn_define_tensor_value(
141                           subgraph, xnn_datatype_fp32, input1_dims.size(), input1_dims.data(), nullptr,
142                           /*external_id=*/0, /*flags=*/0, &input1_id));
143   ASSERT_NE(input1_id, XNN_INVALID_NODE_ID);
144 
145   uint32_t input2_id = XNN_INVALID_NODE_ID;
146   ASSERT_EQ(
147     xnn_status_success, xnn_define_tensor_value(
148                           subgraph, xnn_datatype_fp32, input2_dims.size(), input2_dims.data(), nullptr,
149                           /*external_id=*/0, /*flags=*/0, &input2_id));
150   ASSERT_NE(input2_id, XNN_INVALID_NODE_ID);
151 
152   uint32_t output_id = XNN_INVALID_NODE_ID;
153   ASSERT_EQ(
154     xnn_status_success,
155     xnn_define_tensor_value(
156       subgraph, xnn_datatype_fp32, output_dims.size(), output_dims.data(), nullptr, XNN_INVALID_VALUE_ID, /*flags=*/0, &output_id));
157   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
158 
159   ASSERT_EQ(
160     xnn_status_success,
161     xnn_define_add2(subgraph, output_min, output_max, input1_id, input2_id, output_id, /*flags=*/0));
162 
163   ASSERT_EQ(subgraph->num_nodes, 1);
164   const struct xnn_node* node = &subgraph->nodes[0];
165   ASSERT_EQ(node->type, xnn_node_type_add2);
166   ASSERT_EQ(node->compute_type, xnn_compute_type_fp32);
167   ASSERT_EQ(node->activation.output_min, output_min);
168   ASSERT_EQ(node->activation.output_max, output_max);
169   ASSERT_EQ(node->num_inputs, 2);
170   ASSERT_EQ(node->inputs[0], input1_id);
171   ASSERT_EQ(node->inputs[1], input2_id);
172   ASSERT_EQ(node->num_outputs, 1);
173   ASSERT_EQ(node->outputs[0], output_id);
174   ASSERT_EQ(node->flags, 0);
175 }
176 
TEST_F(Add2TestQS8,matches_operator_api)177 TEST_F(Add2TestQS8, matches_operator_api)
178 {
179   std::generate(input1.begin(), input1.end(), [&]() { return i8dist(rng); });
180   std::generate(input2.begin(), input2.end(), [&]() { return i8dist(rng); });
181   std::fill(operator_output.begin(), operator_output.end(), INT8_C(0xA5));
182   std::fill(subgraph_output.begin(), subgraph_output.end(), INT8_C(0xA5));
183 
184   const int32_t input1_zero_point = i8dist(rng);
185   const float input1_scale = scale_dist(rng);
186   const int32_t input2_zero_point = i8dist(rng);
187   const float input2_scale = scale_dist(rng);
188   const int32_t output_zero_point = i8dist(rng);
189   const float output_scale = scale_dist(rng);
190   const int8_t quantized_output_min = xnn_qs8_quantize(output_min, output_scale, output_zero_point);
191   const int8_t quantized_output_max = xnn_qs8_quantize(output_max, output_scale, output_zero_point);
192 
193   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
194 
195   xnn_operator_t op = nullptr;
196 
197   // Call operator API.
198   ASSERT_EQ(
199     xnn_status_success, xnn_create_add_nd_qs8(
200                           input1_zero_point, input1_scale, input2_zero_point, input2_scale, output_zero_point,
201                           output_scale, quantized_output_min, quantized_output_max, /*flags=*/0, &op));
202   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
203 
204   ASSERT_EQ(
205     xnn_status_success, xnn_setup_add_nd_qs8(
206                           op, input1_dims.size(), input1_dims.data(), input2_dims.size(), input2_dims.data(),
207                           input1.data(), input2.data(), operator_output.data(), nullptr /* thread pool */));
208 
209   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, nullptr /* thread pool */));
210 
211   // Call subgraph API.
212   xnn_subgraph_t subgraph = nullptr;
213   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(3, /*flags=*/0, &subgraph));
214   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
215 
216   uint32_t input1_id = XNN_INVALID_NODE_ID;
217   ASSERT_EQ(
218     xnn_status_success,
219     xnn_define_quantized_tensor_value(
220       subgraph, xnn_datatype_qint8, input1_zero_point, input1_scale, input1_dims.size(), input1_dims.data(), nullptr,
221       /*external_id=*/0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input1_id));
222   ASSERT_NE(input1_id, XNN_INVALID_NODE_ID);
223 
224   uint32_t input2_id = XNN_INVALID_NODE_ID;
225   ASSERT_EQ(
226     xnn_status_success,
227     xnn_define_quantized_tensor_value(
228       subgraph, xnn_datatype_qint8, input2_zero_point, input2_scale, input2_dims.size(), input2_dims.data(), nullptr,
229       /*external_id=*/1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input2_id));
230   ASSERT_NE(input2_id, XNN_INVALID_NODE_ID);
231 
232   uint32_t output_id = XNN_INVALID_NODE_ID;
233   ASSERT_EQ(
234     xnn_status_success, xnn_define_quantized_tensor_value(
235                           subgraph, xnn_datatype_qint8, output_zero_point, output_scale, output_dims.size(),
236                           output_dims.data(), nullptr, /*external_id=*/2,
237                           /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
238   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
239 
240   ASSERT_EQ(
241     xnn_status_success,
242     xnn_define_add2(subgraph, output_min, output_max, input1_id, input2_id, output_id, /*flags=*/0));
243 
244   xnn_runtime_t runtime = nullptr;
245   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
246   ASSERT_NE(nullptr, runtime);
247   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
248   std::array<xnn_external_value, 3> external = {
249     xnn_external_value{input1_id, input1.data()}, xnn_external_value{input2_id, input2.data()},
250     xnn_external_value{output_id, subgraph_output.data()}};
251   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
252   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
253 
254   // Check outputs match.
255   ASSERT_EQ(subgraph_output, operator_output);
256 }
257 
TEST_F(Add2TestQU8,matches_operator_api)258 TEST_F(Add2TestQU8, matches_operator_api)
259 {
260   std::generate(input1.begin(), input1.end(), [&]() { return u8dist(rng); });
261   std::generate(input2.begin(), input2.end(), [&]() { return u8dist(rng); });
262   std::fill(operator_output.begin(), operator_output.end(), UINT8_C(0xA5));
263   std::fill(subgraph_output.begin(), subgraph_output.end(), UINT8_C(0xA5));
264 
265   const int32_t input1_zero_point = u8dist(rng);
266   const float input1_scale = scale_dist(rng);
267   const int32_t input2_zero_point = u8dist(rng);
268   const float input2_scale = scale_dist(rng);
269   const int32_t output_zero_point = u8dist(rng);
270   const float output_scale = scale_dist(rng);
271   const uint8_t quantized_output_min = xnn_qu8_quantize(output_min, output_scale, output_zero_point);
272   const uint8_t quantized_output_max = xnn_qu8_quantize(output_max, output_scale, output_zero_point);
273 
274   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
275 
276   xnn_operator_t op = nullptr;
277 
278   // Call operator API.
279   ASSERT_EQ(
280     xnn_status_success, xnn_create_add_nd_qu8(
281                           input1_zero_point, input1_scale, input2_zero_point, input2_scale, output_zero_point,
282                           output_scale, quantized_output_min, quantized_output_max, /*flags=*/0, &op));
283   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
284 
285   ASSERT_EQ(
286     xnn_status_success, xnn_setup_add_nd_qu8(
287                           op, input1_dims.size(), input1_dims.data(), input2_dims.size(), input2_dims.data(),
288                           input1.data(), input2.data(), operator_output.data(), nullptr /* thread pool */));
289 
290   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, nullptr /* thread pool */));
291 
292   // Call subgraph API.
293   xnn_subgraph_t subgraph = nullptr;
294   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(3, /*flags=*/0, &subgraph));
295   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
296 
297   uint32_t input1_id = XNN_INVALID_NODE_ID;
298   ASSERT_EQ(
299     xnn_status_success,
300     xnn_define_quantized_tensor_value(
301       subgraph, xnn_datatype_quint8, input1_zero_point, input1_scale, input1_dims.size(), input1_dims.data(), nullptr,
302       /*external_id=*/0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input1_id));
303   ASSERT_NE(input1_id, XNN_INVALID_NODE_ID);
304 
305   uint32_t input2_id = XNN_INVALID_NODE_ID;
306   ASSERT_EQ(
307     xnn_status_success,
308     xnn_define_quantized_tensor_value(
309       subgraph, xnn_datatype_quint8, input2_zero_point, input2_scale, input2_dims.size(), input2_dims.data(), nullptr,
310       /*external_id=*/1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input2_id));
311   ASSERT_NE(input2_id, XNN_INVALID_NODE_ID);
312 
313   uint32_t output_id = XNN_INVALID_NODE_ID;
314   ASSERT_EQ(
315     xnn_status_success, xnn_define_quantized_tensor_value(
316                           subgraph, xnn_datatype_quint8, output_zero_point, output_scale, output_dims.size(),
317                           output_dims.data(), nullptr, /*external_id=*/2,
318                           /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
319   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
320 
321   ASSERT_EQ(
322     xnn_status_success,
323     xnn_define_add2(subgraph, output_min, output_max, input1_id, input2_id, output_id, /*flags=*/0));
324 
325   xnn_runtime_t runtime = nullptr;
326   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
327   ASSERT_NE(nullptr, runtime);
328   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
329   std::array<xnn_external_value, 3> external = {
330     xnn_external_value{input1_id, input1.data()}, xnn_external_value{input2_id, input2.data()},
331     xnn_external_value{output_id, subgraph_output.data()}};
332   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
333   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
334 
335   // Check outputs match.
336   ASSERT_EQ(subgraph_output, operator_output);
337 }
338 
TEST_F(Add2TestF32,matches_operator_api)339 TEST_F(Add2TestF32, matches_operator_api)
340 {
341   std::generate(input1.begin(), input1.end(), [&]() { return f32dist(rng); });
342   std::generate(input2.begin(), input2.end(), [&]() { return f32dist(rng); });
343   std::fill(operator_output.begin(), operator_output.end(), nanf(""));
344   std::fill(subgraph_output.begin(), subgraph_output.end(), nanf(""));
345 
346   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
347 
348   xnn_operator_t op = nullptr;
349 
350   // Call operator API.
351   ASSERT_EQ(xnn_status_success, xnn_create_add_nd_f32(output_min, output_max, 0, &op));
352   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
353 
354   ASSERT_EQ(
355     xnn_status_success, xnn_setup_add_nd_f32(
356                           op, input1_dims.size(), input1_dims.data(), input2_dims.size(), input2_dims.data(),
357                           input1.data(), input2.data(), operator_output.data(), nullptr /* thread pool */));
358 
359   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, nullptr /* thread pool */));
360 
361   // Call subgraph API.
362   xnn_subgraph_t subgraph = nullptr;
363   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(3, /*flags=*/0, &subgraph));
364   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
365 
366   uint32_t input1_id = XNN_INVALID_NODE_ID;
367   ASSERT_EQ(
368     xnn_status_success, xnn_define_tensor_value(
369                           subgraph, xnn_datatype_fp32, input1_dims.size(), input1_dims.data(), nullptr,
370                           /*external_id=*/0, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input1_id));
371   ASSERT_NE(input1_id, XNN_INVALID_NODE_ID);
372 
373   uint32_t input2_id = XNN_INVALID_NODE_ID;
374   ASSERT_EQ(
375     xnn_status_success, xnn_define_tensor_value(
376                           subgraph, xnn_datatype_fp32, input2_dims.size(), input2_dims.data(), nullptr,
377                           /*external_id=*/1, /*flags=*/XNN_VALUE_FLAG_EXTERNAL_INPUT, &input2_id));
378   ASSERT_NE(input2_id, XNN_INVALID_NODE_ID);
379 
380   uint32_t output_id = XNN_INVALID_NODE_ID;
381   ASSERT_EQ(
382     xnn_status_success,
383     xnn_define_tensor_value(
384       subgraph, xnn_datatype_fp32, output_dims.size(), output_dims.data(), nullptr, /*external_id=*/2,
385       /*flags=*/XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
386   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
387 
388   ASSERT_EQ(
389     xnn_status_success,
390     xnn_define_add2(subgraph, output_min, output_max, input1_id, input2_id, output_id, /*flags=*/0));
391 
392   xnn_runtime_t runtime = nullptr;
393   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
394   ASSERT_NE(nullptr, runtime);
395   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
396   std::array<xnn_external_value, 3> external = {
397     xnn_external_value{input1_id, input1.data()}, xnn_external_value{input2_id, input2.data()},
398     xnn_external_value{output_id, subgraph_output.data()}};
399   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
400   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
401 
402   // Check outputs match.
403   ASSERT_EQ(subgraph_output, operator_output);
404 }
405