From 04db87da2d505376f168cb14fa5eb9d0a73e4ca7 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 1 Mar 2021 23:50:31 -0500 Subject: [PATCH] octonions: begin adding OctonionMatrix pseudo-matrix class. --- mjo/octonions.py | 201 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/mjo/octonions.py b/mjo/octonions.py index cd25f18..3c3f474 100644 --- a/mjo/octonions.py +++ b/mjo/octonions.py @@ -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()) ]) -- 2.43.2