]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
eja: add some more TODO items.
[sage.d.git] / mjo / eja / eja_algebra.py
index 02cf32c7d27ff93728ad548a964ff6bee5f277d4..4673e916433509876638cfaeabfba5883c2a0192 100644 (file)
@@ -21,7 +21,6 @@ from sage.rings.number_field.number_field import NumberField, QuadraticField
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
 from sage.rings.rational_field import QQ
 from sage.rings.real_lazy import CLF, RLF
-from sage.structure.element import is_Matrix
 
 from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
 from mjo.eja.eja_utils import _mat2vec
@@ -153,26 +152,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
         return self.from_vector(coords)
 
 
-    @staticmethod
-    def _max_test_case_size():
-        """
-        Return an integer "size" that is an upper bound on the size of
-        this algebra when it is used in a random test
-        case. Unfortunately, the term "size" is quite vague -- when
-        dealing with `R^n` under either the Hadamard or Jordan spin
-        product, the "size" refers to the dimension `n`. When dealing
-        with a matrix algebra (real symmetric or complex/quaternion
-        Hermitian), it refers to the size of the matrix, which is
-        far less than the dimension of the underlying vector space.
-
-        We default to five in this class, which is safe in `R^n`. The
-        matrix algebra subclasses (or any class where the "size" is
-        interpreted to be far less than the dimension) should override
-        with a smaller number.
-        """
-        return 5
-
-
     def _repr_(self):
         """
         Return a string representation of ``self``.
@@ -426,8 +405,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
 
         EXAMPLES:
 
-        Our inner product satisfies the Jordan axiom, which is also
-        referred to as "associativity" for a symmetric bilinear form::
+        Our inner product is "associative," which means the following for
+        a symmetric bilinear form::
 
             sage: set_random_seed()
             sage: J = random_eja()
@@ -660,27 +639,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
         """
         return  tuple( self.random_element() for idx in xrange(count) )
 
-    @classmethod
-    def random_instance(cls, field=QQ, **kwargs):
-        """
-        Return a random instance of this type of algebra.
-
-        In subclasses for algebras that we know how to construct, this
-        is a shortcut for constructing test cases and examples.
-        """
-        if cls is FiniteDimensionalEuclideanJordanAlgebra:
-            # Red flag! But in theory we could do this I guess. The
-            # only finite-dimensional exceptional EJA is the
-            # octononions. So, we could just create an EJA from an
-            # associative matrix algebra (generated by a subset of
-            # elements) with the symmetric product. Or, we could punt
-            # to random_eja() here, override it in our subclasses, and
-            # not worry about it.
-            raise NotImplementedError
-
-        n = ZZ.random_element(cls._max_test_case_size()) + 1
-        return cls(n, field, **kwargs)
-
 
     def rank(self):
         """
@@ -757,7 +715,57 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
     Element = FiniteDimensionalEuclideanJordanAlgebraElement
 
 
-class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
+class KnownRankEJA(object):
+    """
+    A class for algebras that we actually know we can construct.  The
+    main issue is that, for most of our methods to make sense, we need
+    to know the rank of our algebra. Thus we can't simply generate a
+    "random" algebra, or even check that a given basis and product
+    satisfy the axioms; because even if everything looks OK, we wouldn't
+    know the rank we need to actuallty build the thing.
+
+    Not really a subclass of FDEJA because doing that causes method
+    resolution errors, e.g.
+
+      TypeError: Error when calling the metaclass bases
+      Cannot create a consistent method resolution
+      order (MRO) for bases FiniteDimensionalEuclideanJordanAlgebra,
+      KnownRankEJA
+
+    """
+    @staticmethod
+    def _max_test_case_size():
+        """
+        Return an integer "size" that is an upper bound on the size of
+        this algebra when it is used in a random test
+        case. Unfortunately, the term "size" is quite vague -- when
+        dealing with `R^n` under either the Hadamard or Jordan spin
+        product, the "size" refers to the dimension `n`. When dealing
+        with a matrix algebra (real symmetric or complex/quaternion
+        Hermitian), it refers to the size of the matrix, which is
+        far less than the dimension of the underlying vector space.
+
+        We default to five in this class, which is safe in `R^n`. The
+        matrix algebra subclasses (or any class where the "size" is
+        interpreted to be far less than the dimension) should override
+        with a smaller number.
+        """
+        return 5
+
+    @classmethod
+    def random_instance(cls, field=QQ, **kwargs):
+        """
+        Return a random instance of this type of algebra.
+
+        Beware, this will crash for "most instances" because the
+        constructor below looks wrong.
+        """
+        n = ZZ.random_element(cls._max_test_case_size()) + 1
+        return cls(n, field, **kwargs)
+
+
+class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra,
+                              KnownRankEJA):
     """
     Return the Euclidean Jordan Algebra corresponding to the set
     `R^n` under the Hadamard product.
@@ -866,11 +874,7 @@ def random_eja():
         Euclidean Jordan algebra of dimension...
 
     """
-    classname = choice([RealCartesianProductEJA,
-                        JordanSpinEJA,
-                        RealSymmetricEJA,
-                        ComplexHermitianEJA,
-                        QuaternionHermitianEJA])
+    classname = choice(KnownRankEJA.__subclasses__())
     return classname.random_instance()
 
 
@@ -935,8 +939,9 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra):
         else:
             basis = ( (b/n) for (b,n) in izip(self.natural_basis(),
                                               self._basis_normalizers) )
-            field = self.base_ring().base_ring() # yeeeaahhhhhhh
-            J = MatrixEuclideanJordanAlgebra(field,
+
+            # Do this over the rationals and convert back at the end.
+            J = MatrixEuclideanJordanAlgebra(QQ,
                                              basis,
                                              self.rank(),
                                              normalize_basis=False)
@@ -945,7 +950,14 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra):
             # p might be missing some vars, have to substitute "optionally"
             pairs = izip(x.base_ring().gens(), self._basis_normalizers)
             substitutions = { v: v*c for (v,c) in pairs }
-            return p.subs(substitutions)
+            result = p.subs(substitutions)
+
+            # The result of "subs" can be either a coefficient-ring
+            # element or a polynomial. Gotta handle both cases.
+            if result in QQ:
+                return self.base_ring()(result)
+            else:
+                return result.change_ring(self.base_ring())
 
 
     @staticmethod
@@ -1041,7 +1053,7 @@ class RealMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
         return M
 
 
-class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra):
+class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra, KnownRankEJA):
     """
     The rank-n simple EJA consisting of real symmetric n-by-n
     matrices, the usual symmetric Jordan product, and the trace inner
@@ -1304,7 +1316,7 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
         return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/2
 
 
-class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra):
+class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra, KnownRankEJA):
     """
     The rank-n simple EJA consisting of complex Hermitian n-by-n
     matrices over the real numbers, the usual symmetric Jordan product,
@@ -1528,7 +1540,7 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
         if M.ncols() != n:
             raise ValueError("the matrix 'M' must be square")
         if not n.mod(4).is_zero():
-            raise ValueError("the matrix 'M' must be a complex embedding")
+            raise ValueError("the matrix 'M' must be a quaternion embedding")
 
         # Use the base ring of the matrix to ensure that its entries can be
         # multiplied by elements of the quaternion algebra.
@@ -1588,7 +1600,8 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
         return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/4
 
 
-class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra):
+class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra,
+                             KnownRankEJA):
     """
     The rank-n simple EJA consisting of self-adjoint n-by-n quaternion
     matrices, the usual symmetric Jordan product, and the
@@ -1699,7 +1712,10 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra):
                     S.append(Sij_J)
                     Sij_K = cls.real_embed(K*Eij - K*Eij.transpose())
                     S.append(Sij_K)
-        return S
+
+        # Since we embedded these, we can drop back to the "field" that we
+        # started with instead of the quaternion algebra "Q".
+        return ( s.change_ring(field) for s in S )
 
 
     def __init__(self, n, field=QQ, **kwargs):
@@ -1707,7 +1723,7 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra):
         super(QuaternionHermitianEJA,self).__init__(field, basis, n, **kwargs)
 
 
-class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
+class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA):
     """
     The rank-2 simple EJA consisting of real vectors ``x=(x0, x_bar)``
     with the usual inner product and jordan product ``x*y =