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