self._rank = rank
self._natural_basis = natural_basis
+ # TODO: HACK for the charpoly.. needs redesign badly.
+ self._basis_normalizers = None
+
if category is None:
category = MagmaticAlgebras(field).FiniteDimensional()
category = category.WithBasis().Unital()
return V.span_of_basis(b)
+
@cached_method
def _charpoly_coeff(self, i):
"""
store the trace/determinant (a_{r-1} and a_{0} respectively)
separate from the entire characteristic polynomial.
"""
+ if self._basis_normalizers is not None:
+ # Must be a matrix class?
+ # WARNING/TODO: this whole mess is mis-designed.
+ n = self.natural_basis_space().nrows()
+ field = self.base_ring().base_ring() # yeeeeaaaahhh
+ J = self.__class__(n, field, False)
+ (_,x,_,_) = J._charpoly_matrix_system()
+ p = J._charpoly_coeff(i)
+ # p might be missing some vars, have to substitute "optionally"
+ pairs = zip(x.base_ring().gens(), self._basis_normalizers)
+ substitutions = { v: v*c for (v,c) in pairs }
+ return p.subs(substitutions)
+
(A_of_x, x, xr, detA) = self._charpoly_matrix_system()
R = A_of_x.base_ring()
if i >= self.rank():
"""
X = x.natural_representation()
Y = y.natural_representation()
- return self.__class__.natural_inner_product(X,Y)
+ return self.natural_inner_product(X,Y)
def is_trivial(self):
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)
+ sage: x.inner_product(y) == J.natural_inner_product(X,Y)
True
"""
-def _quaternion_hermitian_basis(n, field, normalize):
+def _quaternion_hermitian_basis(n, field):
"""
Returns a basis for the space of quaternion Hermitian n-by-n matrices.
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: B = _quaternion_hermitian_basis(n, QQ, False)
+ sage: B = _quaternion_hermitian_basis(n, QQ)
sage: all( M.is_symmetric() for M in B )
True
if not n.mod(4).is_zero():
raise ValueError("the matrix 'M' must be a complex embedding")
- Q = QuaternionAlgebra(QQ,-1,-1)
+ # Use the base ring of the matrix to ensure that its entries can be
+ # multiplied by elements of the quaternion algebra.
+ field = M.base_ring()
+ Q = QuaternionAlgebra(field,-1,-1)
i,j,k = Q.gens()
# Go top-left to bottom-right (reading order), converting every
raise ValueError('bad on-diagonal submatrix')
if submat[0,1] != -submat[1,0].conjugate():
raise ValueError('bad off-diagonal submatrix')
- z = submat[0,0].real() + submat[0,0].imag()*i
- z += submat[0,1].real()*j + submat[0,1].imag()*k
+ z = submat[0,0].vector()[0] # real part
+ z += submat[0,0].vector()[1]*i # imag part
+ z += submat[0,1].vector()[0]*j # real part
+ z += submat[0,1].vector()[1]*k # imag part
elements.append(z)
return matrix(Q, n/4, elements)
sage: (x*y).inner_product(z) == y.inner_product(x*z)
True
- Our basis is normalized with respect to the natural inner product::
+ Our natural basis is normalized with respect to the natural inner
+ product unless we specify otherwise::
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: all( b.norm() == 1 for b in J.gens() )
True
- Left-multiplication operators are symmetric because they satisfy
- the Jordan axiom::
+ Since our natural basis is normalized with respect to the natural
+ inner product, and since we know that this algebra is an EJA, any
+ left-multiplication operator's matrix will be symmetric because
+ natural->EJA basis representation is an isometry and within the EJA
+ the operator is self-adjoint by the Jordan axiom::
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
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 = [ s.change_ring(field) for s in S ]
+ self._basis_normalizers = tuple(
+ ~(self.natural_inner_product(s,s).sqrt()) for s in S )
+ S = tuple( s*c for (s,c) in zip(S,self._basis_normalizers) )
Qs = _multiplication_table_from_matrix_basis(S)
sage: (x*y).inner_product(z) == y.inner_product(x*z)
True
- Our basis is normalized with respect to the natural inner product::
+ Our natural basis is normalized with respect to the natural inner
+ product unless we specify otherwise::
sage: set_random_seed()
sage: n = ZZ.random_element(1,4)
sage: all( b.norm() == 1 for b in J.gens() )
True
- Left-multiplication operators are symmetric because they satisfy
- the Jordan axiom::
+ Since our natural basis is normalized with respect to the natural
+ inner product, and since we know that this algebra is an EJA, any
+ left-multiplication operator's matrix will be symmetric because
+ natural->EJA basis representation is an isometry and within the EJA
+ the operator is self-adjoint by the Jordan axiom::
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
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 = [ s.change_ring(field) for s in S ]
+ self._basis_normalizers = tuple(
+ ~(self.natural_inner_product(s,s).sqrt()) for s in S )
+ S = tuple( s*c for (s,c) in zip(S,self._basis_normalizers) )
Qs = _multiplication_table_from_matrix_basis(S)
# 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):
"""
The rank-n simple EJA consisting of self-adjoint n-by-n quaternion
The dimension of this algebra is `n^2`::
sage: set_random_seed()
- sage: n = ZZ.random_element(1,5)
+ sage: n = ZZ.random_element(1,4)
sage: J = QuaternionHermitianEJA(n)
sage: J.dimension() == 2*(n^2) - n
True
The Jordan multiplication is what we think it is::
sage: set_random_seed()
- sage: n = ZZ.random_element(1,5)
+ sage: n = ZZ.random_element(1,4)
sage: J = QuaternionHermitianEJA(n)
sage: x = J.random_element()
sage: y = J.random_element()
Our inner product satisfies the Jordan axiom::
sage: set_random_seed()
- sage: n = ZZ.random_element(1,5)
+ sage: n = ZZ.random_element(1,4)
sage: J = QuaternionHermitianEJA(n)
sage: x = J.random_element()
sage: y = J.random_element()
sage: (x*y).inner_product(z) == y.inner_product(x*z)
True
+ Our natural basis is normalized with respect to the natural inner
+ product unless we specify otherwise::
+
+ sage: set_random_seed()
+ sage: n = ZZ.random_element(1,4)
+ sage: J = QuaternionHermitianEJA(n)
+ sage: all( b.norm() == 1 for b in J.gens() )
+ True
+
+ Since our natural basis is normalized with respect to the natural
+ inner product, and since we know that this algebra is an EJA, any
+ left-multiplication operator's matrix will be symmetric because
+ natural->EJA basis representation is an isometry and within the EJA
+ the operator is self-adjoint by the Jordan axiom::
+
+ sage: set_random_seed()
+ sage: n = ZZ.random_element(1,5)
+ sage: x = QuaternionHermitianEJA(n).random_element()
+ sage: x.operator().matrix().is_symmetric()
+ True
+
"""
def __init__(self, n, field=QQ, normalize_basis=True, **kwargs):
- S = _quaternion_hermitian_basis(n, field, normalize_basis)
+ S = _quaternion_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
+ # algebra needs to be over the field extension.
+ R = PolynomialRing(field, 'z')
+ z = R.gen()
+ 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_normalizers = tuple(
+ ~(self.natural_inner_product(s,s).sqrt()) for s in S )
+ S = tuple( s*c for (s,c) in zip(S,self._basis_normalizers) )
+
Qs = _multiplication_table_from_matrix_basis(S)
fdeja = super(QuaternionHermitianEJA, self)
natural_basis=S,
**kwargs)
- def inner_product(self, x, y):
- # Since a+bi+cj+dk on the diagonal is represented as
- #
- # a + bi +cj + dk = [ a b c d]
- # [ -b a -d c]
- # [ -c d a -b]
- # [ -d -c b a],
- #
- # we'll quadruple-count the "a" entries if we take the trace of
- # the embedding.
- return _matrix_ip(x,y)/4
+ @staticmethod
+ def natural_inner_product(X,Y):
+ Xu = _unembed_quaternion_matrix(X)
+ Yu = _unembed_quaternion_matrix(Y)
+ # The trace need not be real; consider Xu = (i*I) and Yu = I.
+ # The result will be a quaternion algebra element, which doesn't
+ # have a "vector" method, but does have coefficient_tuple() method
+ # that returns the coefficients of 1, i, j, and k -- in that order.
+ return ((Xu*Yu).trace()).coefficient_tuple()[0]
+
class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
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)
+ sage: x.inner_product(y) == J.natural_inner_product(X,Y)
True
"""