assume_associative=False,
category=None,
rank=None,
- natural_basis=None,
- inner_product=None):
+ natural_basis=None):
n = len(mult_table)
mult_table = [b.base_extend(field) for b in mult_table]
for b in mult_table:
names=names,
category=cat,
rank=rank,
- natural_basis=natural_basis,
- inner_product=inner_product)
+ natural_basis=natural_basis)
def __init__(self,
assume_associative=False,
category=None,
rank=None,
- natural_basis=None,
- inner_product=None):
+ natural_basis=None):
"""
EXAMPLES:
"""
self._rank = rank
self._natural_basis = natural_basis
- self._inner_product = inner_product
fda = super(FiniteDimensionalEuclideanJordanAlgebra, self)
fda.__init__(field,
mult_table,
"""
The inner product associated with this Euclidean Jordan algebra.
- Will default to the trace inner product if nothing else.
+ Defaults to the trace inner product, but can be overridden by
+ subclasses if they are sure that the necessary properties are
+ satisfied.
EXAMPLES:
"""
if (not x in self) or (not y in self):
raise TypeError("arguments must live in this algebra")
- if self._inner_product is None:
- return x.trace_inner_product(y)
- else:
- return self._inner_product(x,y)
+ return x.trace_inner_product(y)
def natural_basis(self):
EXAMPLES::
- sage: J = RealSymmetricSimpleEJA(2)
+ sage: J = RealSymmetricEJA(2)
sage: J.basis()
Family (e0, e1, e2)
sage: J.natural_basis()
::
- sage: J = JordanSpinAlgebra(2)
+ sage: J = JordanSpinEJA(2)
sage: J.basis()
Family (e0, e1)
sage: J.natural_basis()
The identity in `S^n` is converted to the identity in the EJA::
- sage: J = RealSymmetricSimpleEJA(3)
+ sage: J = RealSymmetricEJA(3)
sage: I = identity_matrix(QQ,3)
sage: J(I) == J.one()
True
This skew-symmetric matrix can't be represented in the EJA::
- sage: J = RealSymmetricSimpleEJA(3)
+ sage: J = RealSymmetricEJA(3)
sage: A = matrix(QQ,3, lambda i,j: i-j)
sage: J(A)
Traceback (most recent call last):
inner product on `R^n` (this example only works because the
basis for the Jordan algebra is the standard basis in `R^n`)::
- sage: J = JordanSpinAlgebra(3)
+ sage: J = JordanSpinEJA(3)
sage: x = vector(QQ,[1,2,3])
sage: y = vector(QQ,[4,5,6])
sage: x.inner_product(y)
so the inner product of the identity matrix with itself
should be the `n`::
- sage: J = RealSymmetricSimpleEJA(3)
+ sage: J = RealSymmetricEJA(3)
sage: J.one().inner_product(J.one())
3
part because the product of Hermitian matrices may not be
Hermitian::
- sage: J = ComplexHermitianSimpleEJA(3)
+ sage: J = ComplexHermitianEJA(3)
sage: J.one().inner_product(J.one())
3
Ditto for the quaternions::
- sage: J = QuaternionHermitianSimpleEJA(3)
+ sage: J = QuaternionHermitianEJA(3)
sage: J.one().inner_product(J.one())
3
EXAMPLES::
- sage: J = JordanSpinAlgebra(2)
+ sage: J = JordanSpinEJA(2)
sage: e0,e1 = J.gens()
sage: x = e0 + e1
sage: x.det()
0
- sage: J = JordanSpinAlgebra(3)
+ sage: J = JordanSpinEJA(3)
sage: e0,e1,e2 = J.gens()
sage: x = e0 + e1 + e2
sage: x.det()
sage: set_random_seed()
sage: n = ZZ.random_element(1,10)
- sage: J = JordanSpinAlgebra(n)
+ sage: J = JordanSpinEJA(n)
sage: x = J.random_element()
sage: while x.is_zero():
....: x = J.random_element()
We can't use the superclass method because it relies on
the algebra being associative.
+
+ ALGORITHM:
+
+ The usual way to do this is to check if the determinant is
+ zero, but we need the characteristic polynomial for the
+ determinant. The minimal polynomial is a lot easier to get,
+ so we use Corollary 2 in Chapter V of Koecher to check
+ whether or not the paren't algebra's zero element is a root
+ of this element's minimal polynomial.
+
+ TESTS:
+
+ The identity element is always invertible::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: J.one().is_invertible()
+ True
+
+ The zero element is never invertible::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: J.zero().is_invertible()
+ False
+
"""
- return not self.det().is_zero()
+ zero = self.parent().zero()
+ p = self.minimal_polynomial()
+ return not (p(zero) == zero)
def is_nilpotent(self):
The identity element always has degree one, but any element
linearly-independent from it is regular::
- sage: J = JordanSpinAlgebra(5)
+ sage: J = JordanSpinEJA(5)
sage: J.one().is_regular()
False
sage: e0, e1, e2, e3, e4 = J.gens() # e0 is the identity
EXAMPLES::
- sage: J = JordanSpinAlgebra(4)
+ sage: J = JordanSpinEJA(4)
sage: J.one().degree()
1
sage: e0,e1,e2,e3 = J.gens()
sage: set_random_seed()
sage: n = ZZ.random_element(1,10)
- sage: J = JordanSpinAlgebra(n)
+ sage: J = JordanSpinEJA(n)
sage: x = J.random_element()
sage: x == x.coefficient(0)*J.one() or x.degree() == 2
True
def minimal_polynomial(self):
"""
+ ALGORITHM:
+
+ We restrict ourselves to the associative subalgebra
+ generated by this element, and then return the minimal
+ polynomial of this element's operator matrix (in that
+ subalgebra). This works by Baes Proposition 2.3.16.
+
EXAMPLES::
sage: set_random_seed()
sage: set_random_seed()
sage: n = ZZ.random_element(2,10)
- sage: J = JordanSpinAlgebra(n)
+ sage: J = JordanSpinEJA(n)
sage: y = J.random_element()
sage: while y == y.coefficient(0)*J.one():
....: y = J.random_element()
True
"""
- # The element we're going to call "minimal_polynomial()" on.
- # Either myself, interpreted as an element of a finite-
- # dimensional algebra, or an element of an associative
- # subalgebra.
- elt = None
-
- if self.parent().is_associative():
- elt = FiniteDimensionalAlgebraElement(self.parent(), self)
- else:
- V = self.span_of_powers()
- assoc_subalg = self.subalgebra_generated_by()
- # Mis-design warning: the basis used for span_of_powers()
- # and subalgebra_generated_by() must be the same, and in
- # the same order!
- elt = assoc_subalg(V.coordinates(self.vector()))
-
- # Recursive call, but should work since elt lives in an
- # associative algebra.
- return elt.minimal_polynomial()
+ V = self.span_of_powers()
+ assoc_subalg = self.subalgebra_generated_by()
+ # Mis-design warning: the basis used for span_of_powers()
+ # and subalgebra_generated_by() must be the same, and in
+ # the same order!
+ elt = assoc_subalg(V.coordinates(self.vector()))
+ return elt.operator_matrix().minimal_polynomial()
def natural_representation(self):
EXAMPLES::
- sage: J = ComplexHermitianSimpleEJA(3)
+ sage: J = ComplexHermitianEJA(3)
sage: J.one()
e0 + e5 + e8
sage: J.one().natural_representation()
::
- sage: J = QuaternionHermitianSimpleEJA(3)
+ sage: J = QuaternionHermitianEJA(3)
sage: J.one()
e0 + e9 + e14
sage: J.one().natural_representation()
sage: set_random_seed()
sage: n = ZZ.random_element(1,10)
- sage: J = JordanSpinAlgebra(n)
+ sage: J = JordanSpinEJA(n)
sage: x = J.random_element()
sage: x_vec = x.vector()
sage: x0 = x_vec[0]
# The dimension of the subalgebra can't be greater than
# the big algebra, so just put everything into a list
# and let span() get rid of the excess.
- V = self.vector().parent()
+ #
+ # We do the extra ambient_vector_space() in case we're messing
+ # with polynomials and the direct parent is a module.
+ V = self.vector().parent().ambient_vector_space()
return V.span( (self**d).vector() for d in xrange(V.dimension()) )
TESTS::
sage: set_random_seed()
- sage: J = eja_rn(5)
+ sage: J = RealCartesianProductEJA(5)
sage: c = J.random_element().subalgebra_idempotent()
sage: c^2 == c
True
- sage: J = JordanSpinAlgebra(5)
+ sage: J = JordanSpinEJA(5)
sage: c = J.random_element().subalgebra_idempotent()
sage: c^2 == c
True
EXAMPLES::
- sage: J = JordanSpinAlgebra(3)
+ sage: J = JordanSpinEJA(3)
sage: e0,e1,e2 = J.gens()
sage: x = e0 + e1 + e2
sage: x.trace()
return (self*other).trace()
-def eja_rn(dimension, field=QQ):
+class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
Return the Euclidean Jordan Algebra corresponding to the set
`R^n` under the Hadamard product.
+ Note: this is nothing more than the Cartesian product of ``n``
+ copies of the spin algebra. Once Cartesian product algebras
+ are implemented, this can go.
+
EXAMPLES:
This multiplication table can be verified by hand::
- sage: J = eja_rn(3)
+ sage: J = RealCartesianProductEJA(3)
sage: e0,e1,e2 = J.gens()
sage: e0*e0
e0
e2
"""
- # The FiniteDimensionalAlgebra constructor takes a list of
- # matrices, the ith representing right multiplication by the ith
- # basis element in the vector space. So if e_1 = (1,0,0), then
- # right (Hadamard) multiplication of x by e_1 picks out the first
- # component of x; and likewise for the ith basis element e_i.
- Qs = [ matrix(field, dimension, dimension, lambda k,j: 1*(k == j == i))
- for i in xrange(dimension) ]
+ @staticmethod
+ def __classcall_private__(cls, n, field=QQ):
+ # The FiniteDimensionalAlgebra constructor takes a list of
+ # matrices, the ith representing right multiplication by the ith
+ # basis element in the vector space. So if e_1 = (1,0,0), then
+ # right (Hadamard) multiplication of x by e_1 picks out the first
+ # component of x; and likewise for the ith basis element e_i.
+ Qs = [ matrix(field, n, n, lambda k,j: 1*(k == j == i))
+ for i in xrange(n) ]
- return FiniteDimensionalEuclideanJordanAlgebra(field,
- Qs,
- rank=dimension,
- inner_product=_usual_ip)
+ fdeja = super(RealCartesianProductEJA, cls)
+ return fdeja.__classcall_private__(cls, field, Qs, rank=n)
+ def inner_product(self, x, y):
+ return _usual_ip(x,y)
def random_eja():
"""
n = ZZ.random_element(1,5)
- constructor = choice([eja_rn,
- JordanSpinAlgebra,
- RealSymmetricSimpleEJA,
- ComplexHermitianSimpleEJA,
- QuaternionHermitianSimpleEJA])
+ constructor = choice([RealCartesianProductEJA,
+ JordanSpinEJA,
+ RealSymmetricEJA,
+ ComplexHermitianEJA,
+ QuaternionHermitianEJA])
return constructor(n, field=QQ)
return (X_mat*Y_mat).trace()
-def RealSymmetricSimpleEJA(n, field=QQ):
+class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
The rank-n simple EJA consisting of real symmetric n-by-n
matrices, the usual symmetric Jordan product, and the trace inner
EXAMPLES::
- sage: J = RealSymmetricSimpleEJA(2)
+ sage: J = RealSymmetricEJA(2)
sage: e0, e1, e2 = J.gens()
sage: e0*e0
e0
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: J = RealSymmetricSimpleEJA(n)
+ sage: J = RealSymmetricEJA(n)
sage: J.degree() == (n^2 + n)/2
True
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: J = RealSymmetricSimpleEJA(n)
+ sage: J = RealSymmetricEJA(n)
sage: x = J.random_element()
sage: y = J.random_element()
sage: actual = (x*y).natural_representation()
True
"""
- S = _real_symmetric_basis(n, field=field)
- (Qs, T) = _multiplication_table_from_matrix_basis(S)
+ @staticmethod
+ def __classcall_private__(cls, n, field=QQ):
+ S = _real_symmetric_basis(n, field=field)
+ (Qs, T) = _multiplication_table_from_matrix_basis(S)
- return FiniteDimensionalEuclideanJordanAlgebra(field,
- Qs,
- rank=n,
- natural_basis=T,
- inner_product=_matrix_ip)
+ fdeja = super(RealSymmetricEJA, cls)
+ return fdeja.__classcall_private__(cls,
+ field,
+ Qs,
+ rank=n,
+ natural_basis=T)
+ def inner_product(self, x, y):
+ return _matrix_ip(x,y)
-def ComplexHermitianSimpleEJA(n, field=QQ):
+
+class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
The rank-n simple EJA consisting of complex Hermitian n-by-n
matrices over the real numbers, the usual symmetric Jordan product,
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: J = ComplexHermitianSimpleEJA(n)
+ sage: J = ComplexHermitianEJA(n)
sage: J.degree() == n^2
True
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: J = ComplexHermitianSimpleEJA(n)
+ sage: J = ComplexHermitianEJA(n)
sage: x = J.random_element()
sage: y = J.random_element()
sage: actual = (x*y).natural_representation()
True
"""
- S = _complex_hermitian_basis(n)
- (Qs, T) = _multiplication_table_from_matrix_basis(S)
+ @staticmethod
+ def __classcall_private__(cls, n, field=QQ):
+ S = _complex_hermitian_basis(n)
+ (Qs, T) = _multiplication_table_from_matrix_basis(S)
- # 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.
- ip = lambda X,Y: _matrix_ip(X,Y)/2
+ fdeja = super(ComplexHermitianEJA, cls)
+ return fdeja.__classcall_private__(cls,
+ field,
+ Qs,
+ rank=n,
+ natural_basis=T)
- return FiniteDimensionalEuclideanJordanAlgebra(field,
- Qs,
- rank=n,
- natural_basis=T,
- inner_product=ip)
+ 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
-def QuaternionHermitianSimpleEJA(n, field=QQ):
+class QuaternionHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
The rank-n simple EJA consisting of self-adjoint n-by-n quaternion
matrices, the usual symmetric Jordan product, and the
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: J = QuaternionHermitianSimpleEJA(n)
+ sage: J = QuaternionHermitianEJA(n)
sage: J.degree() == 2*(n^2) - n
True
sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
- sage: J = QuaternionHermitianSimpleEJA(n)
+ sage: J = QuaternionHermitianEJA(n)
sage: x = J.random_element()
sage: y = J.random_element()
sage: actual = (x*y).natural_representation()
True
"""
- S = _quaternion_hermitian_basis(n)
- (Qs, T) = _multiplication_table_from_matrix_basis(S)
-
- # 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.
- ip = lambda X,Y: _matrix_ip(X,Y)/4
-
- return FiniteDimensionalEuclideanJordanAlgebra(field,
- Qs,
- rank=n,
- natural_basis=T,
- inner_product=ip)
+ @staticmethod
+ def __classcall_private__(cls, n, field=QQ):
+ S = _quaternion_hermitian_basis(n)
+ (Qs, T) = _multiplication_table_from_matrix_basis(S)
+ fdeja = super(QuaternionHermitianEJA, cls)
+ return fdeja.__classcall_private__(cls,
+ field,
+ Qs,
+ rank=n,
+ natural_basis=T)
-def OctonionHermitianSimpleEJA(n):
- """
- This shit be crazy. It has dimension 27 over the reals.
- """
- n = 3
- pass
-
-class JordanSpinAlgebra(FiniteDimensionalEuclideanJordanAlgebra):
+ 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
+
+
+class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
"""
The rank-2 simple EJA consisting of real vectors ``x=(x0, x_bar)``
with the usual inner product and jordan product ``x*y =
This multiplication table can be verified by hand::
- sage: J = JordanSpinAlgebra(4)
+ sage: J = JordanSpinEJA(4)
sage: e0,e1,e2,e3 = J.gens()
sage: e0*e0
e0
Qi[0,0] = Qi[0,0] * ~field(2)
Qs.append(Qi)
- fdeja = super(JordanSpinAlgebra, cls)
- return fdeja.__classcall_private__(cls, field, Qs)
-
- def rank(self):
- """
- Return the rank of this Jordan Spin Algebra.
-
- The rank of the spin algebra is two, unless we're in a
- one-dimensional ambient space (because the rank is bounded by
- the ambient dimension).
- """
- return min(self.dimension(),2)
+ # The rank of the spin algebra is two, unless we're in a
+ # one-dimensional ambient space (because the rank is bounded by
+ # the ambient dimension).
+ fdeja = super(JordanSpinEJA, cls)
+ return fdeja.__classcall_private__(cls, field, Qs, rank=min(n,2))
def inner_product(self, x, y):
return _usual_ip(x,y)