]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/octonions.py
octonions: add QuaternionMatrixAlgebra; prepare for rename...
[sage.d.git] / mjo / octonions.py
index 352c4c586e7506a218bcc9e7f9cb6332c1cd72c7..78365e298ec7a46fcaff0603039f6410aa768c96 100644 (file)
@@ -7,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"""
@@ -139,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.
@@ -339,226 +346,124 @@ class Octonions(CombinatorialFreeModule):
         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(HurwitzMatrixAlgebra):
     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
+    commutative and associative ring, and the octonions are neither.
 
-    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 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: A = OctonionMatrixAlgebra(2)
+        sage: e0,e1,e2,e3,e4,e5,e6,e7 = A.entry_algebra().gens()
+        sage: A([ [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: A = OctonionMatrixAlgebra(ZZ.random_element(10))
+        sage: x = A.random_element()
+        sage: x*A.one() == x and A.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
+                         **kwargs)
 
-    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() )
+class QuaternionMatrixAlgebra(HurwitzMatrixAlgebra):
+    r"""
+    The algebra of ``n``-by-``n`` matrices with quaternion entries over
+    (a subfield of) the real numbers.
 
-    def from_list(self, entries):
-        r"""
-        Construct an element of this algebra from a list of lists of
-        octonions.
+    The usual matrix spaces in SageMath don't support quaternion entries
+    because they assume that the entries of the matrix come from a
+    commutative ring, and the quaternions are not commutative.
 
-        SETUP::
+    SETUP::
 
-            sage: from mjo.octonions import Octonions, OctonionMatrixAlgebra
+        sage: from mjo.octonions import QuaternionMatrixAlgebra
 
-        EXAMPLES::
+    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 |
-            +---------+---------+
+        sage: QuaternionMatrixAlgebra(3)
+        Module of 3 by 3 matrices with entries in Quaternion
+        Algebra (-1, -1) with base ring Algebraic Real Field
+        over the scalar ring Algebraic Real Field
+        sage: QuaternionMatrixAlgebra(3,QQ)
+        Module of 3 by 3 matrices with entries in Quaternion
+        Algebra (-1, -1) with base ring Rational Field over
+        the scalar ring Rational Field
+
+    ::
+
+        sage: A = QuaternionMatrixAlgebra(2)
+        sage: i,j,k = A.entry_algebra().gens()
+        sage: A([ [1+i, j-2],
+        ....:     [k,   k+j] ])
+        +-------+--------+
+        | 1 + i | -2 + j |
+        +-------+--------+
+        | k     | j + k  |
+        +-------+--------+
+
+    ::
+
+        sage: A1 = QuaternionMatrixAlgebra(1,QQ)
+        sage: A2 = QuaternionMatrixAlgebra(2,QQ)
+        sage: cartesian_product([A1,A2])
+        Module of 1 by 1 matrices with entries in Quaternion Algebra
+        (-1, -1) with base ring Rational Field over the scalar ring
+        Rational Field (+) Module of 2 by 2 matrices with entries in
+        Quaternion Algebra (-1, -1) with base ring Rational Field over
+        the scalar ring Rational Field
+
+    TESTS::
+
+        sage: set_random_seed()
+        sage: A = QuaternionMatrixAlgebra(ZZ.random_element(10))
+        sage: x = A.random_element()
+        sage: x*A.one() == x and A.one()*x == x
+        True
 
-        """
-        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() )
+    """
+    def __init__(self, n, scalars=AA, **kwargs):
+        # The -1,-1 gives us the "usual" definition of quaternion
+        Q = QuaternionAlgebra(scalars,-1,-1)
+        super().__init__(Q, scalars, n, **kwargs)