+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
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"""
"""
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:
- r"""
- A pseudo-matrix class that supports octonion entries.
-
- Matrices in SageMath can't have base rings that are
- non-commutative, much less non-associative. The "matrix" scaling,
- addition, and multiplication operations for this class are all
- wholly inefficient, but are hand-written to guarantee that they
- are performed in the correct order. Of course, it can't guarantee
- that you won't write something visually ambiguous like
- `A*B*C`... but you already have that problem with the
- non-associative octonions themselves.
-
- This class is only as sophisticated as it need to be to implement
- the Jordan and inner-products in the space of Hermitian matrices
- with octonion entries, namely ``(X*Y+Y*X)/2`` and
- ``(X*Y).trace().real()`` for two octonion matrices ``X`` and
- ``Y``.
-
- .. WARNING:
-
- These are not matrices in the usual sense! Matrix
- multiplication is associative. Multiplication of octonion
- "matrices" cannot be, since the multiplication of the
- underlying octonions is not (consider two 1-by-1 matrices each
- containing a single octonion).
- """
- def __init__(self, entries):
- r"""
- Initialize this matrix with a list of lists in (row,column) order,
- just like in SageMath.
- """
- self._nrows = len(entries)
-
- if self._nrows == 0:
- self._ncols = 0
- else:
- # We don't check that you haven't supplied two rows (or
- # columns) of different lengths!
- self._ncols = len(entries[0])
-
- self._entries = entries
-
- def __getitem__(self, indices):
- r"""
- SETUP::
-
- sage: from mjo.octonions import Octonions, OctonionMatrix
-
- EXAMPLES::
-
- sage: O = Octonions(field=QQ)
- sage: M = OctonionMatrix([ [O.one(), O.zero()],
- ....: [O.zero(), O.one() ] ])
- sage: M[0,0]
- e0
- sage: M[0,1]
- 0
- sage: M[1,0]
- 0
- sage: M[1,1]
- e0
- """
- i,j = indices
- return self._entries[i][j]
-
- def nrows(self):
- r"""
- SETUP::
-
- sage: from mjo.octonions import Octonions, OctonionMatrix
-
- EXAMPLES::
-
- sage: O = Octonions(field=QQ)
- sage: M = OctonionMatrix([ [O.one(), O.zero()],
- ....: [O.zero(), O.one() ],
- ....: [O.zero(), O.zero()] ])
- sage: M.nrows()
- 3
-
- """
- return self._nrows
-
- def ncols(self):
- r"""
-
- SETUP::
-
- sage: from mjo.octonions import Octonions, OctonionMatrix
-
- EXAMPLES::
- sage: O = Octonions(field=QQ)
- sage: M = OctonionMatrix([ [O.one(), O.zero()],
- ....: [O.zero(), O.one() ],
- ....: [O.zero(), O.zero()] ])
- sage: M.ncols()
- 2
-
- """
- return self._ncols
-
- def __repr__(self):
- return table(self._entries, frame=True)._repr_()
-
- def __mul__(self,rhs):
- r"""
-
- SETUP::
-
- sage: from mjo.octonions import Octonions, OctonionMatrix
-
- EXAMPLES::
-
- sage: O = Octonions(QQ)
- sage: e1 = O.monomial(1)
- sage: e2 = O.monomial(2)
- sage: e1*e2
- e3
- sage: e2*e1
- -e3
- sage: E1 = OctonionMatrix([[e1]])
- sage: E2 = OctonionMatrix([[e2]])
- sage: E1*E2
- +----+
- | e3 |
- +----+
- sage: E2*E1
- +-----+
- | -e3 |
- +-----+
-
- """
- if not self.ncols() == rhs.nrows():
- raise ValueError("dimension mismatch")
-
- m = self.nrows()
- n = self.ncols()
- p = rhs.ncols()
-
- C = lambda i,j: sum( self[i,k]*rhs[k,j] for k in range(n) )
- return OctonionMatrix([ [C(i,j) for j in range(m)]
- for i in range(p) ] )
-
- def __rmul__(self,scalar):
- r"""
-
- SETUP::
-
- sage: from mjo.octonions import Octonions, OctonionMatrix
-
- EXAMPLES::
-
- sage: O = Octonions(QQ)
- sage: M = OctonionMatrix([[O.one(), O.zero()],
- ....: [O.zero(),O.one() ] ])
- sage: 2*M
- +------+------+
- | 2*e0 | 0 |
- +------+------+
- | 0 | 2*e0 |
- +------+------+
+class OctonionMatrixAlgebra(HurwitzMatrixAlgebra):
+ r"""
+ The algebra of ``n``-by-``n`` matrices with octonion entries over
+ (a subfield of) the real numbers.
- r"""
- # SCALAR GOES ON THE LEFT HERE
- return OctonionMatrix([ [scalar*self[i,j]
- for j in range(self.ncols())]
- for i in range(self.nrows()) ])
+ 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).
- def __add__(self,rhs):
- r"""
- SETUP::
+ SETUP::
- sage: from mjo.octonions import Octonions, OctonionMatrix
+ sage: from mjo.octonions import Octonions, OctonionMatrixAlgebra
- EXAMPLES::
+ EXAMPLES::
- sage: O = Octonions(QQ)
- sage: e0,e1,e2,e3,e4,e5,e6,e7 = O.gens()
- sage: A = OctonionMatrix([ [e0,e1],
- ....: [e2,e3] ])
- sage: B = OctonionMatrix([ [e4,e5],
- ....: [e6,e7] ])
- sage: A+B
- +---------+---------+
- | e0 + e4 | e1 + e5 |
- +---------+---------+
- | e2 + e6 | e3 + e7 |
- +---------+---------+
+ 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
- """
- if not self.ncols() == rhs.ncols():
- raise ValueError("column dimension mismatch")
- if not self.nrows() == rhs.nrows():
- raise ValueError("row dimension mismatch")
- return OctonionMatrix([ [self[i,j] + rhs[i,j]
- for j in range(self.ncols())]
- for i in range(self.nrows()) ])
+ """
+ def __init__(self, n, scalars=AA, prefix="E", **kwargs):
+ super().__init__(Octonions(field=scalars),
+ scalars,
+ n,
+ prefix=prefix,
+ **kwargs)