1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 #include <executorch/kernels/quantized/NativeFunctions.h> // Declares the operator
10 #include <executorch/runtime/core/exec_aten/exec_aten.h>
11 #include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h>
12 #include <executorch/runtime/core/exec_aten/testing_util/tensor_util.h>
13 #include <executorch/runtime/core/exec_aten/util/scalar_type_util.h>
14
15 #include <executorch/test/utils/DeathTest.h>
16
17 #include <gtest/gtest.h>
18 #include <limits>
19
20 using namespace ::testing;
21 using exec_aten::ArrayRef;
22 using exec_aten::Scalar;
23 using exec_aten::ScalarType;
24 using exec_aten::Tensor;
25 using torch::executor::native::choose_qparams_per_token_asymmetric_out;
26 using torch::executor::native::choose_qparams_tensor_out;
27 using torch::executor::testing::TensorFactory;
28
29 /// A generic smoke test that works for any dtype that supports ones() and
30 /// zeros().
31 template <ScalarType DTYPE>
test_dtype()32 void test_dtype() {
33 et_pal_init();
34 TensorFactory<ScalarType::Float> tf_float;
35 TensorFactory<ScalarType::Double> tf_double;
36 TensorFactory<ScalarType::Long> tf_long;
37
38 Tensor input = tf_float.make({2, 2}, {1.0, 2.5, 3.2, 15.4});
39 Tensor scale_out = tf_double.zeros({1});
40 Tensor zero_point_out = tf_long.zeros({1});
41 Tensor expected_scale = tf_double.make({1}, {0.0603922});
42 Tensor expected_zero_point = tf_long.make({1}, {0});
43
44 int64_t quant_min = 0;
45 int64_t quant_max = 255;
46
47 choose_qparams_tensor_out(
48 input, quant_min, quant_max, 0.0, DTYPE, scale_out, zero_point_out);
49
50 EXPECT_TENSOR_CLOSE(scale_out, expected_scale);
51 EXPECT_TENSOR_EQ(zero_point_out, expected_zero_point);
52 }
53
TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest,Float)54 TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest, Float) {
55 et_pal_init();
56 TensorFactory<ScalarType::Float> tf_float;
57 TensorFactory<ScalarType::Double> tf_double;
58 TensorFactory<ScalarType::Long> tf_long;
59
60 Tensor input = tf_float.make({2, 3}, {-0.5, 0.3, 1.2, 0.1, -0.8, 2.1});
61 Tensor scale_out = tf_double.zeros({2, 1});
62 Tensor zero_point_out = tf_long.zeros({2, 1});
63 Tensor expected_scale = tf_double.make({2, 1}, {0.00666667, 0.0113725485});
64 Tensor expected_zero_point = tf_long.make({2, 1}, {-53, -58});
65
66 choose_qparams_per_token_asymmetric_out(
67 input, ScalarType::Float, scale_out, zero_point_out);
68
69 EXPECT_TENSOR_CLOSE_WITH_TOL(scale_out, expected_scale, 1e-4, 1e-4);
70 EXPECT_TENSOR_EQ(zero_point_out, expected_zero_point);
71 }
72
TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest,ExtraDimFloat)73 TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest, ExtraDimFloat) {
74 et_pal_init();
75 TensorFactory<ScalarType::Float> tf_float;
76 TensorFactory<ScalarType::Double> tf_double;
77 TensorFactory<ScalarType::Long> tf_long;
78
79 Tensor input = tf_float.make({1, 2, 3}, {-0.5, 0.3, 1.2, 0.1, -0.8, 2.1});
80 Tensor scale_out = tf_double.zeros({1, 2, 1});
81 Tensor zero_point_out = tf_long.zeros({1, 2, 1});
82 Tensor expected_scale = tf_double.make({1, 2, 1}, {0.00666667, 0.0113725485});
83 Tensor expected_zero_point = tf_long.make({1, 2, 1}, {-53, -58});
84
85 choose_qparams_per_token_asymmetric_out(
86 input, ScalarType::Float, scale_out, zero_point_out);
87
88 EXPECT_TENSOR_CLOSE_WITH_TOL(scale_out, expected_scale, 1e-4, 1e-4);
89 EXPECT_TENSOR_EQ(zero_point_out, expected_zero_point);
90 }
91
TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest,LargeArray)92 TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest, LargeArray) {
93 et_pal_init();
94 TensorFactory<ScalarType::Float> tf_float;
95 TensorFactory<ScalarType::Double> tf_double;
96 TensorFactory<ScalarType::Long> tf_long;
97
98 Tensor input = tf_float.make(
99 {5, 17},
100 {0.41654, 0.26599, 0.4141, 0.83809, 0.02938, 0.12199, 0.53667,
101 0.799, 0.6606, 0.46657, 0.66142, 0.71787, 0.56098, 0.30202,
102 0.059377, 0.85473, 0.8017, 0.2703, 0.44299, 0.49045, 0.75581,
103 0.24429, 0.43906, 0.78652, 0.83885, 0.31034, 0.76534, 0.74422,
104 0.62549, 0.80006, 0.38144, 0.70652, 0.33553, 0.89136, 0.49126,
105 0.072916, 0.75654, 0.82057, 0.083848, 0.29753, 0.62718, 0.95579,
106 0.83097, 0.47293, 0.15666, 0.6248, 0.21672, 0.14626, 0.71834,
107 0.93664, 0.23382, 0.68931, 0.70866, 0.60545, 0.98648, 0.30335,
108 0.62439, 0.19195, 0.1923, 0.75638, 0.81114, 0.34778, 0.0070671,
109 0.50918, 0.19698, 0.19969, 0.57687, 0.062786, 0.18447, 0.22961,
110 0.29656, 0.25486, 0.75965, 0.11328, 0.86468, 0.21264, 0.99591,
111 0.75231, 0.97834, 0.042441, 0.39978, 0.9633, 0.9297, 0.12188,
112 0.73564});
113 Tensor scale_out = tf_double.zeros({5, 1});
114 Tensor zero_point_out = tf_long.zeros({5, 1});
115 Tensor expected_scale = tf_double.make(
116 {5, 1}, {0.0033519, 0.0034955, 0.0037482, 0.0038685, 0.0039055});
117 Tensor expected_zero_point =
118 tf_long.make({5, 1}, {-128, -128, -128, -128, -128});
119
120 choose_qparams_per_token_asymmetric_out(
121 input, ScalarType::Float, scale_out, zero_point_out);
122
123 EXPECT_TENSOR_CLOSE_WITH_TOL(scale_out, expected_scale, 1e-5, 1e-5);
124 EXPECT_TENSOR_EQ(zero_point_out, expected_zero_point);
125 }
126
TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest,DynamicShapeFloat)127 TEST(OpChooseQparamsPerTokenAsymmetricTensorOutTest, DynamicShapeFloat) {
128 et_pal_init();
129 TensorFactory<ScalarType::Float> tf_float;
130 TensorFactory<ScalarType::Double> tf_double;
131 TensorFactory<ScalarType::Long> tf_long;
132
133 Tensor input = tf_float.make({1, 2, 3}, {-0.5, 0.3, 1.2, 0.1, -0.8, 2.1});
134 Tensor scale_out = tf_double.zeros(
135 {1, 5, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
136 Tensor zero_point_out = tf_long.zeros(
137 {1, 5, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
138 Tensor expected_scale = tf_double.make({1, 2, 1}, {0.00666667, 0.0113725485});
139 Tensor expected_zero_point = tf_long.make({1, 2, 1}, {-53, -58});
140
141 choose_qparams_per_token_asymmetric_out(
142 input, ScalarType::Float, scale_out, zero_point_out);
143
144 EXPECT_TENSOR_CLOSE_WITH_TOL(scale_out, expected_scale, 1e-4, 1e-4);
145 EXPECT_TENSOR_EQ(zero_point_out, expected_zero_point);
146
147 Tensor new_input = tf_float.make(
148 {1, 5, 8},
149 {5.2254, 5.6041, 5.7653, -1.0126, -0.86126, -0.1606, -0.99196,
150 -1.067, 5.5913, 5.7713, 5.4901, -0.43128, -1.1759, -0.60466,
151 -0.82913, -0.73623, 5.4588, 5.4066, 5.2644, -0.89692, -0.16866,
152 -0.63169, -0.42352, -0.48866, 5.594, 5.5223, 5.5277, -0.17658,
153 -0.30669, -1.1777, -0.65389, -0.36422, 5.6375, 5.1857, 5.0743,
154 -0.46654, -0.43817, -0.41506, -0.94515, -0.60247});
155 Tensor new_expected_scale = tf_double.make(
156 {1, 5, 1}, {0.026793, 0.027244, 0.024924, 0.026556, 0.025814});
157 Tensor new_expected_zero_point =
158 tf_long.make({1, 5, 1}, {-88, -85, -92, -84, -91});
159
160 choose_qparams_per_token_asymmetric_out(
161 new_input, ScalarType::Float, scale_out, zero_point_out);
162
163 EXPECT_TENSOR_CLOSE_WITH_TOL(scale_out, new_expected_scale, 1e-4, 1e-4);
164 EXPECT_TENSOR_EQ(zero_point_out, new_expected_zero_point);
165 }
166