]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
eja: normalize the complex hermitian matrix basis, too.
authorMichael Orlitzky <michael@orlitzky.com>
Tue, 20 Aug 2019 23:16:54 +0000 (19:16 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Tue, 20 Aug 2019 23:16:54 +0000 (19:16 -0400)
mjo/eja/eja_algebra.py

index 12166267cd34410e0a03616a4b2ad5c7f5b8c105..99490b0e0aecee54c1ff14314c2060e1d9e553ab 100644 (file)
@@ -18,7 +18,7 @@ from sage.rings.integer_ring import ZZ
 from sage.rings.number_field.number_field import NumberField
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
 from sage.rings.rational_field import QQ
-from sage.rings.real_lazy import CLF
+from sage.rings.real_lazy import CLF, RLF
 from sage.structure.element import is_Matrix
 
 from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
@@ -839,6 +839,12 @@ def _complex_hermitian_basis(n, field):
     """
     Returns a basis for the space of complex Hermitian n-by-n matrices.
 
+    Why do we embed these? Basically, because all of numerical linear
+    algebra assumes that you're working with vectors consisting of `n`
+    entries from a field and scalars from the same field. There's no way
+    to tell SageMath that (for example) the vectors contain complex
+    numbers, while the scalar field is real.
+
     SETUP::
 
         sage: from mjo.eja.eja_algebra import _complex_hermitian_basis
@@ -847,7 +853,10 @@ def _complex_hermitian_basis(n, field):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: B = _complex_hermitian_basis(n, QQ)
+        sage: R = PolynomialRing(QQ, 'z')
+        sage: z = R.gen()
+        sage: field = NumberField(z**2 - 2, 'sqrt2', embedding=RLF(2).sqrt())
+        sage: B = _complex_hermitian_basis(n, field)
         sage: all( M.is_symmetric() for M in  B)
         True
 
@@ -865,7 +874,7 @@ def _complex_hermitian_basis(n, field):
     S = []
     for i in xrange(n):
         for j in xrange(i+1):
-            Eij = matrix(field, n, lambda k,l: k==i and l==j)
+            Eij = matrix(F, n, lambda k,l: k==i and l==j)
             if i == j:
                 Sij = _embed_complex_matrix(Eij)
                 S.append(Sij)
@@ -876,13 +885,25 @@ def _complex_hermitian_basis(n, field):
                 S.append(Sij_real)
                 Sij_imag = _embed_complex_matrix(I*Eij - I*Eij.transpose())
                 S.append(Sij_imag)
-    return tuple(S)
+
+    # Normalize these with our inner product before handing them back.
+    # And since we embedded them, we can drop back to the "field" that
+    # we started with instead of the complex extension "F".
+    return tuple( (s / _complex_hermitian_matrix_ip(s,s).sqrt()).change_ring(field)
+                  for s in S )
+
 
 
 def _quaternion_hermitian_basis(n, field):
     """
     Returns a basis for the space of quaternion Hermitian n-by-n matrices.
 
+    Why do we embed these? Basically, because all of numerical linear
+    algebra assumes that you're working with vectors consisting of `n`
+    entries from a field and scalars from the same field. There's no way
+    to tell SageMath that (for example) the vectors contain complex
+    numbers, while the scalar field is real.
+
     SETUP::
 
         sage: from mjo.eja.eja_algebra import _quaternion_hermitian_basis
@@ -891,7 +912,7 @@ def _quaternion_hermitian_basis(n, field):
 
         sage: set_random_seed()
         sage: n = ZZ.random_element(1,5)
-        sage: B = _quaternion_hermitian_basis(n, QQbar)
+        sage: B = _quaternion_hermitian_basis(n, QQ)
         sage: all( M.is_symmetric() for M in B )
         True
 
@@ -1006,8 +1027,8 @@ def _embed_complex_matrix(M):
     field = M.base_ring()
     blocks = []
     for z in M.list():
-        a = z.real()
-        b = z.imag()
+        a = z.vector()[0] # real part, I guess
+        b = z.vector()[1] # imag part, I guess
         blocks.append(matrix(field, 2, [[a,b],[-b,a]]))
 
     # We can drop the imaginaries here.
@@ -1052,9 +1073,10 @@ def _unembed_complex_matrix(M):
     if not n.mod(2).is_zero():
         raise ValueError("the matrix 'M' must be a complex embedding")
 
-    R = PolynomialRing(QQ, 'z')
+    field = M.base_ring() # This should already have sqrt2
+    R = PolynomialRing(field, 'z')
     z = R.gen()
-    F = NumberField(z**2 + 1, 'i', embedding=CLF(-1).sqrt())
+    F = NumberField(z**2 + 1,'i', embedding=CLF(-1).sqrt())
     i = F.gen()
 
     # Go top-left to bottom-right (reading order), converting every
@@ -1207,6 +1229,12 @@ def _matrix_ip(X,Y):
 def _real_symmetric_matrix_ip(X,Y):
     return (X*Y).trace()
 
+def _complex_hermitian_matrix_ip(X,Y):
+    # This takes EMBEDDED matrices.
+    Xu = _unembed_complex_matrix(X)
+    Yu = _unembed_complex_matrix(Y)
+    # The trace need not be real; consider Xu = (i*I) and Yu = I.
+    return ((Xu*Yu).trace()).vector()[0] # real part, I guess
 
 class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
     """
@@ -1296,7 +1324,7 @@ class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
             # algebra needs to be over the field extension.
             R = PolynomialRing(field, 'z')
             z = R.gen()
-            field = NumberField(z**2 - 2, 'sqrt2')
+            field = NumberField(z**2 - 2, 'sqrt2', embedding=RLF(2).sqrt())
 
         S = _real_symmetric_basis(n, field)
         Qs = _multiplication_table_from_matrix_basis(S)
@@ -1367,8 +1395,32 @@ class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
         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,4)
+        sage: J = ComplexHermitianEJA(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 = ComplexHermitianEJA(n).random_element()
+        sage: x.operator().matrix().is_symmetric()
+        True
+
     """
     def __init__(self, n, field=QQ, **kwargs):
+        if n > 1:
+            # 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.
+            R = PolynomialRing(field, 'z')
+            z = R.gen()
+            field = NumberField(z**2 - 2, 'sqrt2', embedding=RLF(2).sqrt())
         S = _complex_hermitian_basis(n, field)
         Qs = _multiplication_table_from_matrix_basis(S)
 
@@ -1381,14 +1433,9 @@ class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
 
 
     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
+        X = x.natural_representation()
+        Y = y.natural_representation()
+        return _complex_hermitian_matrix_ip(X,Y)
 
 
 class QuaternionHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):