True
"""
- if (not x in self) or (not y in self):
- raise TypeError("arguments must live in this algebra")
- return x.trace_inner_product(y)
+ X = x.natural_representation()
+ Y = y.natural_representation()
+ return self.__class__.natural_inner_product(X,Y)
def is_trivial(self):
return self._natural_basis[0].matrix_space()
+ @staticmethod
+ def natural_inner_product(X,Y):
+ """
+ Compute the inner product of two naturally-represented elements.
+
+ For example in the real symmetric matrix EJA, this will compute
+ the trace inner-product of two n-by-n symmetric matrices. The
+ default should work for the real cartesian product EJA, the
+ Jordan spin EJA, and the real symmetric matrices. The others
+ will have to be overridden.
+ """
+ return (X.conjugate_transpose()*Y).trace()
+
+
@cached_method
def one(self):
"""
return fdeja.__init__(field, mult_table, rank=n, **kwargs)
def inner_product(self, x, y):
- return _usual_ip(x,y)
+ """
+ Faster to reimplement than to use natural representations.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import RealCartesianProductEJA
+
+ TESTS:
+
+ Ensure that this is the usual inner product for the algebras
+ over `R^n`::
+
+ 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: X = x.natural_representation()
+ sage: Y = y.natural_representation()
+ sage: x.inner_product(y) == J.__class__.natural_inner_product(X,Y)
+ True
+
+ """
+ return x.to_vector().inner_product(y.to_vector())
def random_eja():
-def _real_symmetric_basis(n, field, normalize):
+def _real_symmetric_basis(n, field):
"""
Return a basis for the space of real symmetric n-by-n matrices.
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: B = _real_symmetric_basis(n, QQbar, False)
+ sage: B = _real_symmetric_basis(n, QQ)
sage: all( M.is_symmetric() for M in B)
True
Sij = Eij
else:
Sij = Eij + Eij.transpose()
- if normalize:
- Sij = Sij / _real_symmetric_matrix_ip(Sij,Sij).sqrt()
S.append(Sij)
return tuple(S)
-def _complex_hermitian_basis(n, field, normalize):
+def _complex_hermitian_basis(n, field):
"""
Returns a basis for the space of complex Hermitian n-by-n matrices.
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: field = QuadraticField(2, 'sqrt2')
- sage: B = _complex_hermitian_basis(n, field, False)
+ sage: B = _complex_hermitian_basis(n, field)
sage: all( M.is_symmetric() for M in B)
True
Sij = _embed_complex_matrix(Eij)
S.append(Sij)
else:
- # Beware, orthogonal but not normalized! The second one
- # has a minus because it's conjugated.
+ # The second one has a minus because it's conjugated.
Sij_real = _embed_complex_matrix(Eij + Eij.transpose())
S.append(Sij_real)
Sij_imag = _embed_complex_matrix(I*Eij - I*Eij.transpose())
# Since we embedded these, we can drop back to the "field" that we
# started with instead of the complex extension "F".
- S = [ s.change_ring(field) for s in S ]
- if normalize:
- S = [ s / _complex_hermitian_matrix_ip(s,s).sqrt() for s in S ]
-
- return tuple(S)
+ return tuple( s.change_ring(field) for s in S )
return matrix(Q, n/4, elements)
-# The usual inner product on R^n.
-def _usual_ip(x,y):
- return x.to_vector().inner_product(y.to_vector())
-
# The inner product used for the real symmetric simple EJA.
# We keep it as a separate function because e.g. the complex
# algebra uses the same inner product, except divided by 2.
Y_mat = Y.natural_representation()
return (X_mat*Y_mat).trace()
-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):
"""
"""
def __init__(self, n, field=QQ, normalize_basis=True, **kwargs):
+ S = _real_symmetric_basis(n, field)
+
if n > 1 and normalize_basis:
# We'll need sqrt(2) to normalize the basis, and this
# winds up in the multiplication table, so the whole
p = z**2 - 2
if p.is_irreducible():
field = NumberField(p, 'sqrt2', embedding=RLF(2).sqrt())
+ S = [ s.change_ring(field) for s in S ]
+ self._basis_denormalizers = tuple(
+ self.__class__.natural_inner_product(s,s).sqrt()
+ for s in S )
+ S = tuple( s/c for (s,c) in zip(S,self._basis_denormalizers) )
- S = _real_symmetric_basis(n, field, normalize_basis)
Qs = _multiplication_table_from_matrix_basis(S)
fdeja = super(RealSymmetricEJA, self)
natural_basis=S,
**kwargs)
- def inner_product(self, x, y):
- X = x.natural_representation()
- Y = y.natural_representation()
- return _real_symmetric_matrix_ip(X,Y)
class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
def __init__(self, n, field=QQ, normalize_basis=True, **kwargs):
+ S = _complex_hermitian_basis(n, field)
+
if n > 1 and normalize_basis:
# We'll need sqrt(2) to normalize the basis, and this
# winds up in the multiplication table, so the whole
p = z**2 - 2
if p.is_irreducible():
field = NumberField(p, 'sqrt2', embedding=RLF(2).sqrt())
+ S = [ s.change_ring(field) for s in S ]
+ self._basis_denormalizers = tuple(
+ self.__class__.natural_inner_product(s,s).sqrt()
+ for s in S )
+ S = tuple( s/c for (s,c) in zip(S,self._basis_denormalizers) )
- S = _complex_hermitian_basis(n, field, normalize_basis)
Qs = _multiplication_table_from_matrix_basis(S)
fdeja = super(ComplexHermitianEJA, self)
**kwargs)
- def inner_product(self, x, y):
- X = x.natural_representation()
- Y = y.natural_representation()
- return _complex_hermitian_matrix_ip(X,Y)
-
+ @staticmethod
+ def natural_inner_product(X,Y):
+ 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 QuaternionHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
return fdeja.__init__(field, mult_table, rank=min(n,2), **kwargs)
def inner_product(self, x, y):
- return _usual_ip(x,y)
+ """
+ Faster to reimplement than to use natural representations.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import JordanSpinEJA
+
+ TESTS:
+
+ Ensure that this is the usual inner product for the algebras
+ over `R^n`::
+
+ 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: X = x.natural_representation()
+ sage: Y = y.natural_representation()
+ sage: x.inner_product(y) == J.__class__.natural_inner_product(X,Y)
+ True
+
+ """
+ return x.to_vector().inner_product(y.to_vector())