From: Michael Orlitzky Date: Tue, 20 Aug 2019 23:16:54 +0000 (-0400) Subject: eja: normalize the complex hermitian matrix basis, too. X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=419af75ae2b0c1bcefbf7ae39f912504dc75c828;p=sage.d.git eja: normalize the complex hermitian matrix basis, too. --- diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 1216626..99490b0 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -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):