]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
octonions: begin adding OctonionMatrix pseudo-matrix class.
authorMichael Orlitzky <michael@orlitzky.com>
Tue, 2 Mar 2021 04:50:31 +0000 (23:50 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Tue, 2 Mar 2021 04:50:31 +0000 (23:50 -0500)
mjo/octonions.py

index cd25f18a6500b45ce621fa9ae94b6d7c28760750..3c3f47418b1186147ddcf3efc47f3b5dc5b969ee 100644 (file)
@@ -336,3 +336,204 @@ class Octonions(CombinatorialFreeModule):
                for i in range(n) ]
 
         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 |
+            +------+------+
+
+        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()) ])
+
+    def __add__(self,rhs):
+        r"""
+        SETUP::
+
+            sage: from mjo.octonions import Octonions, OctonionMatrix
+
+        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 |
+            +---------+---------+
+
+        """
+        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()) ])