Package: hypercomplex
HypercomplexA Python library for working with quaternions, octonions, sedenions, and beyond following the Cayley-Dickson construction of hypercomplex numbers.Thecomplex numbersmay be viewed as an extension of the everydayreal numbers. A complex number has two real-number coefficients, one multiplied by 1, the other multiplied byi.In a similar way, aquaternion, which has 4 components, can be constructed by combining two complex numbers. Likewise, two quaternions can construct anoctonion(8 components), and two octonions can construct asedenion(16 components).The method for this construction is known as theCayley-Dickson constructionand the resulting classes of numbers are types ofhypercomplex numbers. There is no limit to the number of times you can repeat the Cayley-Dickson construction to create new types of hypercomplex numbers, doubling the number of components each time.This Python 3 package allows the creation of number classes at any repetition level of Cayley-Dickson constructions, and has built-ins for the lower, named levels such as quaternion, octonion, and sedenion.Installationpip install hypercomplexView on PyPI-View on GitHubThis package was built in Python 3.9.6 and has been tested to be compatible with python 3.6 through 3.10.Basic UsagefromhypercompleximportComplex,Quaternion,Octonion,Voudon,cayley_dickson_constructionc=Complex(0,7)print(c)# -> (0 7)print(c==7j)# -> Trueq=Quaternion(1.1,2.2,3.3,4.4)print(2*q)# -> (2.2 4.4 6.6 8.8)print(Quaternion.e_matrix())# -> e0  e1  e2  e3#    e1 -e0  e3 -e2#    e2 -e3 -e0  e1#    e3  e2 -e1 -e0o=Octonion(0,0,0,0,8,8,9,9)print(o+q)# -> (1.1 2.2 3.3 4.4 8 8 9 9)v=Voudon()print(v==0)# -> Trueprint(len(v))# -> 256BeyondVoudon=cayley_dickson_construction(Voudon)print(len(BeyondVoudon()))# -> 512For more snippets see the Thorough Usage Examples section below.Package ContentsThree functions form the core of the package:reals(base)- Given a base type (floatby default), generates a class that represents numbers with 1 hypercomplex dimension, i.e.real numbers. This class can then be extended into complex numbers and beyond withcayley_dickson_construction.Any usual math operations on instances of the class returned byrealsbehave as instances ofbasewould but their type remains the reals class. By default they are printed with thegformat-specand surrounded by parentheses, e.g.(1), to remain consistent with the format of higher dimension hypercomplex numbers.Python'sdecimal.Decimalmight be another likely choice forbase.# reals example:fromhypercompleximportrealsfromdecimalimportDecimalD=reals(Decimal)print(D(10)/4)# -> (2.5)print(D(3)*D(9))# -> (27)cayley_dickson_construction(basis)(aliascd_construction) generates a new class of hypercomplex numbers with twice the dimension of the givenbasis, which must be another hypercomplex number class or class returned fromreals. The new class of numbers is defined recursively on the basis according theCayley-Dickson construction. Normal math operations may be done upon its instances and with instances of other numeric types.# cayley_dickson_construction example:fromhypercompleximport*RealNum=reals()ComplexNum=cayley_dickson_construction(RealNum)QuaternionNum=cayley_dickson_construction(ComplexNum)q=QuaternionNum(1,2,3,4)print(q)# -> (1 2 3 4)print(1/q)# -> (0.0333333 -0.0666667 -0.1 -0.133333)print(q+1+2j)# -> (2 4 3 4)cayley_dickson_algebra(level, base)(aliascd_algebra) is a helper function that repeatedly appliescayley_dickson_constructionto the givenbasetype (floatby default)levelnumber of times. That is,cayley_dickson_algebrareturns the class for the Cayley-Dickson algebra of hypercomplex numbers with2**leveldimensions.# cayley_dickson_algebra example:fromhypercompleximport*OctonionNum=cayley_dickson_algebra(3)o=OctonionNum(8,7,6,5,4,3,2,1)print(o)# -> (8 7 6 5 4 3 2 1)print(2*o)# -> (16 14 12 10 8 6 4 2)print(o.conjugate())# -> (8 -7 -6 -5 -4 -3 -2 -1)For convenience, nine internal number types are already defined, built off of each other:NameAliasesDescriptionRealR,CD1,CD[0]Real numberswith 1 hypercomplex dimension based onfloat.ComplexC,CD2,CD[1]Complex numberswith 2 hypercomplex dimensions based onReal.QuaternionQ,CD4,CD[2]Quaternion numberswith 4 hypercomplex dimensions based onComplex.OctonionO,CD8,CD[3]Octonion numberswith 8 hypercomplex dimensions based onQuaternion.SedenionS,CD16,CD[4]Sedenion numberswith 16 hypercomplex dimensions based onOctonion.PathionP,CD32,CD[5]Pathion numbers with 32 hypercomplex dimensions based onSedenion.ChingonX,CD64,CD[6]Chingon numbers with 64 hypercomplex dimensions based onPathion.RoutonU,CD128,CD[7]Routon numbers with 128 hypercomplex dimensions based onChingon.VoudonV,CD256,CD[8]Voudon numbers with 256 hypercomplex dimensions based onRouton.# built-in types example:fromhypercompleximport*print(Real(4))# -> (4)print(C(3-7j))# -> (3 -7)print(CD4(.1,-2.2,3.3e3))# -> (0.1 -2.2 3300 0)print(CD[3](1,0,2,0,3))# -> (1 0 2 0 3 0 0 0)The names and letter-abbreviations were taken fromthis image(mirror) found in Micheal Carter's paperVisualization of the Cayley-Dickson Hypercomplex Numbers Up to the Chingons (64D), but they also may be known according to theirLatin naming conventions.Thorough Usage ExamplesThis list followsexamples.pyexactly and documents nearly all the things you can do with the hypercomplex numbers created by this package.Every example assumes the appropriate imports are already done, e.g.from hypercomplex import *.Initialization can be done in various ways, including using Python's built in complex numbers. Unspecified coefficients become 0.print(R(-1.5))# -> (-1.5)print(C(2,3))# -> (2 3)print(C(2+3j))# -> (2 3)print(Q(4,5,6,7))# -> (4 5 6 7)print(Q(4+5j,C(6,7),pair=True))# -> (4 5 6 7)print(P())# -> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)Numbers can be added and subtracted. The result will be the type with more dimensions.print(Q(0,1,2,2)+C(9,-1))# -> (9 0 2 2)print(100.1-O(0,0,0,0,1.1,2.2,3.3,4.4))# -> (100.1 0 0 0 -1.1 -2.2 -3.3 -4.4)Numbers can be multiplied. The result will be the type with more dimensions.print(10*S(1,2,3))# -> (10 20 30 0 0 0 0 0 0 0 0 0 0 0 0 0)print(Q(1.5,2.0)*O(0,-1))# -> (2 -1.5 0 0 0 0 0 0)# notice quaternions are non-commutativeprint(Q(1,2,3,4)*Q(1,0,0,1))# -> (-3 5 1 5)print(Q(1,0,0,1)*Q(1,2,3,4))# -> (-3 -1 5 5)Numbers can be divided andinversegives the multiplicative inverse.print(100/C(0,2))# -> (0 -50)print(C(2,2)/Q(1,2,3,4))# -> (0.2 -0.0666667 0.0666667 -0.466667)print(C(2,2)*Q(1,2,3,4).inverse())# -> (0.2 -0.0666667 0.0666667 -0.466667)print(R(2).inverse(),1/R(2))# -> (0.5) (0.5)Numbers can be raised to integer powers, a shortcut for repeated multiplication or division.q=Q(0,3,4,0)print(q**5)# -> (0 1875 2500 0)print(q*q*q*q*q)# -> (0 1875 2500 0)print(q**-1)# -> (0 -0.12 -0.16 0)print(1/q)# -> (0 -0.12 -0.16 0)print(q**0)# -> (1 0 0 0)conjugategives the conjugate of the number.print(R(9).conjugate())# -> (9)print(C(9,8).conjugate())# -> (9 -8)print(Q(9,8,7,6).conjugate())# -> (9 -8 -7 -6)normgives the absolute value as the base type (floatby default). There is alsonorm_squared.print(O(3,4).norm(),type(O(3,4).norm()))# -> 5.0 <class 'float'>print(abs(O(3,4)))# -> 5.0print(O(3,4).norm_squared())# -> 25.0Numbers are considered equal if their coefficients all match. Non-existent coefficients are 0.print(R(999)==V(999))# -> Trueprint(C(1,2)==Q(1,2))# -> Trueprint(C(1,2)==Q(1,2,0.1))# -> Falsecoefficientsgives a tuple of the components of the number in their base type (floatby default). The propertiesrealandimagare shortcuts for the first two components. Indexing can also be used (but is inefficient).print(R(100).coefficients())# -> (100.0,)q=Q(2,3,4,5)print(q.coefficients())# -> (2.0, 3.0, 4.0, 5.0)print(q.real,q.imag)# -> 2.0 3.0print(q[0],q[1],q[2],q[3])# -> 2.0 3.0 4.0 5.0e(index)of a number class gives the unit hypercomplex number where the index coefficient is 1 and all others are 0.print(C.e(0))# -> (1 0)print(C.e(1))# -> (0 1)print(O.e(3))# -> (0 0 0 1 0 0 0 0)e_matrixof a number class gives the multiplication table ofe(i)*e(j). Setstring=Falseto get a 2D list instead of a string. Setraw=Trueto get the raw hypercomplex numbers.print(O.e_matrix())# -> e1  e2  e3  e4  e5  e6  e7#   -e0  e3 -e2  e5 -e4 -e7  e6#   -e3 -e0  e1  e6  e7 -e4 -e5#    e2 -e1 -e0  e7 -e6  e5 -e4#   -e5 -e6 -e7 -e0  e1  e2  e3#    e4 -e7  e6 -e1 -e0 -e3  e2#    e7  e4 -e5 -e2  e3 -e0 -e1#   -e6  e5  e4 -e3 -e2  e1 -e0#print(C.e_matrix(string=False,raw=True))# -> [[(1 0), (0 1)], [(0 1), (-1 0)]]A number is considered truthy if it has has non-zero coefficients. Conversion toint,floatandcomplexare only valid when the coefficients beyond the dimension of those types are all 0.print(bool(Q()))# -> Falseprint(bool(Q(0,0,0.01,0)))# -> Trueprint(complex(Q(5,5)))# -> (5+5j)print(int(V(9.9)))# -> 9# print(float(C(1, 2))) <- invalidAny usual format spec for the base type can be given in an f-string.o=O(0.001,1,-2,3.3333,4e5)print(f"{o:.2f}")# -> (0.00 1.00 -2.00 3.33 400000.00 0.00 0.00 0.00)print(f"{R(23.9):04.0f}")# -> (0024)Thelenof a number is its hypercomplex dimension, i.e. the number of components or coefficients it has.print(len(R()))# -> 1print(len(C(7,7)))# -> 2print(len(U()))# -> 128Usinginbehaves the same as if the number were a tuple of its coefficients.print(3inQ(1,2,3,4))# -> Trueprint(5inQ(1,2,3,4))# -> Falsecopycan be used to duplicate a number (but should generally never be needed as all operations create a new number).x=O(9,8,7)y=x.copy()print(x==y)# -> Trueprint(xisy)# -> Falsebaseon a number class will return the base type the entire numbers are built upon.print(R.base())# -> <class 'float'>print(V.base())# -> <class 'float'>A=cayley_dickson_algebra(20,int)print(A.base())# -> <class 'int'>Hypercomplex numbers are weird, so be careful! Here two non-zero sedenions multiply to give zero because sedenions and beyond have zero devisors.s1=S.e(5)+S.e(10)s2=S.e(6)+S.e(9)print(s1)# -> (0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0)print(s2)# -> (0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0)print(s1*s2)# -> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)print((1/s1)*(1/s2))# -> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)# print(1/(s1 * s2)) <- zero division errorAboutI wrote this package for the novelty of it and as a math and programming exercise. The operations it can perform on hypercomplex numbers are not particularly efficient due to the recursive nature of the Cayley-Dickson construction.I am not a mathematician, only a math hobbyist, and apologize if there are issues with the implementations or descriptions I have provided.