X-Git-Url: http://gitweb.michael.orlitzky.com/?p=sage.d.git;a=blobdiff_plain;f=mjo%2Foctonions.py;h=2850545280b122cb8109324f4b6431ed0f5076ec;hp=f786fcf1228e62f605876c04a2003614d5015e64;hb=d006c2e856b970d2f8e77d738030ef2e63ec5b78;hpb=9c8a4f05eeef719ad3b20f8e2cd17074253aff29 diff --git a/mjo/octonions.py b/mjo/octonions.py index f786fcf..2850545 100644 --- a/mjo/octonions.py +++ b/mjo/octonions.py @@ -7,6 +7,8 @@ from sage.rings.all import AA, ZZ from sage.matrix.matrix_space import MatrixSpace from sage.misc.table import table +from mjo.matrix_algebra import HurwitzMatrixAlgebra + class Octonion(IndexedFreeModuleElement): def conjugate(self): r""" @@ -139,6 +141,11 @@ class Octonion(IndexedFreeModuleElement): """ return self._norm_squared().sqrt() + # The absolute value notation is typically used for complex numbers... + # and norm() isn't supported in AA, so this lets us use abs() in all + # of the division algebras we need. + abs = norm + def inverse(self): r""" Return the inverse of this element if it exists. @@ -339,171 +346,8 @@ class Octonions(CombinatorialFreeModule): return table(M, header_row=True, header_column=True, frame=True) -class OctonionMatrix(IndexedFreeModuleElement): - def nrows(self): - return self.parent().nrows() - ncols = nrows - - @cached_method - def to_nested_list(self): - r""" - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: MS = OctonionMatrixAlgebra(3) - sage: E00e0 = MS.gens()[0] - sage: E00e0 - +----+---+---+ - | e0 | 0 | 0 | - +----+---+---+ - | 0 | 0 | 0 | - +----+---+---+ - | 0 | 0 | 0 | - +----+---+---+ - sage: E00e3 = MS.gens()[3] - sage: E00e3 - +----+---+---+ - | e3 | 0 | 0 | - +----+---+---+ - | 0 | 0 | 0 | - +----+---+---+ - | 0 | 0 | 0 | - +----+---+---+ - sage: (E00e0 + 2*E00e3).to_nested_list() - [[e0 + 2*e3, 0, 0], [0, 0, 0], [0, 0, 0]] - - """ - zero = self.parent().entry_algebra().zero() - l = [[zero for j in range(self.ncols())] for i in range(self.nrows())] - for (k,v) in self.monomial_coefficients().items(): - (i,j,e) = k - l[i][j] += v*e - return l - - def __repr__(self): - r""" - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: OctonionMatrixAlgebra(3).one() - +----+----+----+ - | e0 | 0 | 0 | - +----+----+----+ - | 0 | e0 | 0 | - +----+----+----+ - | 0 | 0 | e0 | - +----+----+----+ - - """ - return table(self.to_nested_list(), frame=True)._repr_() - - - def list(self): - r""" - Return one long list of this matrix's entries. - - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: MS = OctonionMatrixAlgebra(3) - sage: E00e0 = MS.gens()[0] - sage: E00e3 = MS.gens()[3] - sage: (E00e0 + 2*E00e3).to_nested_list() - [[e0 + 2*e3, 0, 0], [0, 0, 0], [0, 0, 0]] - sage: (E00e0 + 2*E00e3).list() - [e0 + 2*e3, 0, 0, 0, 0, 0, 0, 0, 0] - - """ - return sum( self.to_nested_list(), [] ) - - - def __getitem__(self, indices): - r""" - - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: MS = OctonionMatrixAlgebra(2) - sage: I = MS.one() - sage: I[0,0] - e0 - sage: I[0,1] - 0 - sage: I[1,0] - 0 - sage: I[1,1] - e0 - - """ - i,j = indices - return self.to_nested_list()[i][j] - - def trace(self): - r""" - - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: MS = OctonionMatrixAlgebra(3) - sage: MS.one().trace() - 3*e0 - - """ - zero = self.parent().entry_algebra().zero() - return sum( (self[i,i] for i in range(self.nrows())), zero ) - - def matrix_space(self): - r""" - - SETUP:: - sage: from mjo.octonions import OctonionMatrixAlgebra - - TESTS:: - - sage: set_random_seed() - sage: MS = OctonionMatrixAlgebra(2) - sage: MS.random_element().matrix_space() - Module of 2 by 2 matrices with octonion entries over the - scalar ring Algebraic Real Field - - """ - return self.parent() - - def is_hermitian(self): - r""" - - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: MS = OctonionMatrixAlgebra(3) - sage: MS.one().is_hermitian() - True - - """ - return all( self[i,j] == self[j,i].conjugate() - for i in range(self.nrows()) - for j in range(self.ncols()) ) - -class OctonionMatrixAlgebra(CombinatorialFreeModule): +class OctonionMatrixAlgebra(HurwitzMatrixAlgebra): r""" The algebra of ``n``-by-``n`` matrices with octonion entries over (a subfield of) the real numbers. @@ -511,116 +355,55 @@ class OctonionMatrixAlgebra(CombinatorialFreeModule): The usual matrix spaces in SageMath don't support octonion entries because they assume that the entries of the matrix come from a commutative and associative ring (i.e. very NOT the octonions). - """ - Element = OctonionMatrix - def __init__(self, n, field=AA, prefix="E", **kwargs): - # Not associative, not commutative - category = MagmaticAlgebras(field).FiniteDimensional() - category = category.WithBasis().Unital() + SETUP:: - self._nrows = n + sage: from mjo.octonions import Octonions, OctonionMatrixAlgebra - # Since the scalar field is real but the entries are octonion, - # sticking a "1" in each position doesn't give us a basis for - # the space. We actually need to stick each of e0, e1, ..., e7 - # (a basis for the Octonions themselves) into each position. - from sage.sets.finite_enumerated_set import FiniteEnumeratedSet - from sage.categories.sets_cat import cartesian_product + EXAMPLES:: - I = FiniteEnumeratedSet(range(n)) - J = FiniteEnumeratedSet(range(n)) - self._entry_algebra = Octonions(field=field) - entry_basis = self._entry_algebra.gens() + sage: OctonionMatrixAlgebra(3) + Module of 3 by 3 matrices with entries in Octonion algebra with base + ring Algebraic Real Field over the scalar ring Algebraic Real Field + sage: OctonionMatrixAlgebra(3,QQ) + Module of 3 by 3 matrices with entries in Octonion algebra with base + ring Rational Field over the scalar ring Rational Field + + :: + + sage: O = Octonions(QQ) + sage: e0,e1,e2,e3,e4,e5,e6,e7 = O.gens() + sage: MS = OctonionMatrixAlgebra(2) + sage: MS([ [e0+e4, e1+e5], + ....: [e2-e6, e3-e7] ]) + +---------+---------+ + | e0 + e4 | e1 + e5 | + +---------+---------+ + | e2 - e6 | e3 - e7 | + +---------+---------+ + + :: + + sage: A1 = OctonionMatrixAlgebra(1,QQ) + sage: A2 = OctonionMatrixAlgebra(1,QQ) + sage: cartesian_product([A1,A2]) + Module of 1 by 1 matrices with entries in Octonion algebra with + base ring Rational Field over the scalar ring Rational Field (+) + Module of 1 by 1 matrices with entries in Octonion algebra with + base ring Rational Field over the scalar ring Rational Field + + TESTS:: + + sage: set_random_seed() + sage: MS = OctonionMatrixAlgebra(ZZ.random_element(10)) + sage: x = MS.random_element() + sage: x*MS.one() == x and MS.one()*x == x + True - basis_indices = cartesian_product([I,J,entry_basis]) - super().__init__(field, - basis_indices, - category=category, + """ + def __init__(self, n, scalars=AA, prefix="E", **kwargs): + super().__init__(Octonions(field=scalars), + scalars, + n, prefix=prefix, - bracket='(') - - def _repr_(self): - return ("Module of %d by %d matrices with octonion entries" - " over the scalar ring %s" % - (self.nrows(), self.ncols(), self.base_ring()) ) - - def entry_algebra(self): - r""" - Return the algebra that our elements' entries come from. - """ - return self._entry_algebra - - def nrows(self): - return self._nrows - ncols = nrows - - def product_on_basis(self, mon1, mon2): - (i,j,oct1) = mon1 - (k,l,oct2) = mon2 - if j == k: - return self.monomial((i,l,oct1*oct2)) - else: - return self.zero() - - def one(self): - r""" - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - TESTS:: - - sage: set_random_seed() - sage: MS = OctonionMatrixAlgebra(ZZ.random_element(10)) - sage: x = MS.random_element() - sage: x*MS.one() == x and MS.one()*x == x - True - - """ - return sum( (self.monomial((i,i,self.entry_algebra().one())) - for i in range(self.nrows()) ), - self.zero() ) - - def from_list(self, entries): - r""" - Construct an element of this algebra from a list of lists of - octonions. - - SETUP:: - - sage: from mjo.octonions import Octonions, OctonionMatrixAlgebra - - EXAMPLES:: - - sage: O = Octonions(QQ) - sage: e0,e1,e2,e3,e4,e5,e6,e7 = O.gens() - sage: MS = OctonionMatrixAlgebra(2) - sage: MS.from_list([ [e0+e4, e1+e5], - ....: [e2-e6, e3-e7] ]) - +---------+---------+ - | e0 + e4 | e1 + e5 | - +---------+---------+ - | e2 - e6 | e3 - e7 | - +---------+---------+ - - """ - nrows = len(entries) - ncols = 0 - if nrows > 0: - ncols = len(entries[0]) - - if (not all( len(r) == ncols for r in entries )) or (ncols != nrows): - raise ValueError("list must be square") - - def convert(e_ij): - # We have to pass through vectors to convert from the - # given octonion algebra to ours. Otherwise we can fail - # to convert an element of (for example) Octonions(QQ) - # to Octonions(AA). - return self.entry_algebra().from_vector(e_ij.to_vector()) - - return sum( (self.monomial( (i,j, convert(entries[i][j])) ) - for i in range(nrows) - for j in range(ncols) ), - self.zero() ) + **kwargs)