blob: 355d3ae6431f98e65ee5bb2c3227588755a10164 [file] [log] [blame]
Fredrik Svedberg1575b942020-08-18 13:19:18 +02001# Copyright (C) 2020 Arm Limited or its affiliates. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the License); you may
6# not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an AS IS BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16# Description:
17# Unit tests for fixed point math
18import numpy as np
19import pytest
20
21from ethosu.vela import fp_math
Louis Verhaardd7911c42020-08-25 13:36:41 +020022from ethosu.vela import scaling
Fredrik Svedberg1575b942020-08-18 13:19:18 +020023from ethosu.vela.softmax import SoftMax
24
25# Turn off black formatting for EXP_LUT to keep it compact
26# fmt: off
27
28EXP_LUT = [
29 0x000011c9, 0x000012b8, 0x000013b4, 0x000014bd, 0x000015d4, 0x000016fa, 0x0000182f, 0x00001975,
30 0x00001acb, 0x00001c34, 0x00001daf, 0x00001f3f, 0x000020e3, 0x0000229e, 0x00002470, 0x0000265a,
31 0x0000285e, 0x00002a7d, 0x00002cb9, 0x00002f13, 0x0000318c, 0x00003427, 0x000036e5, 0x000039c8,
32 0x00003cd1, 0x00004004, 0x00004361, 0x000046ec, 0x00004aa6, 0x00004e93, 0x000052b4, 0x0000570d,
33 0x00005ba1, 0x00006072, 0x00006583, 0x00006ada, 0x00007077, 0x00007661, 0x00007c9a, 0x00008327,
34 0x00008a0c, 0x0000914d, 0x000098f1, 0x0000a0fb, 0x0000a971, 0x0000b259, 0x0000bbb9, 0x0000c597,
35 0x0000cffa, 0x0000dae9, 0x0000e66b, 0x0000f288, 0x0000ff48, 0x00010cb3, 0x00011ad3, 0x000129b1,
36 0x00013957, 0x000149d0, 0x00015b26, 0x00016d65, 0x0001809b, 0x000194d2, 0x0001aa1a, 0x0001c080,
37 0x0001d814, 0x0001f0e4, 0x00020b03, 0x00022681, 0x00024371, 0x000261e7, 0x000281f7, 0x0002a3b5,
38 0x0002c73b, 0x0002ec9e, 0x000313f8, 0x00033d64, 0x000368fd, 0x000396e1, 0x0003c72e, 0x0003fa05,
39 0x00042f89, 0x000467dd, 0x0004a326, 0x0004e18e, 0x0005233d, 0x00056861, 0x0005b126, 0x0005fdbf,
40 0x00064e5f, 0x0006a33c, 0x0006fc8e, 0x00075a93, 0x0007bd89, 0x000825b3, 0x00089356, 0x000906bd,
41 0x00098035, 0x000a000f, 0x000a86a2, 0x000b1447, 0x000ba95f, 0x000c464e, 0x000ceb7c, 0x000d9959,
42 0x000e505a, 0x000f10f9, 0x000fdbb9, 0x0010b120, 0x001191c0, 0x00127e2f, 0x0013770b, 0x00147cfc,
43 0x001590b2, 0x0016b2e7, 0x0017e45d, 0x001925e1, 0x001a784c, 0x001bdc81, 0x001d536f, 0x001ede14,
44 0x00207d77, 0x002232af, 0x0023fee4, 0x0025e349, 0x0027e125, 0x0029f9ce, 0x002c2ead, 0x002e813e,
45 0x0030f30f, 0x003385c7, 0x00363b1f, 0x003914e9, 0x003c1510, 0x003f3d97, 0x004290a1, 0x00461066,
46 0x0049bf41, 0x004d9fad, 0x0051b444, 0x0055ffc3, 0x005a850f, 0x005f4730, 0x0064495a, 0x00698eeb,
47 0x006f1b6c, 0x0074f299, 0x007b185f, 0x008190de, 0x00886074, 0x008f8bae, 0x00971762, 0x009f08a2,
48 0x00a764c2, 0x00b03164, 0x00b9746e, 0x00c3341b, 0x00cd76fa, 0x00d843ed, 0x00e3a23b, 0x00ef9983,
49 0x00fc31d2, 0x010973a0, 0x011767d1, 0x012617cf, 0x01358d70, 0x0145d31c, 0x0156f3c1, 0x0168fadf,
50 0x017bf4a0, 0x018fedb6, 0x01a4f394, 0x01bb145a, 0x01d25ee1, 0x01eae2e5, 0x0204b0c8, 0x021fd9ed,
51 0x023c7091, 0x025a87f9, 0x027a343d, 0x029b8ac5, 0x02bea1ee, 0x02e3914d, 0x030a71c2, 0x03335d4e,
52 0x035e6f8d, 0x038bc56a, 0x03bb7d57, 0x03edb77c, 0x04229573, 0x045a3ae4, 0x0494cd29, 0x04d2739e,
53 0x051357c7, 0x0557a519, 0x059f8997, 0x05eb358d, 0x063adbcc, 0x068eb1ff, 0x06e6f049, 0x0743d21b,
54 0x07a595d9, 0x080c7d29, 0x0878cd66, 0x08eacf1a, 0x0962cf07, 0x09e11dcc, 0x0a661032, 0x0af1ffea,
55 0x0b854a9a, 0x0c20536f, 0x0cc3828e, 0x0d6f4584, 0x0e241040, 0x0ee25bb0, 0x0faaa7f2, 0x107d7b9e,
56 0x115b64be, 0x1244f787, 0x133ad1c6, 0x143d9885, 0x154df999, 0x166cac7a, 0x179a70d5, 0x18d81262,
57 0x1a266657, 0x1b864d4c, 0x1cf8b43e, 0x1e7e9316, 0x2018f0b9, 0x21c8e0b1, 0x238f851d, 0x256e1046,
58 0x2765c287, 0x2977ef55, 0x2ba5fab4, 0x2df15b8a, 0x305b9d83, 0x32e65ea3, 0x35935539, 0x38644d75,
59 0x3b5b2b74, 0x3e79eea7, 0x41c2addc, 0x45379f60, 0x48db159c, 0x4caf81fa, 0x50b7797f, 0x54f5af2b,
60 0x596cfe46, 0x5e2066e8, 0x631310c8, 0x684852d8, 0x6dc3a909, 0x7388c43d, 0x799b84b7, 0x7fffffff,
61]
62# fmt: on
63
64
65def test_saturating_rounding_mul():
66 i32info = np.iinfo(np.int32)
Diqing Zhong189f7482021-01-26 12:12:51 +010067 i16info = np.iinfo(np.int16)
68
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +020069 # Saturation
Diqing Zhong189f7482021-01-26 12:12:51 +010070 assert fp_math.saturating_rounding_mul32(i32info.min, i32info.min) == i32info.max
71 assert fp_math.saturating_rounding_mul32(i32info.min, i32info.max) == -i32info.max
72 assert fp_math.saturating_rounding_mul32(i32info.max, i32info.min) == -i32info.max
73
74 assert fp_math.saturating_rounding_mul16(i16info.min, i16info.min) == i16info.max
75 assert fp_math.saturating_rounding_mul16(i16info.min, i16info.max) == -i16info.max
76 assert fp_math.saturating_rounding_mul16(i16info.max, i16info.min) == -i16info.max
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +020077
78 # Multiply by zero
Diqing Zhong189f7482021-01-26 12:12:51 +010079 assert fp_math.saturating_rounding_mul32(0, fp_math.from_float(1.0)) == 0
80 assert fp_math.saturating_rounding_mul32(0, fp_math.from_float(-1.0)) == 0
81 assert fp_math.saturating_rounding_mul32(fp_math.from_float(1.0), 0) == 0
82 assert fp_math.saturating_rounding_mul32(fp_math.from_float(-1.0), 0) == 0
83
84 assert fp_math.saturating_rounding_mul16(0, i16info.max) == 0
85 assert fp_math.saturating_rounding_mul16(0, i16info.min) == 0
86 assert fp_math.saturating_rounding_mul16(i16info.max, 0) == 0
87 assert fp_math.saturating_rounding_mul16(i16info.min, 0) == 0
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +020088
89 # Multiply positive/negative
Diqing Zhong189f7482021-01-26 12:12:51 +010090 assert fp_math.saturating_rounding_mul32(fp_math.from_float(1.0), fp_math.from_float(1.0)) == fp_math.from_float(
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +020091 1.0, 5 + 5
92 )
Diqing Zhong189f7482021-01-26 12:12:51 +010093 assert fp_math.saturating_rounding_mul32(fp_math.from_float(-1.0), fp_math.from_float(1.0)) == fp_math.from_float(
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +020094 -1.0, 5 + 5
95 )
Diqing Zhong189f7482021-01-26 12:12:51 +010096 assert fp_math.saturating_rounding_mul32(fp_math.from_float(1.0), fp_math.from_float(-1.0)) == fp_math.from_float(
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +020097 -1.0, 5 + 5
98 )
Diqing Zhong189f7482021-01-26 12:12:51 +010099 assert fp_math.saturating_rounding_mul32(fp_math.from_float(-1.0), fp_math.from_float(-1.0)) == fp_math.from_float(
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +0200100 1.0, 5 + 5
101 )
102
103 # Rounding
Diqing Zhong189f7482021-01-26 12:12:51 +0100104 assert fp_math.saturating_rounding_mul32(fp_math.from_float(16.0), 1) == 1
105 assert fp_math.saturating_rounding_mul32(fp_math.from_float(-16.0), 1) == 0
106 assert fp_math.saturating_rounding_mul32(fp_math.from_float(16.0) - 1, 1) == 0
107 assert fp_math.saturating_rounding_mul32(fp_math.from_float(-16.0) - 1, 1) == -1
108
109 assert fp_math.saturating_rounding_mul16(fp_math.from_float(16.0, 21), 1) == 1
110 assert fp_math.saturating_rounding_mul16(fp_math.from_float(-16.0, 21), 1) == 0
111 assert fp_math.saturating_rounding_mul16(fp_math.from_float(16.0, 21) - 1, 1) == 0
112 assert fp_math.saturating_rounding_mul16(fp_math.from_float(-16.0, 21) - 1, 1) == -1
Fredrik Svedberg1575b942020-08-18 13:19:18 +0200113
114
115def test_shift_left():
116 i32info = np.iinfo(np.int32)
Diqing Zhong189f7482021-01-26 12:12:51 +0100117 i16info = np.iinfo(np.int16)
118 assert fp_math.shift_left32(1, i32info.bits) == i32info.max
119 assert fp_math.shift_left32(-1, i32info.bits) == i32info.min
120 assert fp_math.shift_left32(1, i32info.bits - 2) == (i32info.max + 1) / 2
121 assert fp_math.shift_left32(-1, i32info.bits - 2) == i32info.min // 2
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +0200122
Diqing Zhong189f7482021-01-26 12:12:51 +0100123 assert fp_math.shift_left16(1, i16info.bits) == i16info.max
124 assert fp_math.shift_left16(-1, i16info.bits) == i16info.min
125 assert fp_math.shift_left16(1, i16info.bits - 2) == (i16info.max + 1) / 2
126 assert fp_math.shift_left16(-1, i16info.bits - 2) == i16info.min // 2
127
128 assert fp_math.shift_left32(fp_math.from_float(1.0), 5) == i32info.max
129 assert fp_math.shift_left32(fp_math.from_float(-1.0), 5) == i32info.min
130 assert fp_math.shift_left32(fp_math.from_float(1.0), 4) == 16 * fp_math.from_float(1.0)
131 assert fp_math.shift_left32(fp_math.from_float(-1.0), 4) == 16 * fp_math.from_float(-1.0)
132
133 assert fp_math.shift_left16(fp_math.from_float(1.0, 21), 5) == i16info.max
134 assert fp_math.shift_left16(fp_math.from_float(-1.0, 21), 5) == i16info.min
135 assert fp_math.shift_left16(fp_math.from_float(1.0, 21), 4) == 16 * fp_math.from_float(1.0, 21)
136 assert fp_math.shift_left16(fp_math.from_float(-1.0, 21), 4) == 16 * fp_math.from_float(-1.0, 21)
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +0200137
138 with pytest.raises(AssertionError):
Diqing Zhong189f7482021-01-26 12:12:51 +0100139 fp_math.shift_left32(1, -1)
140 fp_math.shift_left16(1, -1)
Fredrik Svedberg1575b942020-08-18 13:19:18 +0200141
142
143def test_rounding_divide_by_pot():
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +0200144 # No remainder division
145 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0), 26) == 1
146 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0), 26) == -1
147
148 # Remainder rounding the result away from zero
149 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0), 27) == -1
150 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0), 27) == 1
151
152 # Remainder smaller than threshold to round the result away from zero
153 # Positive and negative edge cases
154 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0) - 1, 27) == 0
155 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0) + 1, 27) == 0
156 # Far from the edge
157 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0), 28) == 0
158 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0), 28) == 0
159
160 # Regular division - no remainder
161 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0), 4) == fp_math.from_float(1.0 / 16)
162 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0), 4) == fp_math.from_float(-1.0 / 16)
163
164 # Rounding/no rounding edge cases
165 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0) + (1 << 3) - 1, 4) == fp_math.from_float(1.0 / 16)
166 assert fp_math.rounding_divide_by_pot(fp_math.from_float(1.0) + (1 << 3), 4) == fp_math.from_float(1.0 / 16) + 1
167 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0) - (1 << 3) + 1, 4) == fp_math.from_float(-1.0 / 16)
168 assert fp_math.rounding_divide_by_pot(fp_math.from_float(-1.0) - (1 << 3), 4) == fp_math.from_float(-1.0 / 16) - 1
Fredrik Svedberg1575b942020-08-18 13:19:18 +0200169
170
171def test_saturating_rounding_multiply_by_pot():
172 i32info = np.iinfo(np.int32)
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +0200173 assert fp_math.saturating_rounding_multiply_by_pot(fp_math.from_float(1.0), 5) == i32info.max
174 assert fp_math.saturating_rounding_multiply_by_pot(fp_math.from_float(-1.0), 5) == i32info.min
175 assert fp_math.saturating_rounding_multiply_by_pot(fp_math.from_float(1.0) - 1, 5) == i32info.max - 32 + 1
176 assert fp_math.saturating_rounding_multiply_by_pot(fp_math.from_float(-1.0) + 1, 5) == -i32info.max + 32 - 1
177 assert fp_math.saturating_rounding_multiply_by_pot(fp_math.from_float(1.0), 4) == fp_math.from_float(1.0 * 16)
178 assert fp_math.saturating_rounding_multiply_by_pot(fp_math.from_float(-1.0), 4) == fp_math.from_float(-1.0 * 16)
Fredrik Svedberg1575b942020-08-18 13:19:18 +0200179
180
181def test_rescale():
Fredrik Svedberg2f6f3792020-09-10 16:12:33 +0200182 assert fp_math.rescale(5, 0, fp_math.from_float(1.0)) == fp_math.from_float(1.0, 0)
183 assert fp_math.rescale(5, 10, fp_math.from_float(1.0)) == fp_math.from_float(1.0, 10)
184 assert fp_math.rescale(5, 0, fp_math.from_float(-1.0)) == fp_math.from_float(-1.0, 0)
185 assert fp_math.rescale(5, 10, fp_math.from_float(-1.0)) == fp_math.from_float(-1.0, 10)
186
187 assert fp_math.rescale(5, 4, fp_math.from_float(32.0)) == fp_math.from_float(32.0, 4)
188 assert fp_math.rescale(5, 6, fp_math.from_float(32.0)) == fp_math.from_float(32.0, 6)
189 assert fp_math.rescale(5, 4, fp_math.from_float(-32.0)) == fp_math.from_float(-32.0, 4)
190 assert fp_math.rescale(5, 6, fp_math.from_float(-32.0)) == fp_math.from_float(-32.0, 6)
191
192 assert fp_math.rescale(5, 4, fp_math.from_float(31.9)) == fp_math.from_float(31.9, 4)
193 assert fp_math.rescale(5, 6, fp_math.from_float(31.9)) == fp_math.from_float(31.9, 6)
194 assert fp_math.rescale(5, 4, fp_math.from_float(-31.9)) == fp_math.from_float(-31.9, 4)
195 assert fp_math.rescale(5, 6, fp_math.from_float(-31.9)) == fp_math.from_float(-31.9, 6)
Fredrik Svedberg1575b942020-08-18 13:19:18 +0200196
197
198def test_exp():
199 sm = SoftMax(None)
200 for (expected, actual) in zip(EXP_LUT, sm.generate_exp_table(1.0, np.float32(0.05123165))):
201 assert actual == expected
Louis Verhaardd7911c42020-08-25 13:36:41 +0200202
203
204multiply_test_data = [
205 (0, 0, 0),
206 (0, 0.7, 0),
207 (0, 55.8, 0),
208 (6, 0.3, 2),
209 (200, 0, 0),
210 (1, 1, 1),
211 (1, 0.1, 0),
212 (1, 3.49, 3),
213 (1, 3.51, 4),
214 (27, 1, 27),
215 (13, 0.9, 12),
216 (3, 21.2, 64),
217 (1000, 2000, 2000000),
218 (32767, 32767, 32767 * 32767), # extreme values
219]
220
221
222@pytest.mark.parametrize("x, factor, expected", multiply_test_data)
223def test_multiply_by_quantized_multiplier(x, factor, expected):
224 scale, shift = scaling.quantise_scale(factor)
225 assert fp_math.multiply_by_quantized_multiplier(x, scale, shift) == expected
226 assert fp_math.multiply_by_quantized_multiplier(-x, scale, shift) == -expected
227 assert fp_math.multiply_by_quantized_multiplier(x, -scale, shift) == -expected
228 assert fp_math.multiply_by_quantized_multiplier(-x, -scale, shift) == expected
229
230
231def test_multiply_by_quantized_multiplier_int16_limits():
232 # Tests min/max limits of foreseen practical usage of multiply_by_quantized_multiplier
233 # for the purpose of calculating LUTs
234 for x in [-32768, 32767]:
235 for y in [-32768, 32767]:
236 scale, shift = scaling.quantise_scale(y)
237 assert fp_math.multiply_by_quantized_multiplier(x, scale, shift) == x * y