├── PySigmoid.Math.egg-info ├── not-zip-safe ├── dependency_links.txt ├── top_level.txt ├── PKG-INFO └── SOURCES.txt ├── PySigmoid ├── Math │ ├── __init__.py │ ├── __pycache__ │ │ ├── Math.cpython-36.pyc │ │ └── __init__.cpython-36.pyc │ └── Math.py ├── __pycache__ │ ├── Posit.cpython-35.pyc │ ├── Posit.cpython-36.pyc │ ├── Quire.cpython-35.pyc │ ├── Quire.cpython-36.pyc │ ├── BitUtils.cpython-35.pyc │ ├── BitUtils.cpython-36.pyc │ └── __init__.cpython-36.pyc ├── __init__.py ├── BitUtils.py ├── Quire.py └── Posit.py ├── extra ├── taylor ├── triangle ├── newton.cpp ├── taylor.cpp └── triangle.cpp ├── .gitignore ├── example ├── __pycache__ │ ├── Math.cpython-36.pyc │ └── numpy.cpython-36.pyc ├── trig.py ├── triangle.py ├── typecast.py ├── numpy-example.py ├── sigmoid.py ├── accuracy.py ├── neuralnet2.py └── neuralnet.py ├── tests ├── __pycache__ │ ├── test_math.cpython-36.pyc │ ├── test_trig.cpython-36.pyc │ └── test_float.cpython-36.pyc ├── test_math.py ├── test_float.py ├── test_trig.py └── run_tests.py ├── setup.py ├── LICENSE └── README.md /PySigmoid.Math.egg-info/not-zip-safe: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PySigmoid/Math/__init__.py: -------------------------------------------------------------------------------- 1 | from .Math import * -------------------------------------------------------------------------------- /PySigmoid.Math.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PySigmoid.Math.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | PySigmoid 2 | -------------------------------------------------------------------------------- /extra/taylor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/extra/taylor -------------------------------------------------------------------------------- /extra/triangle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/extra/triangle -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | */*/.vscode 3 | PySigmoid.egg-info 4 | dist 5 | */*/__pycache__ 6 | PySigmoid.Math.egg-info -------------------------------------------------------------------------------- /example/__pycache__/Math.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/example/__pycache__/Math.cpython-36.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/Posit.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/Posit.cpython-35.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/Posit.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/Posit.cpython-36.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/Quire.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/Quire.cpython-35.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/Quire.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/Quire.cpython-36.pyc -------------------------------------------------------------------------------- /example/__pycache__/numpy.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/example/__pycache__/numpy.cpython-36.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_math.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/tests/__pycache__/test_math.cpython-36.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_trig.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/tests/__pycache__/test_trig.cpython-36.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_float.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/tests/__pycache__/test_float.cpython-36.pyc -------------------------------------------------------------------------------- /PySigmoid/Math/__pycache__/Math.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/Math/__pycache__/Math.cpython-36.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/BitUtils.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/BitUtils.cpython-35.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/BitUtils.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/BitUtils.cpython-36.pyc -------------------------------------------------------------------------------- /PySigmoid/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /example/trig.py: -------------------------------------------------------------------------------- 1 | from PySigmoid import * 2 | import PySigmoid.Math as Math 3 | set_posit_env(32, 2) 4 | a = Posit(number = 8) 5 | b = Math.sin(a) 6 | print(b) -------------------------------------------------------------------------------- /PySigmoid/Math/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightymercado/pysigmoid/HEAD/PySigmoid/Math/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /PySigmoid/__init__.py: -------------------------------------------------------------------------------- 1 | from .Posit import * 2 | from .Quire import * 3 | 4 | def set_posit_env(nbits, es): 5 | Posit.NBITS = nbits 6 | Posit.ES = es 7 | Quire.NBITS = nbits 8 | Quire.ES = es 9 | -------------------------------------------------------------------------------- /PySigmoid.Math.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: PySigmoid.Math 3 | Version: 0.3 4 | Summary: UNKNOWN 5 | Home-page: UNKNOWN 6 | Author: UNKNOWN 7 | Author-email: UNKNOWN 8 | License: UNKNOWN 9 | Description: UNKNOWN 10 | Platform: UNKNOWN 11 | -------------------------------------------------------------------------------- /PySigmoid.Math.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | README.md 2 | setup.py 3 | PySigmoid.Math.egg-info/PKG-INFO 4 | PySigmoid.Math.egg-info/SOURCES.txt 5 | PySigmoid.Math.egg-info/dependency_links.txt 6 | PySigmoid.Math.egg-info/not-zip-safe 7 | PySigmoid.Math.egg-info/top_level.txt 8 | PySigmoid/Math/Math.py 9 | PySigmoid/Math/__init__.py -------------------------------------------------------------------------------- /extra/newton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | //f(x) = x^2 - a 5 | //f'(x) = 2x 6 | int main() { 7 | double a, x; 8 | scanf("%lf", &a); 9 | x = a; 10 | double eps = FLOAT_MIN; 11 | for (int i = 0; i < 8; i++) { 12 | x = (x + a/x) / 2; 13 | } 14 | printf("%f\n", x); 15 | } -------------------------------------------------------------------------------- /example/triangle.py: -------------------------------------------------------------------------------- 1 | #from decimal import Decimal as D, getcontext 2 | from math import sqrt 3 | #getcontext().prec = 40 4 | from PySigmoid import * 5 | D = Posit 6 | set_posit_env(128, 4) 7 | 8 | def area(a, b, c): 9 | s = (a + b + c) / D(2) 10 | return sqrt(s) * sqrt(s-a) * sqrt(s-b) * sqrt(s-c) 11 | 12 | a = D(7) 13 | b = D(7) / D(2) + D(3) * D(2)**D(-111) 14 | c = b 15 | k = area(a,b,c) 16 | 17 | set_posit_env(16, 5) 18 | print(Posit(float(k))) -------------------------------------------------------------------------------- /extra/taylor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | double sin(double x) { 6 | double sum = 0; 7 | double mul = x; 8 | double den = 1; 9 | for (int i = 0; i < 1000; i++) { 10 | int sign = (i%2==0?1:-1); 11 | printf("%f\n", mul); 12 | sum += (i%2==0?1:-1) * mul; 13 | mul *= x * x; 14 | mul /= 2*(i+1)*(2*(i+1)+1); 15 | } 16 | return sum; 17 | } 18 | int main() { 19 | double x; 20 | scanf("%lf", &x); 21 | printf("%lf", sin(x)); 22 | } -------------------------------------------------------------------------------- /extra/triangle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | long double area(long double a, long double b, long double c) { 4 | long double s = (a + b + c) / 2.0L; 5 | return sqrtl(s)*sqrt(s-a)*sqrtl(s-b)*sqrtl(s-c); 6 | } 7 | 8 | long double powow(int w) { 9 | long double r = 1; 10 | while (w--) { 11 | r /= 2.0L; 12 | printf("%.70Lf\n", r); 13 | } 14 | return r; 15 | } 16 | int main() { 17 | long double a = 7.0L; 18 | long double b = 7.0L / 2.0L + 3.0L * powow(111); 19 | long double c = b; 20 | printf("%0.200Lf", area(a, b, c)); 21 | } -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='PySigmoid', 4 | version='0.3', 5 | description='A Python Library that Implements Posit', 6 | url='https://github.com/mightymercado/PySigmoid', 7 | author='Ken Mercado', 8 | author_email='mightymercado@gmail.com', 9 | license='MIT', 10 | packages=['PySigmoid'], 11 | install_requires=['spfpm'], 12 | python_requires='>=3', 13 | zip_safe=False) 14 | 15 | setup(name='PySigmoid.Math', 16 | version='0.3', 17 | packages=['PySigmoid.Math'], 18 | zip_safe=False) 19 | -------------------------------------------------------------------------------- /example/typecast.py: -------------------------------------------------------------------------------- 1 | from PySigmoid import * 2 | from math import * 3 | set_posit_env(32, 2) 4 | # float to posit 5 | print(Posit(3.2)) 6 | # int to posit 7 | print(Posit(31238912839)) 8 | # string to posit 9 | print(Posit("-2.333344")) 10 | 11 | # posit to float 12 | print(float(Posit("3.2"))) 13 | # posit to int 14 | print(int(Posit(31238912839))) 15 | # posit to string 16 | print(str(Posit("-2.333344"))) 17 | 18 | a = Posit(1) 19 | b = Posit(2) 20 | # Addition 21 | print(a+b) 22 | # Subtraction 23 | print(a-b) 24 | # Division 25 | print(a/b) 26 | # multiplication 27 | print(a*b) 28 | # square root 29 | print(sqrt(a)) 30 | # power 31 | print(a**b) 32 | 33 | # operation with other types works too! 34 | print(2.5 + a + 3 + b) -------------------------------------------------------------------------------- /example/numpy-example.py: -------------------------------------------------------------------------------- 1 | from PySigmoid import Posit as P, set_posit_env 2 | import numpy as np 3 | set_posit_env(64, 3) 4 | 5 | # input matrix 6 | reg = [ 7 | [0, 2, 3, 4], 8 | [3, 4, 5, 6], 9 | [1, 3, 2, 11], 10 | [3, 5, 3, 4] 11 | ] 12 | 13 | # functiont to cast a regular matrix to a posit matrix 14 | posify = lambda x: [list(map(P, e)) for e in x] 15 | pos = posify(reg) # convert to posit 16 | 17 | b = np.matrix(pos) 18 | a = np.matrix(pos) 19 | 20 | # addition 21 | print(b+b) 22 | print(a+a) 23 | # subtraction 24 | print(b-b) 25 | print(a-a) 26 | # multiplication 27 | print(b*b) 28 | print(a*a) 29 | # transpose 30 | print(np.transpose(b)) 31 | print(np.transpose(a)) 32 | # exponentiate 33 | print(b**2) 34 | # dot product 35 | print(a.dot(b)) -------------------------------------------------------------------------------- /example/sigmoid.py: -------------------------------------------------------------------------------- 1 | from PySigmoid import Posit, set_posit_env 2 | from math import log 3 | from decimal import Decimal as D, getcontext 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | from math import exp 7 | from copy import deepcopy as dc 8 | 9 | # 500 digits of precision 10 | getcontext().prec = 50 11 | def decacc(x, y): 12 | if x == y: 13 | return D('inf') 14 | else: 15 | try: 16 | return -abs((x / y).log10()).log10() 17 | except: 18 | return 0 19 | 20 | set_posit_env(8, 0) 21 | 22 | start = -100 23 | end = 100 24 | xx = [] 25 | yy = [] 26 | yy2 = [] 27 | 28 | def sigmoid(x): 29 | return 1 / (1 + exp(-x)) 30 | 31 | while start <= end: 32 | q = Posit(start) 33 | q = q.sigmoid() 34 | xx.append(start) 35 | yy.append(float(q)) 36 | yy2.append(sigmoid(start)) 37 | start += 0.01 38 | 39 | # Plot 40 | 41 | a = plt.plot(xx, yy, color = 'red', label = "Posit Flip Sign, Shift Right Two Times") 42 | b = plt.plot(xx, yy2, color = 'blue', label = "Floating Point 1/(1 + e^-x)") 43 | plt.legend() 44 | 45 | plt.show() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ken Mercado 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_math.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from math import * 3 | from PySigmoid import Posit, Quire, set_posit_env, Math 4 | 5 | env = [ 6 | [8, 2], 7 | [8, 3], 8 | [8, 4], 9 | [16, 0], 10 | [16, 1], 11 | [16, 3], 12 | [16, 4], 13 | [32, 0], 14 | [32, 1], 15 | [32, 2], 16 | [32, 3] 17 | ] 18 | 19 | class TestMath(unittest.TestCase): 20 | def test_log(self): 21 | start = 10 22 | diff = 0.1 23 | for i in range(100): 24 | for e in env: 25 | set_posit_env(e[0], e[1]) 26 | b = Math.log(Posit(start)) 27 | self.assertEqual(Posit(log(float(Posit(start).get_value()))), b) 28 | start += diff 29 | 30 | def test_sqrt(self): 31 | start = 10 32 | diff = 0.1 33 | for i in range(100): 34 | for e in env: 35 | set_posit_env(e[0], e[1]) 36 | b = Math.sqrt(Posit(start)) 37 | self.assertEqual(Posit(sqrt(float(Posit(start).get_value()))), b) 38 | start += diff 39 | 40 | def test_pow(self): 41 | i = 10.0 42 | while i < 10: 43 | j = -10.0 44 | while j < 10.0: 45 | for e in env: 46 | set_posit_env(e[0], e[1]) 47 | b = Posit(i) ** Posit(j) 48 | self.assertEqual(Posit(float(Posit(i).get_value()) ** float(Posit(j).get_value())), b) 49 | j+=0.1 50 | i+=0.1 -------------------------------------------------------------------------------- /tests/test_float.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from v8cffi import shortcuts 3 | from PySigmoid import Posit, set_posit_env 4 | 5 | shortcuts.set_up() 6 | class TestFloat(unittest.TestCase): 7 | def test_small_cast(self): 8 | ctx = shortcuts.get_context() 9 | ctx.load_libs(['../PySigmoid/posit-javascript/js/decimallookupv2.js']) 10 | start = 1.5 11 | diff = 0.0001 12 | for i in range(10000): 13 | res = eval(ctx.run_script('convertDToP("{}")'.format(str(start)))) 14 | for j in res: 15 | nbits = j["ps"] 16 | es = j["es"] 17 | set_posit_env(nbits, es) 18 | a, b = bin(Posit(start).number)[2:], bin(int(j["posit"], 2))[2:] 19 | self.assertEqual(a, b) 20 | start += diff 21 | 22 | def test_large_cast(self): 23 | ctx = shortcuts.get_context() 24 | ctx.load_libs(['../PySigmoid/posit-javascript/js/decimallookupv2.js']) 25 | start = 1231239123.1928282 26 | diff = 123123.298383 27 | for i in range(10000): 28 | res = eval(ctx.run_script('convertDToP("{}")'.format(str(start)))) 29 | for j in res: 30 | nbits = j["ps"] 31 | es = j["es"] 32 | set_posit_env(nbits, es) 33 | a, b = bin(Posit(start).number)[2:], bin(int(j["posit"], 2))[2:] 34 | self.assertEqual(a, b) 35 | start += diff 36 | 37 | if __name__ == '__main__': 38 | unittest.main() -------------------------------------------------------------------------------- /example/accuracy.py: -------------------------------------------------------------------------------- 1 | s 2 | from PySigmoid import Posit, set_posit_env 3 | from math import log 4 | from decimal import Decimal as D, getcontext 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | from copy import deepcopy 8 | from mpmath import mpf, mp 9 | mp.prec = 7 10 | 11 | # 500 digits of precision 12 | getcontext().prec = 50 13 | def decacc(x, y): 14 | if x == y: 15 | return D('inf') 16 | else: 17 | try: 18 | return -abs((x / y).log10()).log10() 19 | except: 20 | return 0 21 | 22 | import numpy 23 | p = Posit() 24 | p.set_bit_pattern(p.maxpos) 25 | p = -p 26 | p = p.get_value() 27 | 28 | q = Posit() 29 | q.set_bit_pattern(q.maxpos) 30 | q = q.get_value() 31 | 32 | xx = [] 33 | yy = [] 34 | while p <= q: 35 | t = deepcopy(p) 36 | x = Posit(float(p)).get_value() 37 | y = p 38 | d = decacc(x, y) 39 | xx.append(float(p)) 40 | yy.append(float(d)) 41 | p += D("1") 42 | print(p) 43 | 44 | # Plot 45 | plt.plot(xx, yy, alpha=1) 46 | plt.xlabel('x') 47 | plt.ylabel('Decimal Accuracy of x when converting to Posit') 48 | plt.show() 49 | 50 | set_posit_env(8, 1) 51 | 52 | p = Posit() 53 | p.set_bit_pattern(p.maxpos) 54 | p = -p 55 | p = p.get_value() 56 | 57 | q = Posit() 58 | q.set_bit_pattern(q.maxpos), 59 | q = q.get_value() 60 | 61 | xx = [] 62 | yy = [] 63 | while p <= q: 64 | t = deepcopy(p) 65 | x = Posit(float(p)).get_value() 66 | y = p 67 | d = decacc(x, y) 68 | xx.append(float(p)) 69 | yy.append(float(d)) 70 | p += D("1") 71 | print(p) 72 | 73 | # Plot 74 | plt.plot(xx, yy, alpha=1) 75 | plt.xlabel('x') 76 | plt.ylabel('Decimal Accuracy of x when converting to Posit') 77 | plt.show() -------------------------------------------------------------------------------- /PySigmoid/Math/Math.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | # wrapped using FixedPoint arithmetic via Quire 4 | # tan, sqrt, sin, log, intpower, exp, cos, atan, asin, acos 5 | 6 | def sqrt(x): 7 | if type(x) != Posit: 8 | raise Exception("Argument must be posit") 9 | q = Quire(x) 10 | q.q = q.q.sqrt() 11 | return Posit(q) 12 | 13 | def sin(x): 14 | if type(x) != Posit: 15 | raise Exception("Argument must be posit") 16 | q = Quire(x) 17 | q.q = q.q.sin() 18 | return Posit(q) 19 | 20 | def cos(x): 21 | if type(x) != Posit: 22 | raise Exception("Argument must be posit") 23 | q = Quire(x) 24 | q.q = q.q.cos() 25 | return Posit(q) 26 | 27 | def tan(x): 28 | if type(x) != Posit: 29 | raise Exception("Argument must be posit") 30 | q = Quire(x) 31 | q.q = q.q.tan() 32 | return Posit(q) 33 | 34 | def asin(x): 35 | if type(x) != Posit: 36 | raise Exception("Argument must be posit") 37 | q = Quire(x) 38 | q.q = q.q.asin() 39 | return Posit(q) 40 | 41 | def acos(x): 42 | if type(x) != Posit: 43 | raise Exception("Argument must be posit") 44 | q = Quire(x) 45 | q.q = q.q.acos() 46 | return Posit(q) 47 | 48 | def atan(x): 49 | if type(x) != Posit: 50 | raise Exception("Argument must be posit") 51 | q = Quire(x) 52 | q.q = q.q.atan() 53 | return Posit(q) 54 | 55 | def log(x): 56 | if type(x) != Posit: 57 | raise Exception("Argument must be posit") 58 | q = Quire(x) 59 | q.q = q.q.log() 60 | return Posit(q) 61 | 62 | def exp(x): 63 | if type(x) != Posit: 64 | raise Exception("Argument must be posit") 65 | q = Quire(x) 66 | q.q = q.q.exp() 67 | return Posit(q) 68 | 69 | def pi(nbits, es): 70 | # uses Bailey–Borwein–Plouffe formula 71 | q = Quire(0, nbits = nbits, es = es) 72 | q.q = q.family.pi 73 | return Posit(q) 74 | 75 | def intpower(x): 76 | if type(x) != Posit: 77 | raise Exception("Argument must be posit") 78 | q = Quire(x) 79 | q.q = q.q.intpower() 80 | return Posit(q) 81 | 82 | from PySigmoid import Quire, Posit 83 | from FixedPoint import * 84 | from copy import deepcopy -------------------------------------------------------------------------------- /PySigmoid/BitUtils.py: -------------------------------------------------------------------------------- 1 | def twosComplement(n, bits): 2 | n = ((1 << bits) - n) % (1 << bits) 3 | return n 4 | 5 | def onesComplement(n, bits): 6 | return (1 << bits) - n - 1 7 | 8 | def lastSetBit(n): 9 | return n.bit_length() - 1 10 | 11 | def lastUnsetBit(n): 12 | return lastSetBit(onesComplement(n, n.bit_length())) 13 | 14 | def ceilLog2(n): 15 | x = lastSetBit(n) 16 | return x+int(n!=1<> i) & 1 32 | 33 | def toggleBit(n, i): 34 | return n ^ (1 << i) 35 | 36 | # creates mask of n consecutive bits, and k trailing zeroes 37 | def createMask(n, k): 38 | return ((1 << n) - 1) << k 39 | 40 | # k = end position 41 | # n = number of bits 42 | # x = integer 43 | def extractBits(x, n, k): 44 | return (x & createMask(n, k)) >> k 45 | 46 | def printBits(n, bits): 47 | b = bin(n)[2:] 48 | l = len(b) 49 | print((bits - l) * "0" + b) 50 | 51 | def countBits(n): 52 | return n.bit_length() 53 | 54 | def countTrailingZeroes(n): 55 | if n == 0: 56 | return 0 57 | return (n & -n).bit_length() - 1 58 | 59 | def removeTrailingZeroes(n): 60 | if n == 0: 61 | return n 62 | return n >> countTrailingZeroes(n) 63 | 64 | def align(a, b): 65 | a_length = countBits(a) 66 | b_length = countBits(b) 67 | if a_length > b_length: 68 | b <<= (a_length - b_length) 69 | elif a_length < b_length: 70 | a <<= (b_length - a_length) 71 | trailing_a = countTrailingZeroes(a) 72 | trailing_b = countTrailingZeroes(b) 73 | a >>= min(trailing_a, trailing_b) 74 | b >>= min(trailing_a, trailing_b) 75 | return (a, b) 76 | 77 | def floorLog2FivePow(x): 78 | # works for 1 <= x <= 100 79 | # https://stackoverflow.com/questions/47229444/how-to-compute-floorlog25x-without-floating-point-arithmetic-or-long-integ/47229742#47229742 80 | log2_5 = 23219281 81 | scale = 10000000 82 | result = x * log2_5 83 | output = result // scale -------------------------------------------------------------------------------- /tests/test_trig.py: -------------------------------------------------------------------------------- 1 | env = [ 2 | [8, 2], 3 | [8, 3], 4 | [8, 4], 5 | [16, 0], 6 | [16, 1], 7 | [16, 3], 8 | [16, 4], 9 | [32, 0], 10 | [32, 1], 11 | [32, 2], 12 | [32, 3] 13 | ] 14 | 15 | import unittest 16 | from math import * 17 | from PySigmoid import Posit, Quire, set_posit_env, Math 18 | 19 | class TestTrig(unittest.TestCase): 20 | def test_small_sin(self): 21 | start = -10 22 | diff = 0.1 23 | for i in range(100): 24 | for e in env: 25 | set_posit_env(e[0], e[1]) 26 | b = Math.sin(Posit(start)) 27 | self.assertEqual(Posit(sin(float(Posit(start).get_value()))), b) 28 | start += diff 29 | 30 | def test_large_sin(self): 31 | start = -1000 32 | diff = 100 33 | for i in range(100): 34 | for e in env: 35 | set_posit_env(e[0], e[1]) 36 | b = Math.sin(Posit(start)) 37 | self.assertEqual(Posit(sin(float(Posit(start).get_value()))), b) 38 | start += diff 39 | 40 | def test_small_cos(self): 41 | start = -10 42 | diff = 0.1 43 | for i in range(100): 44 | for e in env: 45 | set_posit_env(e[0], e[1]) 46 | b = Math.cos(Posit(start)) 47 | self.assertEqual(Posit(cos(float(Posit(start).get_value()))), b) 48 | start += diff 49 | 50 | def test_large_cos(self): 51 | start = -100 52 | diff = 10 53 | for i in range(100): 54 | for e in env: 55 | set_posit_env(e[0], e[1]) 56 | b = Math.cos(Posit(start)) 57 | self.assertEqual(Posit(cos(float(Posit(start).get_value()))), b) 58 | start += diff 59 | 60 | def test_small_tan(self): 61 | start = -10 62 | diff = 0.1 63 | for i in range(100): 64 | for e in env: 65 | set_posit_env(e[0], e[1]) 66 | b = Math.tan(Posit(start)) 67 | self.assertEqual(Posit(tan(float(Posit(start).get_value()))), b) 68 | start += diff 69 | 70 | def test_large_tan(self): 71 | start = -100 72 | diff = 10 73 | for i in range(100): 74 | for e in env: 75 | set_posit_env(e[0], e[1]) 76 | b = Math.tan(Posit(start)) 77 | self.assertEqual(Posit(tan(float(Posit(start).get_value()))), b) 78 | start += diff 79 | 80 | def test_asin(self): 81 | start = 0 82 | diff = 0.00000001 83 | for i in range(2): 84 | for e in env: 85 | set_posit_env(e[0], e[1]) 86 | b = Math.asin(Posit(start)) 87 | Posit(asin(float(Posit(start).get_value()))) 88 | start += diff 89 | 90 | if __name__ == '__main__': 91 | unittest.main() -------------------------------------------------------------------------------- /example/neuralnet2.py: -------------------------------------------------------------------------------- 1 | from numpy import exp, array, random, dot 2 | 3 | 4 | class NeuralNetwork(): 5 | def __init__(self): 6 | # Seed the random number generator, so it generates the same numbers 7 | # every time the program runs. 8 | random.seed(1) 9 | 10 | # We model a single neuron, with 3 input connections and 1 output connection. 11 | # We assign random weights to a 3 x 1 matrix, with values in the range -1 to 1 12 | # and mean 0. 13 | self.synaptic_weights = 2 * random.random((3, 1)) - 1 14 | 15 | # The Sigmoid function, which describes an S shaped curve. 16 | # We pass the weighted sum of the inputs through this function to 17 | # normalise them between 0 and 1. 18 | def __sigmoid(self, x): 19 | # print("A", x) 20 | #print("B", 1 / (1 + exp(-x))) 21 | return 1 / (1 + exp(-x)) 22 | 23 | # The derivative of the Sigmoid function. 24 | # This is the gradient of the Sigmoid curve. 25 | # It indicates how confident we are about the existing weight. 26 | def __sigmoid_derivative(self, x): 27 | return x * (1 - x) 28 | 29 | # We train the neural network through a process of trial and error. 30 | # Adjusting the synaptic weights each time. 31 | def train(self, training_set_inputs, training_set_outputs, number_of_training_iterations): 32 | for iteration in xrange(number_of_training_iterations): 33 | # Pass the training set through our neural network (a single neuron). 34 | output = self.think(training_set_inputs) 35 | 36 | # Calculate the error (The difference between the desired output 37 | # and the predicted output). 38 | error = training_set_outputs - output 39 | 40 | # Multiply the error by the input and again by the gradient of the Sigmoid curve. 41 | # This means less confident weights are adjusted more. 42 | # This means inputs, which are zero, do not cause changes to the weights. 43 | #print("Z", self.__sigmoid_derivative(output)) 44 | adjustment = dot(training_set_inputs.T, error * self.__sigmoid_derivative(output)) 45 | # Adjust the weights. 46 | self.synaptic_weights += adjustment 47 | 48 | # The neural network thinks. 49 | def think(self, inputs): 50 | # Pass inputs through our neural network (our single neuron). 51 | return self.__sigmoid(dot(inputs, self.synaptic_weights)) 52 | 53 | if __name__ == "__main__": 54 | 55 | #Intialise a single neuron neural network. 56 | neural_network = NeuralNetwork() 57 | 58 | print "Random starting synaptic weights: " 59 | print neural_network.synaptic_weights 60 | 61 | # The training set. We have 4 examples, each consisting of 3 input values 62 | # and 1 output value. 63 | training_set_inputs = array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]]) 64 | training_set_outputs = array([[0, 1, 1, 0]]).T 65 | 66 | # Train the neural network using a training set. 67 | # Do it 10,000 times and make small adjustments each time. 68 | neural_network.train(training_set_inputs, training_set_outputs, 10000) 69 | 70 | print "New synaptic weights after training: " 71 | print neural_network.synaptic_weights 72 | 73 | # Test the neural network with a new situation. 74 | print "Considering new situation [1, 0, 0] -> ?: " 75 | 76 | print(neural_network.think(array([1, 0, 0]))) 77 | print(neural_network.think(array([0, 0, 0]))) 78 | print(neural_network.think(array([1, 1, 0]))) 79 | print(neural_network.think(array([0, 1, 0]))) 80 | -------------------------------------------------------------------------------- /PySigmoid/Quire.py: -------------------------------------------------------------------------------- 1 | from copy import * 2 | from FixedPoint import * 3 | class Quire(object): 4 | def __init__(self, number = 0, nbits = None, es = None): 5 | if nbits != None and es != None: 6 | self.nbits = nbits 7 | self.es = es 8 | elif type(number) == Posit: 9 | self.nbits = number.nbits 10 | self.es = number.es 11 | elif type(Quire.NBITS) is not int or type(Quire.ES) is not int: 12 | raise Exception("Set posit envrionemnt first using set_posit_env(nbits, es)") 13 | else: 14 | self.nbits = Quire.NBITS 15 | self.es = Quire.ES 16 | 17 | self.fraction_bits = (2 * self.nbits - 4) * 2 ** self.es + 1 18 | self.integer_bits = (2 * self.nbits - 4) * 2 ** self.es + 1 + 30 19 | 20 | if type(number) == Posit: 21 | self.family = FXfamily(n_bits = self.fraction_bits, n_intbits = self.integer_bits) 22 | if number.number == 0: 23 | self.q = FXnum(0, family=self.family) 24 | elif number.number == number.inf: 25 | raise Exception("Cannot convert to fixed point") 26 | 27 | sign, regime, exponent, fraction = number.decode() 28 | 29 | f = FXnum(fraction, family=self.family) 30 | n = countBits(fraction) - 1 31 | 32 | self.q = ((-1)**sign * FXnum(2, family=self.family)**Decimal(2**self.es * regime + exponent - n) * FXnum(f, family = self.family)) 33 | elif type(number) == int: 34 | self.family = FXfamily(n_bits = self.fraction_bits, n_intbits = self.integer_bits) 35 | self.q = FXnum(val = number, family= self.family) 36 | else: 37 | raise "Unsupported conversion to quire" 38 | 39 | # set quire to 0 40 | def clear(self): 41 | self.q = FXnum(val = 0, family = self.family) 42 | 43 | def add_posit_product(self, p1, p2): 44 | if type(p1) == Posit and type(p2) == Posit: 45 | self.q += Quire(p1) * Quire(p2) 46 | else: 47 | raise Exception("Arguments must be posit") 48 | 49 | def sub_posit_product(self, p1, p2): 50 | if type(p1) == Posit and type(p2) == Posit: 51 | self.q -= Quire(p1) * Quire(p2) 52 | else: 53 | raise Exception("Arguments must be posit") 54 | 55 | def set_int(self, n): 56 | if type(n) == int: 57 | self.q = FXnum(val = n, family = self.family) 58 | else: 59 | raise "Not int" 60 | 61 | def __add__(self, other): 62 | ret = deepcopy(self) 63 | ret.q += other.q 64 | return ret 65 | 66 | def __sub__(self, other): 67 | ret = deepcopy(self) 68 | ret.q -= other.q 69 | return ret 70 | 71 | def __mul__(self, other): 72 | ret = deepcopy(self) 73 | ret.q *= other.q 74 | return ret 75 | 76 | def __pow__(self, other): 77 | ret = deepcopy(self) 78 | ret.q = ret.q**other.q 79 | return ret 80 | 81 | def __truediv__(self, other): 82 | ret = deepcopy(self) 83 | ret.q /= other.q 84 | return ret 85 | 86 | def __str__(self): 87 | return self.q.__str__() 88 | 89 | def reduce2PI(self): 90 | sign = -1 if self.q.scaledval < 0 else 1 91 | self.q.scaledval = abs(self.q.scaledval) 92 | y = copy(self.q) 93 | t = y / (2 * self.family.pi) 94 | t.scaledval &= onesComplement((1 << self.fraction_bits) - 1, self.integer_bits + self.fraction_bits) 95 | self.q = (y - t * (2 * self.family.pi)) 96 | self.q = sign * self.q 97 | 98 | from .Posit import * 99 | from .BitUtils import * 100 | -------------------------------------------------------------------------------- /example/neuralnet.py: -------------------------------------------------------------------------------- 1 | from numpy import exp, array, random, dot 2 | import numpy as np 3 | from PySigmoid import * 4 | set_posit_env(8, 0) 5 | from math import * 6 | 7 | def posify(x): 8 | if type(x) == np.ndarray: 9 | if len(x.shape) == 1: 10 | return np.array([Posit(y) for y in x]) 11 | else: 12 | return np.array([[Posit(z) for z in y] for y in x]) 13 | else: 14 | return Posit(x) 15 | 16 | class NeuralNetwork(): 17 | def __init__(self): 18 | # Seed the random number generator, so it generates the same numbers 19 | # every time the program runs. 20 | random.seed(1) 21 | 22 | # We model a single neuron, with 3 input connections and 1 output connection. 23 | # We assign random weights to a 3 x 1 matrix, with values in the range -1 to 1 24 | # and mean 0. 25 | self.synaptic_weights = 2 * random.random((3, 1)) - 1 26 | self.synaptic_weights = posify(self.synaptic_weights) 27 | 28 | # The Sigmoid function, which describes an S shaped curve. 29 | # We pass the weighted sum of the inputs through this function to 30 | # normalise them between 0 and 1. 31 | def __sigmoid(self, x): 32 | if type(x) == np.ndarray: 33 | if len(x.shape) == 1: 34 | return array([y.sigmoid() for y in x]) 35 | else: 36 | return array([[z.sigmoid() for z in y] for y in x]) 37 | else: 38 | return x.sigmoid() 39 | 40 | # The derivative of the Sigmoid function. 41 | # This is the gradient of the Sigmoid curve. 42 | # It indicates how confident we are about the existing weight. 43 | def __sigmoid_derivative(self, x): 44 | return x * (1 - x) 45 | 46 | # We train the neural network through a process of trial and error. 47 | # Adjusting the synaptic weights each time. 48 | def train(self, training_set_inputs, training_set_outputs, number_of_training_iterations): 49 | for iteration in range(number_of_training_iterations): 50 | # Pass the training set through our neural network (a single neuron). 51 | output = self.think(training_set_inputs) 52 | 53 | # Calculate the error (The difference between the desired output 54 | # and the predicted output). 55 | error = training_set_outputs - output 56 | 57 | # Multiply the error by the input and again by the gradient of the Sigmoid curve. 58 | # This means less confident weights are adjusted more. 59 | # This means inputs, which are zero, do not cause changes to the weights. 60 | 61 | adjustment = dot(training_set_inputs.T, error * self.__sigmoid_derivative(output)) 62 | # Adjust the weights. 63 | self.synaptic_weights += adjustment 64 | 65 | # The neural network thinks. 66 | def think(self, inputs): 67 | # Pass inputs through our neural network (our single neuron). 68 | return self.__sigmoid(dot(inputs, self.synaptic_weights)) 69 | 70 | if __name__ == "__main__": 71 | neural_network = NeuralNetwork() 72 | 73 | print("Random starting synaptic weights: ") 74 | print(neural_network.synaptic_weights) 75 | 76 | # The training set. We have 4 examples, each consisting of 3 input values 77 | # and 1 output value. 78 | training_set_inputs = posify(array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]])) 79 | training_set_outputs = posify(array([[0, 1, 1, 0]])).T 80 | 81 | # Train the neural network using a training set. 82 | # Do it 10,000 times and make small adjustments each time. 83 | neural_network.train(training_set_inputs, training_set_outputs, 10000) 84 | 85 | print("New synaptic weights after training: ") 86 | print(neural_network.synaptic_weights) 87 | 88 | # Test the neural network with a new situation. 89 | print("Considering new situation [1, 0, 0] -> ?: ") 90 | print(neural_network.think(posify(array([1, 0, 0])))) 91 | print(neural_network.think(posify(array([0, 0, 0])))) 92 | print(neural_network.think(posify(array([1, 1, 0])))) 93 | print(neural_network.think(posify(array([0, 1, 0])))) -------------------------------------------------------------------------------- /tests/run_tests.py: -------------------------------------------------------------------------------- 1 | import BitUtils 2 | import Posit 3 | 4 | def test(name, func, tests): 5 | print("-" * 10) 6 | print("Running {} tests for \033[;;1m{}()".format(len(tests), name)) 7 | passed = 0 8 | failed = [] 9 | 10 | for i in range(len(tests)): 11 | if func(*tests[i][0]) == tests[i][1]: 12 | passed += 1 13 | else: 14 | failed.append(i) 15 | 16 | print("\033[1;32m{}\033[0;37m out of {} tests passed".format(passed, len(tests))) 17 | for i in failed: 18 | print("\033[1;31mTest {} failed: \033[0;37mInput: {}. Expected: {}. Actual: {}.".format(i, tests[i][0], func(*tests[i][0]), tests[i][1])) 19 | 20 | # comprehensive tests 21 | def test_method(name, generator, method, tests): 22 | print("-" * 10) 23 | print("Running {} tests for \033[;;1m{}()".format(len(tests), name)) 24 | passed = 0 25 | failed = [] 26 | for i in range(len(tests)): 27 | if getattr(generator(*(tests[i][0])), method)() == tests[i][1]: 28 | passed += 1 29 | else: 30 | failed.append(i) 31 | 32 | print("\033[1;32m{}\033[0;37m out of {} tests passed".format(passed, len(tests))) 33 | for i in failed: 34 | print("\033[1;31mTest {} failed: \033[0;37mInput: {}. Expected: {}. Actual: {}.".format(i, tests[i][0], getattr(generator(*(tests[i][0])), method)(), tests[i][1])) 35 | 36 | print("-" * 10) 37 | 38 | # comprehensive tests 39 | def test_operator(name, generator, method, tests): 40 | print("-" * 10) 41 | print("Running {} tests for \033[;;1m{}()".format(len(tests), name)) 42 | passed = 0 43 | failed = [] 44 | for i in range(len(tests)): 45 | a = generator(*(tests[i][0])) 46 | b = generator(*(tests[i][1])) 47 | c = generator(*(tests[i][2])) 48 | d = getattr(a, method)(b) 49 | if d == c: 50 | passed += 1 51 | else: 52 | failed.append(i) 53 | 54 | print("\033[1;32m{}\033[0;37m out of {} tests passed".format(passed, len(tests))) 55 | for i in failed: 56 | a = generator(*(tests[i][0])) 57 | b = generator(*(tests[i][1])) 58 | c = generator(*(tests[i][2])) 59 | d = getattr(a, method)(b) 60 | print("\033[1;31mTest {} failed: \033[0;37mInput: {}. Expected: {}. Actual: {}.".format(i, (a,b), c, d)) 61 | 62 | print("-" * 10) 63 | 64 | 65 | def make_posit_from_bit_pattern(es, bit_pattern): 66 | p = Posit.Posit(len(bit_pattern), es) 67 | p.set_bit_pattern(bit_pattern) 68 | return p 69 | 70 | 71 | test("lastSetBit", BitUtils.lastSetBit, [ 72 | ((int("1", 2),), 0), 73 | ((int("110", 2),), 2), 74 | ((int("1101010", 2),), 6), 75 | ((int("100101000", 2),), 8), 76 | ]) 77 | test("lastUnsetBit", BitUtils.lastUnsetBit, [ 78 | ((int("1", 2),), -1), 79 | ((int("110", 2),), 0), 80 | ((int("1101010", 2),), 4), 81 | ((int("100101000", 2),), 7), 82 | ]) 83 | test("ceilLog2", BitUtils.ceilLog2, [ 84 | ((4,), 2), 85 | ((5,), 3), 86 | ((7,), 3), 87 | ((17,), 5), 88 | ]) 89 | test("removeTrailingZeroes", BitUtils.removeTrailingZeroes, [ 90 | ((int("111000000", 2),), int("111", 2)), 91 | ((int("111000100", 2),), int("1110001", 2)) 92 | ]) 93 | test("align", BitUtils.align, [ 94 | ((int("1", 2), int("10", 2)), (int("1", 2), int("1", 2))), 95 | ((int("1000", 2), int("1001000", 2)), (int("1000", 2), int("1001", 2))), 96 | ((int("1000000010101", 2), int("1001000", 2)), (int("1000000010101", 2), int("1001000000000", 2))), 97 | ]) 98 | 99 | test_method("Posit.decode", make_posit_from_bit_pattern, "decode", [ 100 | ((3, "0111111001001111"), (0, 5, 2, 47)), 101 | ((4, "0000001010101100"), (0, -5, 5, 11)), 102 | ((4, "0111110100"), (0, 4, 8, 1)), 103 | ((8, "0111110"), (0, 4, 0, 1)), 104 | ((8, "0000000"), None), 105 | ((8, "1000000"), None) 106 | ]) 107 | 108 | test_operator("Posit.__add__", make_posit_from_bit_pattern, "__add__", [ 109 | ((1, "00001011"), (1, "00001111"), (1, "00010010")), 110 | ((1, "00000010"), (1, "00000011"), (1, "00000100")), 111 | ]) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `PySigmoid` 2 | (This is no longer maintained, but feel free to make Pull Requests). 3 | PS This contains pretty bad Python code because it was written a long time ago. 4 | > A Python implementation of [Posits] and Quires with linear algebra applications. Posits were 5 | proposed by [John Gustafson]. The sigmoid in PySigmoid is motivated by the [application of Posits in 8-bit neural networks]. Gustafson introduced this number format as a replacement for the IEEE 754 floating point, which has many issues. 6 | 7 | # How to install 8 | > pip3 install PySigmoid 9 | 10 | # Posit Examples 11 | ```python 12 | from PySigmoid import * 13 | from math import * 14 | set_posit_env(32, 2) 15 | # float to posit 16 | print(Posit(3.2)) 17 | # int to posit 18 | print(Posit(31238912839)) 19 | # string to posit 20 | print(Posit("-2.333344")) 21 | 22 | # posit to float 23 | print(float(Posit("3.2"))) 24 | # posit to int 25 | print(int(Posit(31238912839))) 26 | # posit to string 27 | print(str(Posit("-2.333344"))) 28 | 29 | a = Posit(1) 30 | b = Posit(2) 31 | # Addition 32 | print(a+b) 33 | # Subtraction 34 | print(a-b) 35 | # Division 36 | print(a/b) 37 | # multiplication 38 | print(a*b) 39 | # square root 40 | print(sqrt(a)) 41 | # power 42 | print(a**b) 43 | 44 | # operation with other types works too! 45 | print(2.5 + a + 3 + b) 46 | ``` 47 | 48 | # Issues with IEEE Floats 49 | 1. **Wasted Bit Patterns** - 32-bit IEEE floating point has around sixteen million ways to represent Not-A-Number 50 | (NaN) while 64-bit floating point has nine quadrillion. A NaN is an exception value for invalid operations such as division by zero. 51 | 2. **Mathematically Incorrect** - The format specifies two zeroes - a negative and positive zero - which have different behaviors. 52 | 3. **Overflows to ± inf and underflows to 0** - Overflowing to ± inf increases the relative error by an infinite factor, while underflowing to 0 loses sign information. 53 | 4. **Complicated Circuitry** - One of the reasons why IEEE floating points have complicated circuitry is because 54 | the standard defines support for denormalized numbers. Denormalized floating point numbers have a hidden bit of 0 instead of 1. 55 | 5. **No Gradual Overflow and Fixed Accuracy** - If accuracy is defined as the number of significand bits, IEEE 56 | floating point have fixed accuracy for all numbers except denormalized numbers because the number of signficand 57 | digits is fixed. Denormalized numbers are characterized by a decreased number of significand digits when the value approaches zero as a result of having a zero hidden bit. Denormalized numbers fill the underflow gap (i.e. 58 | the gap between zero and the least non-zero values). The counterpart for gradual underflow is gradual overflow 59 | which does not exist in IEEE floating points. 60 | 61 | # Advantages of Posits 62 | 1. **Economical** - No bit patterns are redundant. There is one representation for infinity denoted as ± inf and zero. 63 | All other bit patterns are valid distinct non-zero real numbers. ± inf serves as a replacement for NaN. 64 | 2. **Mathematical Elegant** - There is only one representation for zero. 65 | 3. **Gradual Underflow and Overflow** - The number of significand digits are not fixed in posits. In fact, a greater magnitude exponent, automatically reduces the number of significand digits which allows for both gradual overflow and underflow. 66 | 4. **Simpler Circuitry** - There are no denormalized numbers. The hidden bit is always 1. 67 | 5. **Tapered Accuracy** - Tapered accuracy is when values with small exponent have more digits of accuracy and values with large exponents have less digits of accuracy. This concept was first introduced by Morris (1971) in his paper ”Tapered Floating Point: A New Floating-Point Representation”. 68 | 69 | [John Gustafson]: https://en.wikipedia.org/wiki/John_Gustafson_(scientist) 70 | [application of Posits in 8-bit neural networks]: https://github.com/interplanetary-robot/SigmoidNumbers 71 | 72 | # References and Resources 73 | 74 | - https://posithub.org/docs/Posits4.pdf - a detailed article on Posits 75 | - http://web.stanford.edu/class/ee380/Abstracts/170201.html - official paper of Posits 76 | - http://posithub.org/ - information and news about posits and universal numbers 77 | 78 | # Mission 79 | I want to make this a really useable software implementation of Posits. In particular, I want it to be useful in linear algebra, where floating point computations are everywhere. It is still a work in progress and still has a long way to go. 80 | 81 | # License 82 | 83 | Licensed under MIT License 84 | -------------------------------------------------------------------------------- /PySigmoid/Posit.py: -------------------------------------------------------------------------------- 1 | from copy import * 2 | from math import * 3 | from fractions import Fraction 4 | from decimal import Decimal, getcontext 5 | from ctypes import c_ulonglong, c_double 6 | from .BitUtils import * 7 | from FixedPoint import * 8 | 9 | class Posit(object): 10 | def __init__(self, number = 0, nbits = None, es = None): 11 | if nbits != None and es != None: 12 | self.nbits = nbits 13 | self.es = es 14 | elif type(Posit.NBITS) is not int or type(Posit.ES) is not int: 15 | raise Exception("Set posit envrionemnt first using set_posit_env(nbits, es)") 16 | else: 17 | self.nbits = Posit.NBITS 18 | self.es = Posit.ES 19 | 20 | self.number = 0 21 | # number of bit patterns 22 | self.npat = 2**self.nbits 23 | # useed 24 | self.useed = 2**2**self.es 25 | # minimum positive value (bit pattern) 26 | self.minpos = 1 27 | # maximum positive value 28 | self.maxpos = 2**(self.nbits - 1) - 1 29 | self.inf = 2**(self.nbits - 1) 30 | self.zero = 0 31 | 32 | if type(number) == Quire: 33 | sign = int(number.q.scaledval < 0) 34 | if sign == 1: 35 | number.q = -number.q 36 | scale = number.integer_bits - number.q.toBinaryString().find("1") 37 | if scale > 0: 38 | scale -= 1 39 | fraction = number.q.scaledval 40 | self.number = Posit(nbits = self.nbits, es = self.es).construct_posit(sign, scale, fraction).number 41 | elif type(number) == str: 42 | self.set_string(number) 43 | elif type(number) == int: 44 | self.set_int(number) 45 | else: 46 | self.set_float(float(number)) 47 | 48 | def float_to_int(self, n): 49 | ''' 50 | Input: Float 51 | Returns the equivalent integer bit pattern of the 64-bit float 52 | ''' 53 | if type(n) == float: 54 | return c_ulonglong.from_buffer(c_double(n)).value 55 | else: 56 | raise "Not float" 57 | 58 | def set_bit_pattern(self, x): 59 | ''' 60 | Input: String of 1's and 0's of length at most nbits 61 | Sets the number to a bit pattern 62 | ''' 63 | if type(x) == str: 64 | if x.count("1") + x.count("0") == len(x): 65 | if len(x) <= self.nbits: 66 | self.number = int(x, 2) 67 | else: 68 | raise "String length exceeds number of bits" 69 | else: 70 | raise "String must contain only 1 and 0's" 71 | elif type(x) == int: 72 | if countBits(x) <= self.nbits: 73 | self.number = x 74 | else: 75 | raise "Integer exceeds number of bits" 76 | else: 77 | raise "Not string or int" 78 | 79 | def set_float(self, x): 80 | ''' 81 | Input: Float 82 | Map float to posit 83 | Cases -> Action 84 | (1) negative or positive zero -> return zero posit 85 | (2) +-inf or NaN -> return posit infinity 86 | (3) normal float -> round to nearest representable posit 87 | ''' 88 | if type(x) == float: 89 | # (1) negative or positive zero -> return zero posit 90 | if x == 0: 91 | self.number = self.zero 92 | # (2) +-inf or NaN -> return posit infinity 93 | elif isinf(x) or isnan(x): 94 | self.number = self.inf 95 | # (3) normal float 96 | else: 97 | # convert to integer 98 | n = self.float_to_int(x) 99 | # to get sign bit, shift 63 times to the right 100 | sign = n >> 63 101 | # to get exponent bits, remove sign, shift, then subtract bias 102 | exponent = ((n & ((1 << 63) - 1)) >> 52) - 1023 103 | # to get fractions bits, mask fraction bits and then OR the hidden bit 104 | fraction = (1 << 52) | (n & ((1 << 52) - 1)) 105 | # given the decoded values, construct a posit 106 | self.number = self.construct_posit(sign, exponent, fraction).number 107 | else: 108 | raise "Not Float" 109 | 110 | def set_int(self, x): 111 | ''' 112 | Input: Integer 113 | Returns nearest representable posit to the input integer 114 | ''' 115 | if type(x) == int: 116 | if x == 0: 117 | self.number = 0 118 | else: 119 | sign = 0 if x >= 0 else 1 120 | if sign == 1: 121 | x = abs(x) 122 | exponent = countBits(x) - 1 123 | fraction = x 124 | self.number = self.construct_posit(sign, exponent, fraction).number 125 | else: 126 | raise "Not an integer" 127 | 128 | def set_string(self, x): 129 | if type(x) == str: 130 | if len(x) == 0: 131 | return "Empty string" 132 | dot_index = x.find('.') 133 | sign = int(x[0] == "-") 134 | if dot_index == -1: 135 | self.set_int(int(x)) 136 | elif dot_index == len(x) - 1: 137 | self.set_int(int(x[:-1])) 138 | else: 139 | if sign == 1: 140 | x = x[1:] 141 | dot_index -= 1 142 | # count number of fractional digits 143 | fdig = len(x) - 1 - dot_index 144 | # get fraction 145 | fraction = int(x[:dot_index] + x[dot_index+1:]) 146 | exponent = countBits(fraction) - 1 - fdig 147 | self.number = (self.construct_posit(sign, exponent, fraction) / Posit(5**fdig, nbits = self.nbits, es = self.es)).number 148 | else: 149 | return "Not string" 150 | 151 | def is_valid(self): 152 | # check if a number is a valid posit of nbits 153 | return 0 <= self.number and self.number < self.npat 154 | 155 | def get_sign_bit(self, x): 156 | # extract the sign bit, returns 0 or 1 157 | return x >> (self.nbits - 1) 158 | 159 | def to_signed_int(self, x): 160 | sign = self.get_sign_bit(x) 161 | if sign == 1: 162 | if x != self.inf: 163 | x = - twosComplement(unsetBit(x, self.nbits-1), self.nbits) 164 | return x 165 | 166 | def __ge__(self, other): 167 | if type(other) != Posit: 168 | other = Posit(other, nbits = self.nbits, es = self.es) 169 | return self.to_signed_int(self.number) >= self.to_signed_int(other.number) 170 | 171 | def __le__(self, other): 172 | if type(other) != Posit: 173 | other = Posit(other, nbits = self.nbits, es = self.es) 174 | return self.to_signed_int(self.number) <= self.to_signed_int(other.number) 175 | 176 | def __lt__(self, other): 177 | if type(other) != Posit: 178 | other = Posit(other, nbits = self.nbits, es = self.es) 179 | return self.to_signed_int(self.number) < self.to_signed_int(other.number) 180 | 181 | def __gt__(self, other): 182 | if type(other) != Posit: 183 | other = Posit(other, nbits = self.nbits, es = self.es) 184 | return self.to_signed_int(self.number) > self.to_signed_int(other.number) 185 | 186 | def __eq__(self, other): 187 | if type(other) != Posit: 188 | other = Posit(other, nbits = self.nbits, es = self.es) 189 | return self.number == other.number 190 | 191 | def increment_posit(self): 192 | self.number = (self.number + 1) % (1 << self.nbits) 193 | 194 | # multiply two posits 195 | def __mul__(self, other): 196 | if type(other) != Posit: 197 | other = Posit(other, nbits = self.nbits, es = self.es) 198 | if self.number == 0 or self.number == self.inf: 199 | return self 200 | elif other.number == 0 or other.number == self.inf: 201 | return other 202 | 203 | sign_a, regime_a, exponent_a, fraction_a = self.decode() 204 | sign_b, regime_b, exponent_b, fraction_b = other.decode() 205 | sign_c = sign_a ^ sign_b 206 | 207 | # compute total scale factor 208 | scale_c = (2**self.es * (regime_a + regime_b) + exponent_a + exponent_b) 209 | fraction_c = fraction_a * fraction_b 210 | 211 | fa = floorLog2(fraction_a) 212 | fb = floorLog2(fraction_b) 213 | fc = floorLog2(fraction_c) 214 | 215 | # adjust based on carry 216 | scale_c += fc - fa - fb 217 | 218 | # construct posit then return 219 | return self.construct_posit(sign_c, scale_c, fraction_c) 220 | 221 | def __rmul__(self, other): 222 | return self.__mul__(other) 223 | 224 | def __radd__(self, other): 225 | return self.__add__(other) 226 | 227 | def __rsub__(self, other): 228 | if type(other) != Posit: 229 | other = Posit(other, nbits = self.nbits, es = self.es) 230 | return other.__sub__(self) 231 | 232 | def __rtruediv__(self, other): 233 | if type(other) != Posit: 234 | other = Posit(other, nbits = self.nbits, es = self.es) 235 | return other.__truediv__(self) 236 | 237 | def __rpow__(self, other): 238 | if type(other) != Posit: 239 | other = Posit(other, nbits = self.nbits, es = self.es) 240 | return other.__pow__(self) 241 | 242 | def construct_posit(self, sign, scale, fraction): 243 | if fraction == 0: 244 | return Posit(nbits = self.nbits, es = self.es) 245 | n = 0 246 | # regime = floor(scale / self.es) 247 | regime = scale >> self.es 248 | # exponent = scale % 2**es 249 | exponent = scale & createMask(self.es, 0) 250 | 251 | # number of bits written for regime 252 | regime_length = regime + 2 if regime >= 0 else - regime + 1 253 | 254 | # overflow to maxpos underflow to minpos 255 | if regime_length >= self.nbits + 1: 256 | p = Posit(nbits = self.nbits, es = self.es) 257 | p.set_bit_pattern(self.maxpos if regime >= 0 else self.minpos) 258 | if sign == 1: 259 | p = -p 260 | return p 261 | 262 | # encode regime 263 | if regime >= 0: 264 | n |= createMask(regime_length - 1, self.nbits - regime_length) 265 | elif self.nbits - 1 >= regime_length: 266 | n |= setBit(n, self.nbits - 1 - regime_length) 267 | 268 | # count number of bits available for exponent and fraction 269 | exponent_bits = min(self.es, self.nbits - 1 - regime_length) 270 | fraction_bits = self.nbits - 1 - regime_length - exponent_bits 271 | 272 | # remove trailing zeroes 273 | fraction = removeTrailingZeroes(fraction) 274 | # length of fraction bits, -1 is for hidden bit 275 | fraction_length = countBits(fraction) - 1 276 | # remove hidden bit 277 | fraction &= 2**(countBits(fraction)-1) - 1 278 | 279 | # trailing_bits = number of bits available for exponent + fraction 280 | trailing_bits = self.nbits - 1 - regime_length 281 | # exp_frac = concatenate exponent + fraction without trailing zeroes 282 | exp_frac = removeTrailingZeroes(exponent << (fraction_length) | fraction) 283 | 284 | # exp_frac_bits = minimum number of bits needed to represent exp_frac 285 | # exponent only 286 | if fraction_length == 0: 287 | exp_frac_bits = self.es - countTrailingZeroes(exponent) 288 | # exponent plus fraction 289 | else: 290 | exp_frac_bits = self.es + fraction_length 291 | 292 | # rounding needs to be done 293 | if trailing_bits < exp_frac_bits: 294 | # get overflow bits 295 | overflown = exp_frac & createMask(exp_frac_bits - trailing_bits, 0) 296 | # truncate trailing bits, encode to number 297 | n |= exp_frac >> (exp_frac_bits - trailing_bits) 298 | # perform round to even rounding by adding last bit to overflown bit 299 | # tie-breaking 300 | if overflown == (1 << (exp_frac_bits - trailing_bits - 1)): 301 | # check last bit 302 | if checkBit(exp_frac, exp_frac_bits - trailing_bits): 303 | n += 1 304 | # round to next higher value 305 | elif overflown > (1 << (exp_frac_bits - trailing_bits - 1)): 306 | n += 1 307 | # round to next lower value 308 | else: 309 | None 310 | else: 311 | n |= exp_frac << (trailing_bits - exp_frac_bits) 312 | 313 | p = Posit(nbits = self.nbits, es = self.es) 314 | if sign == 0: 315 | p.set_bit_pattern(n) 316 | else: 317 | p.set_bit_pattern(twosComplement(n, self.nbits)) 318 | 319 | return p 320 | 321 | def __sub__(self, other): 322 | if type(other) != Posit: 323 | other = Posit(other, nbits = self.nbits, es = self.es) 324 | return self.__add__(other.__neg__()) 325 | 326 | def __float__(self): 327 | return float(self.get_value()) 328 | 329 | def __abs__(self): 330 | if self.number == 0: 331 | return self 332 | elif self.number == self.inf: 333 | return self 334 | elif self.get_sign_bit(self.number): 335 | return self.__neg__() 336 | else: 337 | return self 338 | 339 | def __add__(self, other): 340 | if type(other) != Posit: 341 | other = Posit(other, nbits = self.nbits, es = self.es) 342 | if self.number == 0: 343 | return other 344 | elif other.number == 0: 345 | return self 346 | elif self.number == self.inf or other.number == self.inf: 347 | return self.inf 348 | 349 | sign_a, regime_a, exponent_a, fraction_a = self.decode() 350 | sign_b, regime_b, exponent_b, fraction_b = other.decode() 351 | 352 | # align fraction bits 353 | fraction_a, fraction_b = align(fraction_a, fraction_b) 354 | 355 | # compute total scale factor 356 | scale_a = 2**self.es * regime_a + exponent_a 357 | scale_b = 2**self.es * regime_b + exponent_b 358 | scale_c = max(scale_a, scale_b) 359 | 360 | # shift fraction bits 361 | if scale_a > scale_b: 362 | fraction_a <<= scale_a - scale_b 363 | estimated_length = countBits(fraction_a) 364 | elif scale_a <= scale_b: 365 | fraction_b <<= scale_b - scale_a 366 | estimated_length = countBits(fraction_b) 367 | 368 | # get fraction 369 | fraction_c = (-1)**sign_a * fraction_a + (-1)**sign_b * fraction_b 370 | sign_c = int(fraction_c < 0) 371 | fraction_c = abs(fraction_c) 372 | 373 | # check for carry bit 374 | result_length = countBits(fraction_c) 375 | scale_c += result_length - estimated_length 376 | fraction_c = removeTrailingZeroes(fraction_c) 377 | 378 | if fraction_c == 0: 379 | return Posit(nbits = self.nbits, es = self.es) 380 | 381 | # construct posit then return 382 | return self.construct_posit(sign_c, scale_c, fraction_c) 383 | 384 | def __pow__(self, other): 385 | if type(other) != Posit: 386 | other = Posit(other, nbits = self.nbits, es = self.es) 387 | return Posit(Quire(self)**Quire(other), nbits = self.nbits, es = self.es) 388 | 389 | def get_value(self): 390 | # 50 digits of precision 391 | getcontext().prec = 100 392 | if self.number == 0: 393 | return Decimal("0") 394 | elif self.number == self.inf: 395 | return Decimal("inf") 396 | 397 | sign, regime, exponent, fraction = self.decode() 398 | 399 | f = Decimal(fraction) 400 | n = countBits(fraction) - 1 401 | 402 | return ((-1)**sign * Decimal(2)**Decimal(2**self.es * regime + exponent - n) * Decimal(f)) 403 | 404 | def __str__(self): 405 | return self.get_value().__str__() 406 | 407 | def get_reciprocal(self): 408 | r = Posit(nbits = self.nbits, es = self.es) 409 | r.number = unsetBit(twosComplement(self.number, self.nbits), self.nbits - 1) 410 | return r 411 | 412 | def decode(self): 413 | # TODO: decode without twos complement 414 | x = self.number 415 | 416 | # exception values 417 | if x == 0: 418 | return (0, 0, 0, 0) 419 | elif x == self.inf: 420 | return None 421 | 422 | # determine sign and decode 423 | sign = checkBit(x, self.nbits - 1) 424 | 425 | if sign == 1: 426 | x = twosComplement(x, self.nbits) 427 | 428 | # decode regime length and regime sign 429 | regime_sign = checkBit(x, self.nbits - 2) 430 | if regime_sign == 0: 431 | regime_length = self.nbits - lastSetBit(x) - 1 432 | else: 433 | regime_length = self.nbits - lastUnsetBit(x) - 1 434 | 435 | # determine lengths 436 | exponent_length = max(0, min(self.es, self.nbits - 1 - regime_length)) 437 | fraction_length = max(0, self.nbits - 1 - regime_length - exponent_length) 438 | 439 | # determine actual values 440 | regime = - regime_length + 1 if regime_sign == 0 else regime_length - 2 441 | exponent = extractBits(x, exponent_length, fraction_length) << (self.es - exponent_length) 442 | 443 | fraction = removeTrailingZeroes(setBit(extractBits(x, fraction_length, 0), fraction_length)) 444 | 445 | return (sign, regime, exponent, fraction) 446 | 447 | def __truediv__(self, other): 448 | if type(other) != Posit: 449 | other = Posit(other, nbits = self.nbits, es = self.es) 450 | fraction = other.decode()[3] 451 | # reciprocation is accurate for powers of two 452 | if fraction & (fraction - 1) == 0: 453 | return self * other.get_reciprocal() 454 | 455 | if self.number == 0 or self.number == self.inf: 456 | return self 457 | elif other.number == 0 or other.number == self.inf: 458 | return other 459 | 460 | sign_a, regime_a, exponent_a, fraction_a = self.decode() 461 | sign_b, regime_b, exponent_b, fraction_b = other.decode() 462 | sign_c = sign_a ^ sign_b 463 | 464 | # compute total scale factor 465 | scale_c = (2**self.es * (regime_a - regime_b) + exponent_a - exponent_b) 466 | fraction_a, fraction_b = align(fraction_a, fraction_b) 467 | fraction_a <<= self.nbits * 4 468 | fraction_c = fraction_a // fraction_b 469 | fa = floorLog2(fraction_a) 470 | fb = floorLog2(fraction_b) 471 | fc = floorLog2(fraction_c) 472 | 473 | # adjust exponent 474 | scale_c -= fa - fb - fc 475 | 476 | # construct posit then return 477 | return self.construct_posit(sign_c, scale_c, fraction_c) 478 | 479 | def __neg__(self): 480 | # negate a number 481 | p = Posit(nbits = self.nbits, es = self.es) 482 | p.set_bit_pattern(twosComplement(self.number, self.nbits)) 483 | return p 484 | 485 | def __trunc__(self): 486 | if self.number == 0 or self.number == self.inf: 487 | return self 488 | 489 | sign, regime, exponent, fraction = self.decode() 490 | scale = 2**self.es * regime + exponent 491 | 492 | if scale >= 0: 493 | if scale <= countBits(fraction) - 1: 494 | fraction &= ~((1 << (countBits(fraction) - 1 - scale)) - 1) 495 | else: 496 | fraction = 0 497 | scale = 0 498 | 499 | # construct posit then return 500 | return self.construct_posit(sign, scale, fraction) 501 | 502 | 503 | def __mod__(self, other): 504 | if type(other) != Posit: 505 | other = Posit(other, nbits = self.nbits, es = self.es) 506 | return self - (self / other).__trunc__() * other 507 | 508 | def __repr__(self): 509 | return self.__str__() 510 | 511 | def __int__(self): 512 | return int(self.__trunc__().get_value()) 513 | 514 | def fused_multiply_add(self, a, b): 515 | ''' 516 | Input: Posits a and b to multiply 517 | Performs the operation (a×b)+c deferring the rounding until the last operation. 518 | ''' 519 | if type(a) == Posit and type(b) == Posit: 520 | return Posit(Quire(self) + (Quire(a) * Quire(b))) 521 | else: 522 | raise Exception("Arguments must be posit") 523 | 524 | def fused_add_multiply(self, a, b): 525 | ''' 526 | Input: Posits a and b to add 527 | Performs the operation (a+b)×c deferring the rounding until the last operation. 528 | ''' 529 | if type(a) == Posit and type(b) == Posit: 530 | return Posit(Quire(self) * (Quire(a) + Quire(b))) 531 | else: 532 | raise Exception("Arguments must be posit") 533 | 534 | def fused_multiply_multiply_subtract(self, b, c, d): 535 | ''' 536 | Input: Posits b to multiply and c, d to multiply and substract 537 | Performs the operation (a×b) - (c×d) deferring the rounding until the last operation. 538 | ''' 539 | if type(b) == Posit and type(c) == Posit and type(d) == Posit: 540 | mul1 = Quire(self) * Quire(b) 541 | mul2 = Quire(c) * Quire(d) 542 | return Posit(mul1 - mul2) 543 | else: 544 | raise Exception("Arguments must be posit") 545 | 546 | @staticmethod 547 | def fused_sum(a): 548 | if all(isinstance(x, Posit) for x in a): 549 | r = reduce(operator.add, map(lambda x: Quire(x), a)) 550 | return Posit(r) 551 | else: 552 | raise Exception("Argument must be a list of posit") 553 | 554 | @staticmethod 555 | def fused_dot_product(a, b): 556 | if all(isinstance(x, Posit) for x in (a + b)): 557 | r = reduce(operator.add, map(lambda x, y: Quire(x) * Quire(y), a,b)) 558 | return Posit(r) 559 | else: 560 | raise Exception("Arguments must be lists of posit") 561 | 562 | @staticmethod 563 | def fused_matmult(a, b): 564 | zip_b = zip(*b) 565 | # uncomment next line if python 3 : 566 | zip_b = list(zip_b) 567 | return [[fused_dot_product(row_a, col_b) for col_b in zip_b] for row_a in a] 568 | 569 | def sigmoid(self): 570 | other = deepcopy(self) 571 | other.number = toggleBit(other.number, other.nbits-1) 572 | other.number = other.number >> 2 573 | return other 574 | 575 | from .Quire import * 576 | --------------------------------------------------------------------------------