]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
eja: normalize the real symmetric matrix basis.
[sage.d.git] / mjo / eja / eja_algebra.py
index 06f6f531ac46305da59ec2b4290ba808602cea50..992174e45b763e95677588beee2787859c0e64b1 100644 (file)
@@ -9,6 +9,7 @@ from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
 from sage.categories.magmatic_algebras import MagmaticAlgebras
 from sage.combinat.free_module import CombinatorialFreeModule
 from sage.matrix.constructor import matrix
+from sage.matrix.matrix_space import MatrixSpace
 from sage.misc.cachefunc import cached_method
 from sage.misc.prandom import choice
 from sage.misc.table import table
@@ -134,13 +135,17 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
             return self.zero()
 
         natural_basis = self.natural_basis()
-        if elt not in natural_basis[0].matrix_space():
+        basis_space = natural_basis[0].matrix_space()
+        if elt not in basis_space:
             raise ValueError("not a naturally-represented algebra element")
 
-        # Thanks for nothing! Matrix spaces aren't vector
-        # spaces in Sage, so we have to figure out its
-        # natural-basis coordinates ourselves.
-        V = VectorSpace(elt.base_ring(), elt.nrows()*elt.ncols())
+        # Thanks for nothing! Matrix spaces aren't vector spaces in
+        # Sage, so we have to figure out its natural-basis coordinates
+        # ourselves. We use the basis space's ring instead of the
+        # element's ring because the basis space might be an algebraic
+        # closure whereas the base ring of the 3-by-3 identity matrix
+        # could be QQ instead of QQbar.
+        V = VectorSpace(basis_space.base_ring(), elt.nrows()*elt.ncols())
         W = V.span_of_basis( _mat2vec(s) for s in natural_basis )
         coords =  W.coordinate_vector(_mat2vec(elt))
         return self.from_vector(coords)
@@ -497,8 +502,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
             Finite family {0: e0, 1: e1, 2: e2}
             sage: J.natural_basis()
             (
-            [1 0]  [0 1]  [0 0]
-            [0 0], [1 0], [0 1]
+            [1 0]  [        0 1/2*sqrt2]  [0 0]
+            [0 0], [1/2*sqrt2         0], [0 1]
             )
 
         ::
@@ -514,11 +519,23 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
 
         """
         if self._natural_basis is None:
-            return tuple( b.to_vector().column() for b in self.basis() )
+            M = self.natural_basis_space()
+            return tuple( M(b.to_vector()) for b in self.basis() )
         else:
             return self._natural_basis
 
 
+    def natural_basis_space(self):
+        """
+        Return the matrix space in which this algebra's natural basis
+        elements live.
+        """
+        if self._natural_basis is None or len(self._natural_basis) == 0:
+            return MatrixSpace(self.base_ring(), self.dimension(), 1)
+        else:
+            return self._natural_basis[0].matrix_space()
+
+
     @cached_method
     def one(self):
         """
@@ -661,7 +678,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
 
             sage: J = RealSymmetricEJA(2)
             sage: J.vector_space()
-            Vector space of dimension 3 over Rational Field
+            Vector space of dimension 3 over...
 
         """
         return self.zero().to_vector().parent().ambient_vector_space()
@@ -709,6 +726,17 @@ class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
         sage: RealCartesianProductEJA(3, prefix='r').gens()
         (r0, r1, r2)
 
+    Our inner product satisfies the Jordan axiom::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: J = RealCartesianProductEJA(n)
+        sage: x = J.random_element()
+        sage: y = J.random_element()
+        sage: z = J.random_element()
+        sage: (x*y).inner_product(z) == y.inner_product(x*z)
+        True
+
     """
     def __init__(self, n, field=QQ, **kwargs):
         V = VectorSpace(field, n)
@@ -773,9 +801,22 @@ def random_eja():
 
 
 
-def _real_symmetric_basis(n, field=QQ):
+def _real_symmetric_basis(n, field):
     """
     Return a basis for the space of real symmetric n-by-n matrices.
+
+    SETUP::
+
+        sage: from mjo.eja.eja_algebra import _real_symmetric_basis
+
+    TESTS::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: B = _real_symmetric_basis(n, QQbar)
+        sage: all( M.is_symmetric() for M in  B)
+        True
+
     """
     # The basis of symmetric matrices, as matrices, in their R^(n-by-n)
     # coordinates.
@@ -786,13 +827,14 @@ def _real_symmetric_basis(n, field=QQ):
             if i == j:
                 Sij = Eij
             else:
-                # Beware, orthogonal but not normalized!
                 Sij = Eij + Eij.transpose()
+            # Now normalize it.
+            Sij = Sij / _real_symmetric_matrix_ip(Sij,Sij).sqrt()
             S.append(Sij)
     return tuple(S)
 
 
-def _complex_hermitian_basis(n, field=QQ):
+def _complex_hermitian_basis(n, field):
     """
     Returns a basis for the space of complex Hermitian n-by-n matrices.
 
@@ -804,7 +846,8 @@ def _complex_hermitian_basis(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: all( M.is_symmetric() for M in _complex_hermitian_basis(n) )
+        sage: B = _complex_hermitian_basis(n, QQ)
+        sage: all( M.is_symmetric() for M in  B)
         True
 
     """
@@ -833,7 +876,7 @@ def _complex_hermitian_basis(n, field=QQ):
     return tuple(S)
 
 
-def _quaternion_hermitian_basis(n, field=QQ):
+def _quaternion_hermitian_basis(n, field):
     """
     Returns a basis for the space of quaternion Hermitian n-by-n matrices.
 
@@ -845,7 +888,8 @@ def _quaternion_hermitian_basis(n, field=QQ):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: all( M.is_symmetric() for M in _quaternion_hermitian_basis(n) )
+        sage: B = _quaternion_hermitian_basis(n, QQbar)
+        sage: all( M.is_symmetric() for M in B )
         True
 
     """
@@ -1147,6 +1191,9 @@ def _matrix_ip(X,Y):
     Y_mat = Y.natural_representation()
     return (X_mat*Y_mat).trace()
 
+def _real_symmetric_matrix_ip(X,Y):
+    return (X*Y).trace()
+
 
 class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
@@ -1165,7 +1212,7 @@ class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
         sage: e0*e0
         e0
         sage: e1*e1
-        e0 + e2
+        1/2*e0 + 1/2*e2
         sage: e2*e2
         e2
 
@@ -1200,9 +1247,43 @@ class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
         sage: RealSymmetricEJA(3, prefix='q').gens()
         (q0, q1, q2, q3, q4, q5)
 
+    Our inner product satisfies the Jordan axiom::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: J = RealSymmetricEJA(n)
+        sage: x = J.random_element()
+        sage: y = J.random_element()
+        sage: z = J.random_element()
+        sage: (x*y).inner_product(z) == y.inner_product(x*z)
+        True
+
+    Our basis is normalized with respect to the natural inner product::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: J = RealSymmetricEJA(n)
+        sage: all( b.norm() == 1 for b in J.gens() )
+        True
+
+    Left-multiplication operators are symmetric because they satisfy
+    the Jordan axiom::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: x = RealSymmetricEJA(n).random_element()
+        sage: x.operator().matrix().is_symmetric()
+        True
+
     """
     def __init__(self, n, field=QQ, **kwargs):
-        S = _real_symmetric_basis(n, field=field)
+        if n > 1 and field is QQ:
+            # We'll need sqrt(2) to normalize the basis, and this
+            # winds up in the multiplication table, so the whole
+            # algebra needs to be over the field extension.
+            field = QuadraticField(2, 'sqrt2')
+
+        S = _real_symmetric_basis(n, field)
         Qs = _multiplication_table_from_matrix_basis(S)
 
         fdeja = super(RealSymmetricEJA, self)
@@ -1213,7 +1294,9 @@ class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
                               **kwargs)
 
     def inner_product(self, x, y):
-        return _matrix_ip(x,y)
+        X = x.natural_representation()
+        Y = y.natural_representation()
+        return _real_symmetric_matrix_ip(X,Y)
 
 
 class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
@@ -1258,9 +1341,20 @@ class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
         sage: ComplexHermitianEJA(2, prefix='z').gens()
         (z0, z1, z2, z3)
 
+    Our inner product satisfies the Jordan axiom::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: J = ComplexHermitianEJA(n)
+        sage: x = J.random_element()
+        sage: y = J.random_element()
+        sage: z = J.random_element()
+        sage: (x*y).inner_product(z) == y.inner_product(x*z)
+        True
+
     """
     def __init__(self, n, field=QQ, **kwargs):
-        S = _complex_hermitian_basis(n)
+        S = _complex_hermitian_basis(n, field)
         Qs = _multiplication_table_from_matrix_basis(S)
 
         fdeja = super(ComplexHermitianEJA, self)
@@ -1324,9 +1418,20 @@ class QuaternionHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
         sage: QuaternionHermitianEJA(2, prefix='a').gens()
         (a0, a1, a2, a3, a4, a5)
 
+    Our inner product satisfies the Jordan axiom::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: J = QuaternionHermitianEJA(n)
+        sage: x = J.random_element()
+        sage: y = J.random_element()
+        sage: z = J.random_element()
+        sage: (x*y).inner_product(z) == y.inner_product(x*z)
+        True
+
     """
     def __init__(self, n, field=QQ, **kwargs):
-        S = _quaternion_hermitian_basis(n)
+        S = _quaternion_hermitian_basis(n, field)
         Qs = _multiplication_table_from_matrix_basis(S)
 
         fdeja = super(QuaternionHermitianEJA, self)
@@ -1386,6 +1491,17 @@ class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
         sage: JordanSpinEJA(2, prefix='B').gens()
         (B0, B1)
 
+    Our inner product satisfies the Jordan axiom::
+
+        sage: set_random_seed()
+        sage: n = ZZ.random_element(1,5)
+        sage: J = JordanSpinEJA(n)
+        sage: x = J.random_element()
+        sage: y = J.random_element()
+        sage: z = J.random_element()
+        sage: (x*y).inner_product(z) == y.inner_product(x*z)
+        True
+
     """
     def __init__(self, n, field=QQ, **kwargs):
         V = VectorSpace(field, n)