]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/octonions.py
octonions: add QuaternionMatrixAlgebra; prepare for rename...
[sage.d.git] / mjo / octonions.py
index 73acf2db2ebf76258f47ed1156113925f4f257ed..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,249 +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 )
-
-    def matrix_space(self):
-        r"""
-
-        SETUP::
 
-            sage: from mjo.octonions import OctonionMatrixAlgebra
-
-        TESTS::
-
-            sage: set_random_seed()
-            sage: MS = OctonionMatrixAlgebra(2)
-            sage: MS.random_element().matrix_space()
-            Module of 2 by 2 matrices with octonion entries over the
-            scalar ring Algebraic Real Field
-
-        """
-        return self.parent()
-
-
-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 _repr_(self):
-        return ("Module of %d by %d matrices with octonion entries"
-                " over the scalar ring %s" %
-                (self.nrows(), self.ncols(), self.base_ring()) )
-
-    def octonions(self):
-        r"""
-        Return the Octonion algebra that our elements' entries come from.
-        """
-        return self._octonions
+                         **kwargs)
 
-    def nrows(self):
-        return self._nrows
-    ncols = nrows
-
-    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)