]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/octonions.py
octonions: add a test for Cartesian products.
[sage.d.git] / mjo / octonions.py
index 3c3f47418b1186147ddcf3efc47f3b5dc5b969ee..2850545280b122cb8109324f4b6431ed0f5076ec 100644 (file)
@@ -1,3 +1,4 @@
+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
@@ -6,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"""
@@ -138,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.
@@ -338,202 +346,64 @@ class Octonions(CombinatorialFreeModule):
         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)