+from sage.misc.cachefunc import cached_method
from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
from sage.combinat.free_module import CombinatorialFreeModule
from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement
for i in range(n) ]
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):
+ r"""
+ The algebra of ``n``-by-``n`` matrices with octonion entries over
+ (a subfield of) the real numbers.
+
+ 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, scalars=AA, prefix="E", **kwargs):
+ # Not associative, not commutative
+ category = MagmaticAlgebras(scalars).FiniteDimensional()
+ category = category.WithBasis().Unital()
+
+ self._nrows = n
+
+ # Since the scalar ring is real but the entries are not,
+ # sticking a "1" in each position doesn't give us a basis for
+ # the space. We actually need to stick each of e0, e1, ... (a
+ # basis for the entry algebra itself) into each position.
+ from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
+ from sage.categories.sets_cat import cartesian_product
+
+ I = FiniteEnumeratedSet(range(n))
+ J = FiniteEnumeratedSet(range(n))
+ self._entry_algebra = Octonions(field=scalars)
+ entry_basis = self._entry_algebra.gens()
+
+ basis_indices = cartesian_product([I,J,entry_basis])
+ super().__init__(scalars,
+ basis_indices,
+ category=category,
+ 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() )