beginning to add tests
authorBill Zorn <bill.zorn@gmail.com>
Thu, 27 Sep 2018 23:56:10 +0000 (16:56 -0700)
committerBill Zorn <bill.zorn@gmail.com>
Thu, 27 Sep 2018 23:56:10 +0000 (16:56 -0700)
testing.py

index eecca9a42308dd6f03b16aa0bbb2ae8f50c9f875..e6306d83877ea912d86c0340045c5aa6828a8a1e 100644 (file)
+import multiprocessing
+import time
+import itertools
+import re
+import math
+import random
+
+import numpy
+
 import sfpy
+import softfloat
+import softposit
 
 
-print('testing Poist8 neg / abs...')
-for i in range(1 << 8):
-    x = sfpy.Posit8(i)
-    neg_x = -x
-    minus_x = x * sfpy.Posit8(-1.0)
+posit_derepr = re.compile(r'Posit[0-9]+\((.*)\)')
 
-    if not neg_x.bits == minus_x.bits:
-        print('-', x, neg_x, minus_x)
+def posit_get_classes(nbits):
+    if nbits == 8:
+        return sfpy.Posit8, softposit.posit8
+    elif nbits == 16:
+        return sfpy.Posit16, softposit.posit16
+    elif nbits == 32:
+        return sfpy.Posit32, softposit.posit32
+    else:
+        raise ValueError('no representation of {}-bit posits'.format(repr(nbits)))
+
+def posit_get_fn(mod, nbits, fn):
+    return getattr(mod, 'p' + str(nbits) + '_' + fn)
+
+def posit_get_rounding_arguments(nbits, extra=False):
+    sfpy_cls, sp_cls = posit_get_classes(nbits)
+    mask = (1 << nbits) - 1
+    inf = float('inf')
+    ninf = float('-inf')
+
+    cases = set()
+    for i in range(1 - (1<<(nbits-1)), (1<<(nbits-1)) - 1):
+        f1 = float(sfpy_cls(i & mask))
+        f2 = float(sfpy_cls((i+1) & mask))
+        mean = (f1 + f2) / 2
+        geomean = math.sqrt(f1 * f2)
+
+        cases.add(f1)
+        cases.add(float(numpy.nextafter(f1, ninf)))
+        cases.add(float(numpy.nextafter(f1, inf)))
+        cases.add(mean)
+        cases.add(float(numpy.nextafter(mean, ninf)))
+        cases.add(float(numpy.nextafter(mean, inf)))
+        cases.add(geomean)
+        cases.add(float(numpy.nextafter(geomean, ninf)))
+        cases.add(float(numpy.nextafter(geomean, inf)))
+
+    cases.add(f2)
+    cases.add(float(numpy.nextafter(f2, ninf)))
+    cases.add(float(numpy.nextafter(f2, inf)))
+
+    if extra:
+        morecases = set()
+        for case in cases:
+            if case != 0:
+                morecases.add(1 / case)
+            morecases.add(-case)
+
+        cases.update(morecases)
+
+    return sorted(cases)
+
+
+def posit_test_representation_bits(nbits, it):
+    sfpy_cls, sp_cls = posit_get_classes(nbits)
+    for i in it:
+        sfpy_1 = sfpy_cls(i)
+        sfpy_2 = sfpy_cls.from_bits(i)
+        sp_1 = sp_cls(0)
+        sp_1.fromBits(i)
+
+        if not (
+                sfpy_1.bits == sfpy_2.bits == sp_1.v.v
+            and float(sfpy_1) == float(sfpy_2) == float(sp_1)
+            # problem: nan
+            # and int(sfpy_1) == int(sfpy_2) == int(sp_1)
+        ):
+            print('representation mismatch on bits: {}'.format(repr(i)))
+            return True
+
+        sfpy_3 = sfpy_cls(float(str(sfpy_1)))
+        sfpy_4 = sfpy_cls(float(str(sfpy_2)))
+        sfpy_5 = sfpy_cls(float(posit_derepr.match(repr(sfpy_1)).group(1)))
+        sfpy_6 = sfpy_cls(float(posit_derepr.match(repr(sfpy_2)).group(1)))
+        # problem: 'NaR' strings
+        # sp_2 = sp_cls(float(str(sp_1)))
+        # sp_3 = sp_cls(float(repr(sp_1)))
+
+        if not (
+                sfpy_1.bits == sfpy_3.bits == sfpy_4.bits == sfpy_5.bits == sfpy_6.bits # == sp_2.v.v == sp_3.v.v
+        ):
+            print('string representation mismatch on bits: {}'.format(repr(i)))
+            return True
+
+    return False
+
+def posit_test_representation_floats(nbits, it):
+    sfpy_cls, sp_cls = posit_get_classes(nbits)
+    for f in it:
+        sfpy_1 = sfpy_cls(f)
+        sfpy_2 = sfpy_cls.from_double(f)
+        sp_1 = sp_cls(f)
+
+    if not (
+                sfpy_1.bits == sfpy_2.bits == sp_1.v.v
+        ):
+            print('bit representation mismatch on float: {}'.format(repr(i)))
+            return True
+
+    return False
+
+
+def posit_test_neg(nbits, it):
+    fname = 'neg'
+    sfpy_cls, sp_cls = posit_get_classes(nbits)
+    fn = posit_get_fn(sfpy.posit, nbits, fname)
+    for i in it:
+        sfpy_0 = sfpy_cls(i)
+        sfpy_1 = -sfpy_0
+        sfpy_2 = sfpy_0.neg()
+        sfpy_3 = sfpy_cls(i)
+        sfpy_3.ineg()
+        sfpy_4 = fn(sfpy_0)
+
+        sp_0 = sp_cls(0)
+        sp_0.fromBits(i)
+        sp_1 = -sp_0
+
+        if not (
+                sp_1.v.v == sfpy_1.bits == sfpy_2.bits == sfpy_3.bits == sfpy_4.bits
+        ):
+            print('{} mismatch on bits: {}'.format(fname, repr(i)))
+            return True
+
+    return False
+
+
+def dispatch_test(pool, workers, n, test, nbits, *it):
+    print('running {}'.format(repr(test)))
+    if not it:
+        return False
+
+    idx = 0
+    work_slots = []
+
+    for i in range(workers):
+        new_idx = idx + n
+        it0 = it[0][idx:new_idx]
+        if it0:
+            print('  dispatch {:d}:{:d}'.format(idx, new_idx))
+            work_slots.append(pool.apply_async(test, (nbits, it0, *(it[1:]))))
+        idx = new_idx
+
+    # crude sleep-wait loop to redispatch
+    working = True
+    while working:
+        for i, result in enumerate(work_slots):
+            if result.ready():
+                failed = result.get()
+                if failed:
+                    return True
+                else:
+                    new_idx = idx + n
+                    it0 = it[0][idx:new_idx]
+                    if it0:
+                        print('  dispatch {:d}:{:d}'.format(idx, new_idx))
+                        work_slots[i] = pool.apply_async(test, (nbits, it0, *(it[1:])))
+                    else:
+                        working = False
+                        break
+                    idx = new_idx
+        time.sleep(0.1)
+
+    for result in work_slots:
+        result.wait()
+        failed = result.get()
+        if failed:
+            return True
+
+    return False
+
+
+if __name__ == '__main__':
+    import os
+    workers = os.cpu_count()
 
-    neg_x2 = sfpy.posit.p8_neg(x)
+    pool = multiprocessing.Pool(processes=workers, maxtasksperchild=1)
 
-    if not neg_x2.bits == minus_x.bits:
-        print('p8_neg', x, neg_x2, minus_x)
+    failed = (
+        dispatch_test(pool, workers, 256, posit_test_representation_bits, 8, range(1 << 8))
+        or dispatch_test(pool, workers, 10000, posit_test_representation_bits, 16, range(1 << 16))
+    )
 
-    x.ineg()
+    args = posit_get_rounding_arguments(8, True)
+    dispatch_test(pool, workers, 10000, posit_test_representation_floats, 8, args)
+    args = posit_get_rounding_arguments(16, True)
+    dispatch_test(pool, workers, 100000, posit_test_representation_floats, 16, args)
 
-    if not x.bits == minus_x.bits:
-        print('ineg', x, minus_x)
+    failed = failed or (
+        dispatch_test(pool, workers, 256, posit_test_neg, 8, range(1 << 8))
+        or dispatch_test(pool, workers, 10000, posit_test_neg, 16, range(1 << 16))
+    )
+    
+    
+    
+    print('Failed?', failed)
 
+    # print('testing Poist8 neg / abs...')
+    # for i in range(1 << 8):
+    #     x = sfpy.Posit8(i)
+    #     neg_x = -x
+    #     minus_x = x * sfpy.Posit8(-1.0)
 
-    y = sfpy.Posit8(i)
-    abs_y = abs(y)
-    if y < sfpy.Posit8(0):
-        ifneg_y = y * sfpy.Posit8(-1.0)
-    else:
-        ifneg_y = y * sfpy.Posit8(1.0)
+    #     if not neg_x.bits == minus_x.bits:
+    #         print('-', x, neg_x, minus_x)
 
-    if not abs_y.bits == ifneg_y.bits:
-        print('abs', y, abs_y, ifneg_y)
+    #     neg_x2 = sfpy.posit.p8_neg(x)
 
-    abs_y2 = sfpy.posit.p8_abs(y)
+    #     if not neg_x2.bits == minus_x.bits:
+    #         print('p8_neg', x, neg_x2, minus_x)
 
-    if not abs_y2.bits == ifneg_y.bits:
-        print('p8_abs', y, abs_y2, ifneg_y)
+    #     x.ineg()
 
-    y.iabs()
+    #     if not x.bits == minus_x.bits:
+    #         print('ineg', x, minus_x)
 
-    if not y.bits == ifneg_y.bits:
-        print('iabs', y, ifneg_y)
 
+    #     y = sfpy.Posit8(i)
+    #     abs_y = abs(y)
+    #     if y < sfpy.Posit8(0):
+    #         ifneg_y = y * sfpy.Posit8(-1.0)
+    #     else:
+    #         ifneg_y = y * sfpy.Posit8(1.0)
 
-print('testing Poist16 neg / abs...')
-for i in range(1 << 16):
-    x = sfpy.Posit16(i)
-    neg_x = -x
-    minus_x = x * sfpy.Posit16(-1.0)
+    #     if not abs_y.bits == ifneg_y.bits:
+    #         print('abs', y, abs_y, ifneg_y)
 
-    if not neg_x.bits == minus_x.bits:
-        print('-', x, neg_x, minus_x)
+    #     abs_y2 = sfpy.posit.p8_abs(y)
 
-    neg_x2 = sfpy.posit.p16_neg(x)
+    #     if not abs_y2.bits == ifneg_y.bits:
+    #         print('p8_abs', y, abs_y2, ifneg_y)
 
-    if not neg_x2.bits == minus_x.bits:
-        print('p16_neg', x, neg_x2, minus_x)
+    #     y.iabs()
 
-    x.ineg()
+    #     if not y.bits == ifneg_y.bits:
+    #         print('iabs', y, ifneg_y)
 
-    if not x.bits == minus_x.bits:
-        print('ineg', x, minus_x)
 
+    # print('testing Poist16 neg / abs...')
+    # for i in range(1 << 16):
+    #     x = sfpy.Posit16(i)
+    #     neg_x = -x
+    #     minus_x = x * sfpy.Posit16(-1.0)
 
-    y = sfpy.Posit16(i)
-    abs_y = abs(y)
-    if y < sfpy.Posit16(0):
-        ifneg_y = y * sfpy.Posit16(-1.0)
-    else:
-        ifneg_y = y * sfpy.Posit16(1.0)
+    #     if not neg_x.bits == minus_x.bits:
+    #         print('-', x, neg_x, minus_x)
 
-    if not abs_y.bits == ifneg_y.bits:
-        print('abs', y, abs_y, ifneg_y)
+    #     neg_x2 = sfpy.posit.p16_neg(x)
 
-    abs_y2 = sfpy.posit.p16_abs(y)
+    #     if not neg_x2.bits == minus_x.bits:
+    #         print('p16_neg', x, neg_x2, minus_x)
 
-    if not abs_y2.bits == ifneg_y.bits:
-        print('p16_abs', y, abs_y2, ifneg_y)
+    #     x.ineg()
 
-    y.iabs()
+    #     if not x.bits == minus_x.bits:
+    #         print('ineg', x, minus_x)
 
-    if not y.bits == ifneg_y.bits:
-        print('iabs', y, ifneg_y)
 
+    #     y = sfpy.Posit16(i)
+    #     abs_y = abs(y)
+    #     if y < sfpy.Posit16(0):
+    #         ifneg_y = y * sfpy.Posit16(-1.0)
+    #     else:
+    #         ifneg_y = y * sfpy.Posit16(1.0)
 
-print('testing Poist32 neg / abs...')
-for i in range(1 << 32):
-    x = sfpy.Posit32(i)
-    neg_x = -x
-    minus_x = x * sfpy.Posit32(-1.0)
+    #     if not abs_y.bits == ifneg_y.bits:
+    #         print('abs', y, abs_y, ifneg_y)
 
-    if not neg_x.bits == minus_x.bits:
-        print('-', x, neg_x, minus_x)
+    #     abs_y2 = sfpy.posit.p16_abs(y)
 
-    neg_x2 = sfpy.posit.p32_neg(x)
+    #     if not abs_y2.bits == ifneg_y.bits:
+    #         print('p16_abs', y, abs_y2, ifneg_y)
 
-    if not neg_x2.bits == minus_x.bits:
-        print('p32_neg', x, neg_x2, minus_x)
+    #     y.iabs()
 
-    x.ineg()
+    #     if not y.bits == ifneg_y.bits:
+    #         print('iabs', y, ifneg_y)
 
-    if not x.bits == minus_x.bits:
-        print('ineg', x, minus_x)
 
+    # print('testing Poist32 neg / abs...')
+    # for i in range(1 << 32):
+    #     x = sfpy.Posit32(i)
+    #     neg_x = -x
+    #     minus_x = x * sfpy.Posit32(-1.0)
 
-    y = sfpy.Posit32(i)
-    abs_y = abs(y)
-    if y < sfpy.Posit32(0):
-        ifneg_y = y * sfpy.Posit32(-1.0)
-    else:
-        ifneg_y = y * sfpy.Posit32(1.0)
+    #     if not neg_x.bits == minus_x.bits:
+    #         print('-', x, neg_x, minus_x)
+
+    #     neg_x2 = sfpy.posit.p32_neg(x)
+
+    #     if not neg_x2.bits == minus_x.bits:
+    #         print('p32_neg', x, neg_x2, minus_x)
+
+    #     x.ineg()
+
+    #     if not x.bits == minus_x.bits:
+    #         print('ineg', x, minus_x)
+
+
+    #     y = sfpy.Posit32(i)
+    #     abs_y = abs(y)
+    #     if y < sfpy.Posit32(0):
+    #         ifneg_y = y * sfpy.Posit32(-1.0)
+    #     else:
+    #         ifneg_y = y * sfpy.Posit32(1.0)
 
-    if not abs_y.bits == ifneg_y.bits:
-        print('abs', y, abs_y, ifneg_y)
+    #     if not abs_y.bits == ifneg_y.bits:
+    #         print('abs', y, abs_y, ifneg_y)
 
-    abs_y2 = sfpy.posit.p32_abs(y)
+    #     abs_y2 = sfpy.posit.p32_abs(y)
 
-    if not abs_y2.bits == ifneg_y.bits:
-        print('p32_abs', y, abs_y2, ifneg_y)
+    #     if not abs_y2.bits == ifneg_y.bits:
+    #         print('p32_abs', y, abs_y2, ifneg_y)
 
-    y.iabs()
+    #     y.iabs()
 
-    if not y.bits == ifneg_y.bits:
-        print('iabs', y, ifneg_y)
+    #     if not y.bits == ifneg_y.bits:
+    #         print('iabs', y, ifneg_y)
 
 
-print('...done.')
+    # print('...done.')