from sage.matrix.matrix_space import MatrixSpace
from sage.misc.table import table
+from mjo.matrix_algebra import MatrixAlgebra
+
class Octonion(IndexedFreeModuleElement):
def conjugate(self):
r"""
"""
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.
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_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_list()
- [[e0 + 2*e3, 0, 0], [0, 0, 0], [0, 0, 0]]
-
- """
- zero = self.parent().octonions().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_list(), frame=True)._repr_()
-
-
- 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_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().octonions().zero()
- return sum( (self[i,i] for i in range(self.nrows())), zero )
-
-
-class OctonionMatrixAlgebra(CombinatorialFreeModule):
+class OctonionMatrixAlgebra(MatrixAlgebra):
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, 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._octonions = Octonions(field=field)
- entry_basis = self._octonions.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.from_list([ [e0+e4, e1+e5],
+ ....: [e2-e6, e3-e7] ])
+ +---------+---------+
+ | e0 + e4 | e1 + e5 |
+ +---------+---------+
+ | e2 - e6 | e3 - e7 |
+ +---------+---------+
+
+ 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 octonions(self):
- r"""
- Return the Octonion algebra that our elements' entries come from.
- """
- return self._octonions
-
- 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._octonions.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.octonions().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)