beginning to add tests
[sfpy.git] / testing.py
1 import multiprocessing
2 import time
3 import itertools
4 import re
5 import math
6 import random
7
8 import numpy
9
10 import sfpy
11 import softfloat
12 import softposit
13
14
15 posit_derepr = re.compile(r'Posit[0-9]+\((.*)\)')
16
17 def posit_get_classes(nbits):
18 if nbits == 8:
19 return sfpy.Posit8, softposit.posit8
20 elif nbits == 16:
21 return sfpy.Posit16, softposit.posit16
22 elif nbits == 32:
23 return sfpy.Posit32, softposit.posit32
24 else:
25 raise ValueError('no representation of {}-bit posits'.format(repr(nbits)))
26
27 def posit_get_fn(mod, nbits, fn):
28 return getattr(mod, 'p' + str(nbits) + '_' + fn)
29
30 def posit_get_rounding_arguments(nbits, extra=False):
31 sfpy_cls, sp_cls = posit_get_classes(nbits)
32 mask = (1 << nbits) - 1
33 inf = float('inf')
34 ninf = float('-inf')
35
36 cases = set()
37 for i in range(1 - (1<<(nbits-1)), (1<<(nbits-1)) - 1):
38 f1 = float(sfpy_cls(i & mask))
39 f2 = float(sfpy_cls((i+1) & mask))
40 mean = (f1 + f2) / 2
41 geomean = math.sqrt(f1 * f2)
42
43 cases.add(f1)
44 cases.add(float(numpy.nextafter(f1, ninf)))
45 cases.add(float(numpy.nextafter(f1, inf)))
46 cases.add(mean)
47 cases.add(float(numpy.nextafter(mean, ninf)))
48 cases.add(float(numpy.nextafter(mean, inf)))
49 cases.add(geomean)
50 cases.add(float(numpy.nextafter(geomean, ninf)))
51 cases.add(float(numpy.nextafter(geomean, inf)))
52
53 cases.add(f2)
54 cases.add(float(numpy.nextafter(f2, ninf)))
55 cases.add(float(numpy.nextafter(f2, inf)))
56
57 if extra:
58 morecases = set()
59 for case in cases:
60 if case != 0:
61 morecases.add(1 / case)
62 morecases.add(-case)
63
64 cases.update(morecases)
65
66 return sorted(cases)
67
68
69 def posit_test_representation_bits(nbits, it):
70 sfpy_cls, sp_cls = posit_get_classes(nbits)
71 for i in it:
72 sfpy_1 = sfpy_cls(i)
73 sfpy_2 = sfpy_cls.from_bits(i)
74 sp_1 = sp_cls(0)
75 sp_1.fromBits(i)
76
77 if not (
78 sfpy_1.bits == sfpy_2.bits == sp_1.v.v
79 and float(sfpy_1) == float(sfpy_2) == float(sp_1)
80 # problem: nan
81 # and int(sfpy_1) == int(sfpy_2) == int(sp_1)
82 ):
83 print('representation mismatch on bits: {}'.format(repr(i)))
84 return True
85
86 sfpy_3 = sfpy_cls(float(str(sfpy_1)))
87 sfpy_4 = sfpy_cls(float(str(sfpy_2)))
88 sfpy_5 = sfpy_cls(float(posit_derepr.match(repr(sfpy_1)).group(1)))
89 sfpy_6 = sfpy_cls(float(posit_derepr.match(repr(sfpy_2)).group(1)))
90 # problem: 'NaR' strings
91 # sp_2 = sp_cls(float(str(sp_1)))
92 # sp_3 = sp_cls(float(repr(sp_1)))
93
94 if not (
95 sfpy_1.bits == sfpy_3.bits == sfpy_4.bits == sfpy_5.bits == sfpy_6.bits # == sp_2.v.v == sp_3.v.v
96 ):
97 print('string representation mismatch on bits: {}'.format(repr(i)))
98 return True
99
100 return False
101
102 def posit_test_representation_floats(nbits, it):
103 sfpy_cls, sp_cls = posit_get_classes(nbits)
104 for f in it:
105 sfpy_1 = sfpy_cls(f)
106 sfpy_2 = sfpy_cls.from_double(f)
107 sp_1 = sp_cls(f)
108
109 if not (
110 sfpy_1.bits == sfpy_2.bits == sp_1.v.v
111 ):
112 print('bit representation mismatch on float: {}'.format(repr(i)))
113 return True
114
115 return False
116
117
118 def posit_test_neg(nbits, it):
119 fname = 'neg'
120 sfpy_cls, sp_cls = posit_get_classes(nbits)
121 fn = posit_get_fn(sfpy.posit, nbits, fname)
122 for i in it:
123 sfpy_0 = sfpy_cls(i)
124 sfpy_1 = -sfpy_0
125 sfpy_2 = sfpy_0.neg()
126 sfpy_3 = sfpy_cls(i)
127 sfpy_3.ineg()
128 sfpy_4 = fn(sfpy_0)
129
130 sp_0 = sp_cls(0)
131 sp_0.fromBits(i)
132 sp_1 = -sp_0
133
134 if not (
135 sp_1.v.v == sfpy_1.bits == sfpy_2.bits == sfpy_3.bits == sfpy_4.bits
136 ):
137 print('{} mismatch on bits: {}'.format(fname, repr(i)))
138 return True
139
140 return False
141
142
143 def dispatch_test(pool, workers, n, test, nbits, *it):
144 print('running {}'.format(repr(test)))
145 if not it:
146 return False
147
148 idx = 0
149 work_slots = []
150
151 for i in range(workers):
152 new_idx = idx + n
153 it0 = it[0][idx:new_idx]
154 if it0:
155 print(' dispatch {:d}:{:d}'.format(idx, new_idx))
156 work_slots.append(pool.apply_async(test, (nbits, it0, *(it[1:]))))
157 idx = new_idx
158
159 # crude sleep-wait loop to redispatch
160 working = True
161 while working:
162 for i, result in enumerate(work_slots):
163 if result.ready():
164 failed = result.get()
165 if failed:
166 return True
167 else:
168 new_idx = idx + n
169 it0 = it[0][idx:new_idx]
170 if it0:
171 print(' dispatch {:d}:{:d}'.format(idx, new_idx))
172 work_slots[i] = pool.apply_async(test, (nbits, it0, *(it[1:])))
173 else:
174 working = False
175 break
176 idx = new_idx
177 time.sleep(0.1)
178
179 for result in work_slots:
180 result.wait()
181 failed = result.get()
182 if failed:
183 return True
184
185 return False
186
187
188 if __name__ == '__main__':
189 import os
190 workers = os.cpu_count()
191
192 pool = multiprocessing.Pool(processes=workers, maxtasksperchild=1)
193
194 failed = (
195 dispatch_test(pool, workers, 256, posit_test_representation_bits, 8, range(1 << 8))
196 or dispatch_test(pool, workers, 10000, posit_test_representation_bits, 16, range(1 << 16))
197 )
198
199 args = posit_get_rounding_arguments(8, True)
200 dispatch_test(pool, workers, 10000, posit_test_representation_floats, 8, args)
201 args = posit_get_rounding_arguments(16, True)
202 dispatch_test(pool, workers, 100000, posit_test_representation_floats, 16, args)
203
204 failed = failed or (
205 dispatch_test(pool, workers, 256, posit_test_neg, 8, range(1 << 8))
206 or dispatch_test(pool, workers, 10000, posit_test_neg, 16, range(1 << 16))
207 )
208
209
210
211 print('Failed?', failed)
212
213 # print('testing Poist8 neg / abs...')
214 # for i in range(1 << 8):
215 # x = sfpy.Posit8(i)
216 # neg_x = -x
217 # minus_x = x * sfpy.Posit8(-1.0)
218
219 # if not neg_x.bits == minus_x.bits:
220 # print('-', x, neg_x, minus_x)
221
222 # neg_x2 = sfpy.posit.p8_neg(x)
223
224 # if not neg_x2.bits == minus_x.bits:
225 # print('p8_neg', x, neg_x2, minus_x)
226
227 # x.ineg()
228
229 # if not x.bits == minus_x.bits:
230 # print('ineg', x, minus_x)
231
232
233 # y = sfpy.Posit8(i)
234 # abs_y = abs(y)
235 # if y < sfpy.Posit8(0):
236 # ifneg_y = y * sfpy.Posit8(-1.0)
237 # else:
238 # ifneg_y = y * sfpy.Posit8(1.0)
239
240 # if not abs_y.bits == ifneg_y.bits:
241 # print('abs', y, abs_y, ifneg_y)
242
243 # abs_y2 = sfpy.posit.p8_abs(y)
244
245 # if not abs_y2.bits == ifneg_y.bits:
246 # print('p8_abs', y, abs_y2, ifneg_y)
247
248 # y.iabs()
249
250 # if not y.bits == ifneg_y.bits:
251 # print('iabs', y, ifneg_y)
252
253
254 # print('testing Poist16 neg / abs...')
255 # for i in range(1 << 16):
256 # x = sfpy.Posit16(i)
257 # neg_x = -x
258 # minus_x = x * sfpy.Posit16(-1.0)
259
260 # if not neg_x.bits == minus_x.bits:
261 # print('-', x, neg_x, minus_x)
262
263 # neg_x2 = sfpy.posit.p16_neg(x)
264
265 # if not neg_x2.bits == minus_x.bits:
266 # print('p16_neg', x, neg_x2, minus_x)
267
268 # x.ineg()
269
270 # if not x.bits == minus_x.bits:
271 # print('ineg', x, minus_x)
272
273
274 # y = sfpy.Posit16(i)
275 # abs_y = abs(y)
276 # if y < sfpy.Posit16(0):
277 # ifneg_y = y * sfpy.Posit16(-1.0)
278 # else:
279 # ifneg_y = y * sfpy.Posit16(1.0)
280
281 # if not abs_y.bits == ifneg_y.bits:
282 # print('abs', y, abs_y, ifneg_y)
283
284 # abs_y2 = sfpy.posit.p16_abs(y)
285
286 # if not abs_y2.bits == ifneg_y.bits:
287 # print('p16_abs', y, abs_y2, ifneg_y)
288
289 # y.iabs()
290
291 # if not y.bits == ifneg_y.bits:
292 # print('iabs', y, ifneg_y)
293
294
295 # print('testing Poist32 neg / abs...')
296 # for i in range(1 << 32):
297 # x = sfpy.Posit32(i)
298 # neg_x = -x
299 # minus_x = x * sfpy.Posit32(-1.0)
300
301 # if not neg_x.bits == minus_x.bits:
302 # print('-', x, neg_x, minus_x)
303
304 # neg_x2 = sfpy.posit.p32_neg(x)
305
306 # if not neg_x2.bits == minus_x.bits:
307 # print('p32_neg', x, neg_x2, minus_x)
308
309 # x.ineg()
310
311 # if not x.bits == minus_x.bits:
312 # print('ineg', x, minus_x)
313
314
315 # y = sfpy.Posit32(i)
316 # abs_y = abs(y)
317 # if y < sfpy.Posit32(0):
318 # ifneg_y = y * sfpy.Posit32(-1.0)
319 # else:
320 # ifneg_y = y * sfpy.Posit32(1.0)
321
322 # if not abs_y.bits == ifneg_y.bits:
323 # print('abs', y, abs_y, ifneg_y)
324
325 # abs_y2 = sfpy.posit.p32_abs(y)
326
327 # if not abs_y2.bits == ifneg_y.bits:
328 # print('p32_abs', y, abs_y2, ifneg_y)
329
330 # y.iabs()
331
332 # if not y.bits == ifneg_y.bits:
333 # print('iabs', y, ifneg_y)
334
335
336 # print('...done.')