]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
eja: add fast natural_inner_product() overrides for complex/quaternions.
[sage.d.git] / mjo / eja / eja_algebra.py
index 29ff1da38480b3b9b07d1ff39555948e31785732..ae5baa0eff4a64dac97944bbdaf5a8ec132495bd 100644 (file)
@@ -5,6 +5,8 @@ are used in optimization, and have some additional nice methods beyond
 what can be supported in a general Jordan Algebra.
 """
 
+from itertools import repeat
+
 from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
 from sage.categories.magmatic_algebras import MagmaticAlgebras
 from sage.combinat.free_module import CombinatorialFreeModule
@@ -51,8 +53,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
+            sage: x,y = J.random_elements(2)
             sage: x*y == y*x
             True
 
@@ -446,9 +447,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
-            sage: z = J.random_element()
+            sage: x,y,z = J.random_elements(3)
             sage: (x*y).inner_product(z) == y.inner_product(x*z)
             True
 
@@ -657,6 +656,25 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
             s = super(FiniteDimensionalEuclideanJordanAlgebra, self)
             return s.random_element()
 
+    def random_elements(self, count):
+        """
+        Return ``count`` random elements as a tuple.
+
+        SETUP::
+
+            sage: from mjo.eja.eja_algebra import JordanSpinEJA
+
+        EXAMPLES::
+
+            sage: J = JordanSpinEJA(3)
+            sage: x,y,z = J.random_elements(3)
+            sage: all( [ x in J, y in J, z in J ])
+            True
+            sage: len( J.random_elements(10) ) == 10
+            True
+
+        """
+        return  tuple( self.random_element() for idx in xrange(count) )
 
     @classmethod
     def random_instance(cls, field=QQ, **kwargs):
@@ -713,18 +731,12 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
         The rank of the `n`-by-`n` Hermitian real, complex, or
         quaternion matrices is `n`::
 
-            sage: RealSymmetricEJA(2).rank()
-            2
-            sage: ComplexHermitianEJA(2).rank()
-            2
+            sage: RealSymmetricEJA(4).rank()
+            4
+            sage: ComplexHermitianEJA(3).rank()
+            3
             sage: QuaternionHermitianEJA(2).rank()
             2
-            sage: RealSymmetricEJA(5).rank()
-            5
-            sage: ComplexHermitianEJA(5).rank()
-            5
-            sage: QuaternionHermitianEJA(5).rank()
-            5
 
         TESTS:
 
@@ -824,8 +836,7 @@ class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
 
             sage: set_random_seed()
             sage: J = RealCartesianProductEJA.random_instance()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
+            sage: x,y = J.random_elements(2)
             sage: X = x.natural_representation()
             sage: Y = y.natural_representation()
             sage: x.inner_product(y) == J.natural_inner_product(X,Y)
@@ -996,7 +1007,33 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra):
             return tr.coefficient_tuple()[0]
 
 
-class RealSymmetricEJA(MatrixEuclideanJordanAlgebra):
+class RealMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
+    @staticmethod
+    def real_embed(M):
+        """
+        Embed the matrix ``M`` into a space of real matrices.
+
+        The matrix ``M`` can have entries in any field at the moment:
+        the real numbers, complex numbers, or quaternions. And although
+        they are not a field, we can probably support octonions at some
+        point, too. This function returns a real matrix that "acts like"
+        the original with respect to matrix multiplication; i.e.
+
+          real_embed(M*N) = real_embed(M)*real_embed(N)
+
+        """
+        return M
+
+
+    @staticmethod
+    def real_unembed(M):
+        """
+        The inverse of :meth:`real_embed`.
+        """
+        return M
+
+
+class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra):
     """
     The rank-n simple EJA consisting of real symmetric n-by-n
     matrices, the usual symmetric Jordan product, and the trace inner
@@ -1032,8 +1069,7 @@ class RealSymmetricEJA(MatrixEuclideanJordanAlgebra):
 
         sage: set_random_seed()
         sage: J = RealSymmetricEJA.random_instance()
-        sage: x = J.random_element()
-        sage: y = J.random_element()
+        sage: x,y = J.random_elements(2)
         sage: actual = (x*y).natural_representation()
         sage: X = x.natural_representation()
         sage: Y = y.natural_representation()
@@ -1104,30 +1140,6 @@ class RealSymmetricEJA(MatrixEuclideanJordanAlgebra):
     def _max_test_case_size():
         return 5 # Dimension 10
 
-    @staticmethod
-    def real_embed(M):
-        """
-        Embed the matrix ``M`` into a space of real matrices.
-
-        The matrix ``M`` can have entries in any field at the moment:
-        the real numbers, complex numbers, or quaternions. And although
-        they are not a field, we can probably support octonions at some
-        point, too. This function returns a real matrix that "acts like"
-        the original with respect to matrix multiplication; i.e.
-
-          real_embed(M*N) = real_embed(M)*real_embed(N)
-
-        """
-        return M
-
-
-    @staticmethod
-    def real_unembed(M):
-        """
-        The inverse of :meth:`real_embed`.
-        """
-        return M
-
 
 
 class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
@@ -1249,6 +1261,37 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
         return matrix(F, n/2, elements)
 
 
+    @classmethod
+    def natural_inner_product(cls,X,Y):
+        """
+        Compute a natural inner product in this algebra directly from
+        its real embedding.
+
+        SETUP::
+
+            sage: from mjo.eja.eja_algebra import ComplexHermitianEJA
+
+        TESTS:
+
+        This gives the same answer as the slow, default method implemented
+        in :class:`MatrixEuclideanJordanAlgebra`::
+
+            sage: set_random_seed()
+            sage: J = ComplexHermitianEJA.random_instance()
+            sage: x,y = J.random_elements(2)
+            sage: Xe = x.natural_representation()
+            sage: Ye = y.natural_representation()
+            sage: X = ComplexHermitianEJA.real_unembed(Xe)
+            sage: Y = ComplexHermitianEJA.real_unembed(Ye)
+            sage: expected = (X*Y).trace().vector()[0]
+            sage: actual = ComplexHermitianEJA.natural_inner_product(Xe,Ye)
+            sage: actual == expected
+            True
+
+        """
+        return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/2
+
+
 class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra):
     """
     The rank-n simple EJA consisting of complex Hermitian n-by-n
@@ -1275,8 +1318,7 @@ class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra):
 
         sage: set_random_seed()
         sage: J = ComplexHermitianEJA.random_instance()
-        sage: x = J.random_element()
-        sage: y = J.random_element()
+        sage: x,y = J.random_elements(2)
         sage: actual = (x*y).natural_representation()
         sage: X = x.natural_representation()
         sage: Y = y.natural_representation()
@@ -1498,6 +1540,36 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
         return matrix(Q, n/4, elements)
 
 
+    @classmethod
+    def natural_inner_product(cls,X,Y):
+        """
+        Compute a natural inner product in this algebra directly from
+        its real embedding.
+
+        SETUP::
+
+            sage: from mjo.eja.eja_algebra import QuaternionHermitianEJA
+
+        TESTS:
+
+        This gives the same answer as the slow, default method implemented
+        in :class:`MatrixEuclideanJordanAlgebra`::
+
+            sage: set_random_seed()
+            sage: J = QuaternionHermitianEJA.random_instance()
+            sage: x,y = J.random_elements(2)
+            sage: Xe = x.natural_representation()
+            sage: Ye = y.natural_representation()
+            sage: X = QuaternionHermitianEJA.real_unembed(Xe)
+            sage: Y = QuaternionHermitianEJA.real_unembed(Ye)
+            sage: expected = (X*Y).trace().coefficient_tuple()[0]
+            sage: actual = QuaternionHermitianEJA.natural_inner_product(Xe,Ye)
+            sage: actual == expected
+            True
+
+        """
+        return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/4
+
 
 class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra):
     """
@@ -1525,8 +1597,7 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra):
 
         sage: set_random_seed()
         sage: J = QuaternionHermitianEJA.random_instance()
-        sage: x = J.random_element()
-        sage: y = J.random_element()
+        sage: x,y = J.random_elements(2)
         sage: actual = (x*y).natural_representation()
         sage: X = x.natural_representation()
         sage: Y = y.natural_representation()
@@ -1691,8 +1762,7 @@ class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
 
             sage: set_random_seed()
             sage: J = JordanSpinEJA.random_instance()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
+            sage: x,y = J.random_elements(2)
             sage: X = x.natural_representation()
             sage: Y = y.natural_representation()
             sage: x.inner_product(y) == J.natural_inner_product(X,Y)