]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/euclidean_jordan_algebra.py
eja: simplify is_invertible() for elements.
[sage.d.git] / mjo / eja / euclidean_jordan_algebra.py
index fa112a373315c21f6b50e6b9edf9ced464923d55..fb92a310c2edaa27cd9b9cab35dd86d908790469 100644 (file)
@@ -21,8 +21,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                               assume_associative=False,
                               category=None,
                               rank=None,
-                              natural_basis=None,
-                              inner_product=None):
+                              natural_basis=None):
         n = len(mult_table)
         mult_table = [b.base_extend(field) for b in mult_table]
         for b in mult_table:
@@ -46,8 +45,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                                  names=names,
                                  category=cat,
                                  rank=rank,
-                                 natural_basis=natural_basis,
-                                 inner_product=inner_product)
+                                 natural_basis=natural_basis)
 
 
     def __init__(self,
@@ -57,8 +55,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                  assume_associative=False,
                  category=None,
                  rank=None,
-                 natural_basis=None,
-                 inner_product=None):
+                 natural_basis=None):
         """
         EXAMPLES:
 
@@ -74,7 +71,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         """
         self._rank = rank
         self._natural_basis = natural_basis
-        self._inner_product = inner_product
         fda = super(FiniteDimensionalEuclideanJordanAlgebra, self)
         fda.__init__(field,
                      mult_table,
@@ -94,7 +90,9 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         """
         The inner product associated with this Euclidean Jordan algebra.
 
-        Will default to the trace inner product if nothing else.
+        Defaults to the trace inner product, but can be overridden by
+        subclasses if they are sure that the necessary properties are
+        satisfied.
 
         EXAMPLES:
 
@@ -112,10 +110,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         """
         if (not x in self) or (not y in self):
             raise TypeError("arguments must live in this algebra")
-        if self._inner_product is None:
-            return x.trace_inner_product(y)
-        else:
-            return self._inner_product(x,y)
+        return x.trace_inner_product(y)
 
 
     def natural_basis(self):
@@ -133,7 +128,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
         EXAMPLES::
 
-            sage: J = RealSymmetricSimpleEJA(2)
+            sage: J = RealSymmetricEJA(2)
             sage: J.basis()
             Family (e0, e1, e2)
             sage: J.natural_basis()
@@ -181,14 +176,14 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             The identity in `S^n` is converted to the identity in the EJA::
 
-                sage: J = RealSymmetricSimpleEJA(3)
+                sage: J = RealSymmetricEJA(3)
                 sage: I = identity_matrix(QQ,3)
                 sage: J(I) == J.one()
                 True
 
             This skew-symmetric matrix can't be represented in the EJA::
 
-                sage: J = RealSymmetricSimpleEJA(3)
+                sage: J = RealSymmetricEJA(3)
                 sage: A = matrix(QQ,3, lambda i,j: i-j)
                 sage: J(A)
                 Traceback (most recent call last):
@@ -309,7 +304,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             so the inner product of the identity matrix with itself
             should be the `n`::
 
-                sage: J = RealSymmetricSimpleEJA(3)
+                sage: J = RealSymmetricEJA(3)
                 sage: J.one().inner_product(J.one())
                 3
 
@@ -318,13 +313,13 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             part because the product of Hermitian matrices may not be
             Hermitian::
 
-                sage: J = ComplexHermitianSimpleEJA(3)
+                sage: J = ComplexHermitianEJA(3)
                 sage: J.one().inner_product(J.one())
                 3
 
             Ditto for the quaternions::
 
-                sage: J = QuaternionHermitianSimpleEJA(3)
+                sage: J = QuaternionHermitianEJA(3)
                 sage: J.one().inner_product(J.one())
                 3
 
@@ -497,8 +492,36 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             We can't use the superclass method because it relies on
             the algebra being associative.
+
+            ALGORITHM:
+
+            The usual way to do this is to check if the determinant is
+            zero, but we need the characteristic polynomial for the
+            determinant. The minimal polynomial is a lot easier to get,
+            so we use Corollary 2 in Chapter V of Koecher to check
+            whether or not the paren't algebra's zero element is a root
+            of this element's minimal polynomial.
+
+            TESTS:
+
+            The identity element is always invertible::
+
+                sage: set_random_seed()
+                sage: J = random_eja()
+                sage: J.one().is_invertible()
+                True
+
+            The zero element is never invertible::
+
+                sage: set_random_seed()
+                sage: J = random_eja()
+                sage: J.zero().is_invertible()
+                False
+
             """
-            return not self.det().is_zero()
+            zero = self.parent().zero()
+            p = self.minimal_polynomial()
+            return not (p(zero) == zero)
 
 
         def is_nilpotent(self):
@@ -670,7 +693,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             EXAMPLES::
 
-                sage: J = ComplexHermitianSimpleEJA(3)
+                sage: J = ComplexHermitianEJA(3)
                 sage: J.one()
                 e0 + e5 + e8
                 sage: J.one().natural_representation()
@@ -683,7 +706,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             ::
 
-                sage: J = QuaternionHermitianSimpleEJA(3)
+                sage: J = QuaternionHermitianEJA(3)
                 sage: J.one()
                 e0 + e9 + e14
                 sage: J.one().natural_representation()
@@ -927,7 +950,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             TESTS::
 
                 sage: set_random_seed()
-                sage: J = eja_rn(5)
+                sage: J = RealCartesianProductEJA(5)
                 sage: c = J.random_element().subalgebra_idempotent()
                 sage: c^2 == c
                 True
@@ -1011,16 +1034,20 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             return (self*other).trace()
 
 
-def eja_rn(dimension, field=QQ):
+class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
     Return the Euclidean Jordan Algebra corresponding to the set
     `R^n` under the Hadamard product.
 
+    Note: this is nothing more than the Cartesian product of ``n``
+    copies of the spin algebra. Once Cartesian product algebras
+    are implemented, this can go.
+
     EXAMPLES:
 
     This multiplication table can be verified by hand::
 
-        sage: J = eja_rn(3)
+        sage: J = RealCartesianProductEJA(3)
         sage: e0,e1,e2 = J.gens()
         sage: e0*e0
         e0
@@ -1036,19 +1063,21 @@ def eja_rn(dimension, field=QQ):
         e2
 
     """
-    # The FiniteDimensionalAlgebra constructor takes a list of
-    # matrices, the ith representing right multiplication by the ith
-    # basis element in the vector space. So if e_1 = (1,0,0), then
-    # right (Hadamard) multiplication of x by e_1 picks out the first
-    # component of x; and likewise for the ith basis element e_i.
-    Qs = [ matrix(field, dimension, dimension, lambda k,j: 1*(k == j == i))
-           for i in xrange(dimension) ]
+    @staticmethod
+    def __classcall_private__(cls, n, field=QQ):
+        # The FiniteDimensionalAlgebra constructor takes a list of
+        # matrices, the ith representing right multiplication by the ith
+        # basis element in the vector space. So if e_1 = (1,0,0), then
+        # right (Hadamard) multiplication of x by e_1 picks out the first
+        # component of x; and likewise for the ith basis element e_i.
+        Qs = [ matrix(field, n, n, lambda k,j: 1*(k == j == i))
+               for i in xrange(n) ]
 
-    return FiniteDimensionalEuclideanJordanAlgebra(field,
-                                                   Qs,
-                                                   rank=dimension,
-                                                   inner_product=_usual_ip)
+        fdeja = super(RealCartesianProductEJA, cls)
+        return fdeja.__classcall_private__(cls, field, Qs, rank=n)
 
+    def inner_product(self, x, y):
+        return _usual_ip(x,y)
 
 
 def random_eja():
@@ -1084,11 +1113,11 @@ def random_eja():
 
     """
     n = ZZ.random_element(1,5)
-    constructor = choice([eja_rn,
+    constructor = choice([RealCartesianProductEJA,
                           JordanSpinEJA,
-                          RealSymmetricSimpleEJA,
-                          ComplexHermitianSimpleEJA,
-                          QuaternionHermitianSimpleEJA])
+                          RealSymmetricEJA,
+                          ComplexHermitianEJA,
+                          QuaternionHermitianEJA])
     return constructor(n, field=QQ)
 
 
@@ -1465,7 +1494,7 @@ def _matrix_ip(X,Y):
     return (X_mat*Y_mat).trace()
 
 
-def RealSymmetricSimpleEJA(n, field=QQ):
+class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
     The rank-n simple EJA consisting of real symmetric n-by-n
     matrices, the usual symmetric Jordan product, and the trace inner
@@ -1473,7 +1502,7 @@ def RealSymmetricSimpleEJA(n, field=QQ):
 
     EXAMPLES::
 
-        sage: J = RealSymmetricSimpleEJA(2)
+        sage: J = RealSymmetricEJA(2)
         sage: e0, e1, e2 = J.gens()
         sage: e0*e0
         e0
@@ -1488,7 +1517,7 @@ def RealSymmetricSimpleEJA(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: J = RealSymmetricSimpleEJA(n)
+        sage: J = RealSymmetricEJA(n)
         sage: J.degree() == (n^2 + n)/2
         True
 
@@ -1496,7 +1525,7 @@ def RealSymmetricSimpleEJA(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: J = RealSymmetricSimpleEJA(n)
+        sage: J = RealSymmetricEJA(n)
         sage: x = J.random_element()
         sage: y = J.random_element()
         sage: actual = (x*y).natural_representation()
@@ -1509,17 +1538,23 @@ def RealSymmetricSimpleEJA(n, field=QQ):
         True
 
     """
-    S = _real_symmetric_basis(n, field=field)
-    (Qs, T) = _multiplication_table_from_matrix_basis(S)
+    @staticmethod
+    def __classcall_private__(cls, n, field=QQ):
+        S = _real_symmetric_basis(n, field=field)
+        (Qs, T) = _multiplication_table_from_matrix_basis(S)
+
+        fdeja = super(RealSymmetricEJA, cls)
+        return fdeja.__classcall_private__(cls,
+                                           field,
+                                           Qs,
+                                           rank=n,
+                                           natural_basis=T)
 
-    return FiniteDimensionalEuclideanJordanAlgebra(field,
-                                                   Qs,
-                                                   rank=n,
-                                                   natural_basis=T,
-                                                   inner_product=_matrix_ip)
+    def inner_product(self, x, y):
+        return _matrix_ip(x,y)
 
 
-def ComplexHermitianSimpleEJA(n, field=QQ):
+class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
     The rank-n simple EJA consisting of complex Hermitian n-by-n
     matrices over the real numbers, the usual symmetric Jordan product,
@@ -1532,7 +1567,7 @@ def ComplexHermitianSimpleEJA(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: J = ComplexHermitianSimpleEJA(n)
+        sage: J = ComplexHermitianEJA(n)
         sage: J.degree() == n^2
         True
 
@@ -1540,7 +1575,7 @@ def ComplexHermitianSimpleEJA(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: J = ComplexHermitianSimpleEJA(n)
+        sage: J = ComplexHermitianEJA(n)
         sage: x = J.random_element()
         sage: y = J.random_element()
         sage: actual = (x*y).natural_representation()
@@ -1553,26 +1588,30 @@ def ComplexHermitianSimpleEJA(n, field=QQ):
         True
 
     """
-    S = _complex_hermitian_basis(n)
-    (Qs, T) = _multiplication_table_from_matrix_basis(S)
+    @staticmethod
+    def __classcall_private__(cls, n, field=QQ):
+        S = _complex_hermitian_basis(n)
+        (Qs, T) = _multiplication_table_from_matrix_basis(S)
 
-    # Since a+bi on the diagonal is represented as
-    #
-    #   a + bi  = [  a  b  ]
-    #             [ -b  a  ],
-    #
-    # we'll double-count the "a" entries if we take the trace of
-    # the embedding.
-    ip = lambda X,Y: _matrix_ip(X,Y)/2
+        fdeja = super(ComplexHermitianEJA, cls)
+        return fdeja.__classcall_private__(cls,
+                                           field,
+                                           Qs,
+                                           rank=n,
+                                           natural_basis=T)
 
-    return FiniteDimensionalEuclideanJordanAlgebra(field,
-                                                   Qs,
-                                                   rank=n,
-                                                   natural_basis=T,
-                                                   inner_product=ip)
+    def inner_product(self, x, y):
+        # Since a+bi on the diagonal is represented as
+        #
+        #   a + bi  = [  a  b  ]
+        #             [ -b  a  ],
+        #
+        # we'll double-count the "a" entries if we take the trace of
+        # the embedding.
+        return _matrix_ip(x,y)/2
 
 
-def QuaternionHermitianSimpleEJA(n, field=QQ):
+class QuaternionHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
     The rank-n simple EJA consisting of self-adjoint n-by-n quaternion
     matrices, the usual symmetric Jordan product, and the
@@ -1585,7 +1624,7 @@ def QuaternionHermitianSimpleEJA(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: J = QuaternionHermitianSimpleEJA(n)
+        sage: J = QuaternionHermitianEJA(n)
         sage: J.degree() == 2*(n^2) - n
         True
 
@@ -1593,7 +1632,7 @@ def QuaternionHermitianSimpleEJA(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: J = QuaternionHermitianSimpleEJA(n)
+        sage: J = QuaternionHermitianEJA(n)
         sage: x = J.random_element()
         sage: y = J.random_element()
         sage: actual = (x*y).natural_representation()
@@ -1606,33 +1645,30 @@ def QuaternionHermitianSimpleEJA(n, field=QQ):
         True
 
     """
-    S = _quaternion_hermitian_basis(n)
-    (Qs, T) = _multiplication_table_from_matrix_basis(S)
-
-    # Since a+bi+cj+dk on the diagonal is represented as
-    #
-    #   a + bi +cj + dk = [  a  b  c  d]
-    #                     [ -b  a -d  c]
-    #                     [ -c  d  a -b]
-    #                     [ -d -c  b  a],
-    #
-    # we'll quadruple-count the "a" entries if we take the trace of
-    # the embedding.
-    ip = lambda X,Y: _matrix_ip(X,Y)/4
+    @staticmethod
+    def __classcall_private__(cls, n, field=QQ):
+        S = _quaternion_hermitian_basis(n)
+        (Qs, T) = _multiplication_table_from_matrix_basis(S)
 
-    return FiniteDimensionalEuclideanJordanAlgebra(field,
-                                                   Qs,
-                                                   rank=n,
-                                                   natural_basis=T,
-                                                   inner_product=ip)
+        fdeja = super(QuaternionHermitianEJA, cls)
+        return fdeja.__classcall_private__(cls,
+                                           field,
+                                           Qs,
+                                           rank=n,
+                                           natural_basis=T)
 
+    def inner_product(self, x, y):
+        # Since a+bi+cj+dk on the diagonal is represented as
+        #
+        #   a + bi +cj + dk = [  a  b  c  d]
+        #                     [ -b  a -d  c]
+        #                     [ -c  d  a -b]
+        #                     [ -d -c  b  a],
+        #
+        # we'll quadruple-count the "a" entries if we take the trace of
+        # the embedding.
+        return _matrix_ip(x,y)/4
 
-def OctonionHermitianSimpleEJA(n):
-    """
-    This shit be crazy. It has dimension 27 over the reals.
-    """
-    n = 3
-    pass
 
 class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
@@ -1678,18 +1714,11 @@ class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
             Qi[0,0] = Qi[0,0] * ~field(2)
             Qs.append(Qi)
 
+        # The rank of the spin algebra is two, unless we're in a
+        # one-dimensional ambient space (because the rank is bounded by
+        # the ambient dimension).
         fdeja = super(JordanSpinEJA, cls)
-        return fdeja.__classcall_private__(cls, field, Qs)
-
-    def rank(self):
-        """
-        Return the rank of this Jordan Spin Algebra.
-
-        The rank of the spin algebra is two, unless we're in a
-        one-dimensional ambient space (because the rank is bounded by
-        the ambient dimension).
-        """
-        return min(self.dimension(),2)
+        return fdeja.__classcall_private__(cls, field, Qs, rank=min(n,2))
 
     def inner_product(self, x, y):
         return _usual_ip(x,y)