def __init__(self,
field,
mult_table,
- rank,
prefix='e',
category=None,
natural_basis=None,
# a real embedding.
raise ValueError('field is not real')
- self._rank = rank
self._natural_basis = natural_basis
if category is None:
True
"""
- return tuple( self.random_element() for idx in range(count) )
+ return tuple( self.random_element() for idx in range(count) )
+ @cached_method
+ def rank(self):
+ """
+ Return the rank of this EJA.
- def _rank_computation(self):
- r"""
- Compute the rank of this algebra.
+ ALGORITHM:
+
+ We first compute the polynomial "column matrices" `p_{k}` that
+ evaluate to `x^k` on the coordinates of `x`. Then, we begin
+ adding them to a matrix one at a time, and trying to solve the
+ system that makes `p_{0}`,`p_{1}`,..., `p_{s-1}` add up to
+ `p_{s}`. This will succeed only when `s` is the rank of the
+ algebra, as proven in a recent draft paper of mine.
SETUP::
....: JordanSpinEJA,
....: RealSymmetricEJA,
....: ComplexHermitianEJA,
- ....: QuaternionHermitianEJA)
+ ....: QuaternionHermitianEJA,
+ ....: random_eja)
- EXAMPLES::
+ EXAMPLES:
- sage: J = HadamardEJA(4)
- sage: J._rank_computation() == J.rank()
+ The rank of the Jordan spin algebra is always two::
+
+ sage: JordanSpinEJA(2).rank()
+ 2
+ sage: JordanSpinEJA(3).rank()
+ 2
+ sage: JordanSpinEJA(4).rank()
+ 2
+
+ The rank of the `n`-by-`n` Hermitian real, complex, or
+ quaternion matrices is `n`::
+
+ sage: RealSymmetricEJA(4).rank()
+ 4
+ sage: ComplexHermitianEJA(3).rank()
+ 3
+ sage: QuaternionHermitianEJA(2).rank()
+ 2
+
+ TESTS:
+
+ Ensure that every EJA that we know how to construct has a
+ positive integer rank, unless the algebra is trivial in
+ which case its rank will be zero::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: r = J.rank()
+ sage: r in ZZ
True
- sage: J = JordanSpinEJA(4)
- sage: J._rank_computation() == J.rank()
+ sage: r > 0 or (r == 0 and J.is_trivial())
True
+
+ Ensure that computing the rank actually works, since the ranks
+ of all simple algebras are known and will be cached by default::
+
+ sage: J = HadamardEJA(4)
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 4
+
+ ::
+
+ sage: J = JordanSpinEJA(4)
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 2
+
+ ::
+
sage: J = RealSymmetricEJA(3)
- sage: J._rank_computation() == J.rank()
- True
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 3
+
+ ::
+
sage: J = ComplexHermitianEJA(2)
- sage: J._rank_computation() == J.rank()
- True
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 2
+
+ ::
+
sage: J = QuaternionHermitianEJA(2)
- sage: J._rank_computation() == J.rank()
- True
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 2
"""
n = self.dimension()
for d in range(1,n):
M = matrix(M.rows() + [x_powers[d]])
M.echelonize()
+ # TODO: we've basically solved the system here.
+ # We should save the echelonized matrix somehow
+ # so that it can be reused in the charpoly method.
new_rank = M.rank()
if new_rank == old_rank:
return new_rank
return n
- def rank(self):
- """
- Return the rank of this EJA.
-
- ALGORITHM:
-
- The author knows of no algorithm to compute the rank of an EJA
- where only the multiplication table is known. In lieu of one, we
- require the rank to be specified when the algebra is created,
- and simply pass along that number here.
-
- SETUP::
-
- sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
- ....: RealSymmetricEJA,
- ....: ComplexHermitianEJA,
- ....: QuaternionHermitianEJA,
- ....: random_eja)
-
- EXAMPLES:
-
- The rank of the Jordan spin algebra is always two::
-
- sage: JordanSpinEJA(2).rank()
- 2
- sage: JordanSpinEJA(3).rank()
- 2
- sage: JordanSpinEJA(4).rank()
- 2
-
- The rank of the `n`-by-`n` Hermitian real, complex, or
- quaternion matrices is `n`::
-
- sage: RealSymmetricEJA(4).rank()
- 4
- sage: ComplexHermitianEJA(3).rank()
- 3
- sage: QuaternionHermitianEJA(2).rank()
- 2
-
- TESTS:
-
- Ensure that every EJA that we know how to construct has a
- positive integer rank, unless the algebra is trivial in
- which case its rank will be zero::
-
- sage: set_random_seed()
- sage: J = random_eja()
- sage: r = J.rank()
- sage: r in ZZ
- True
- sage: r > 0 or (r == 0 and J.is_trivial())
- True
-
- """
- return self._rank
-
def vector_space(self):
"""
for i in range(n) ]
fdeja = super(HadamardEJA, self)
- return fdeja.__init__(field, mult_table, rank=n, **kwargs)
+ fdeja.__init__(field, mult_table, **kwargs)
+ self.rank.set_cache(n)
def inner_product(self, x, y):
"""
# field can have dimension 4 (quaternions) too.
return 2
- def __init__(self, field, basis, rank, normalize_basis=True, **kwargs):
+ def __init__(self, field, basis, normalize_basis=True, **kwargs):
"""
Compared to the superclass constructor, we take a basis instead of
a multiplication table because the latter can be computed in terms
# time to ensure that it isn't a generator expression.
basis = tuple(basis)
- if rank > 1 and normalize_basis:
+ if len(basis) > 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.
Qs = self.multiplication_table_from_matrix_basis(basis)
fdeja = super(MatrixEuclideanJordanAlgebra, self)
- return fdeja.__init__(field,
- Qs,
- rank=rank,
- natural_basis=basis,
- **kwargs)
+ fdeja.__init__(field, Qs, natural_basis=basis, **kwargs)
+ return
- def _rank_computation(self):
+ @cached_method
+ def rank(self):
r"""
Override the parent method with something that tries to compute
over a faster (non-extension) field.
if self._basis_normalizers is None:
# We didn't normalize, so assume that the basis we started
# with had entries in a nice field.
- return super(MatrixEuclideanJordanAlgebra, self)._rank_computation()
+ return super(MatrixEuclideanJordanAlgebra, self).rank()
else:
basis = ( (b/n) for (b,n) in zip(self.natural_basis(),
self._basis_normalizers) )
# integers.
J = MatrixEuclideanJordanAlgebra(QQ,
basis,
- self.rank(),
normalize_basis=False)
- return J._rank_computation()
+ return J.rank()
@cached_method
def _charpoly_coeff(self, i):
# Do this over the rationals and convert back at the end.
J = MatrixEuclideanJordanAlgebra(QQ,
basis,
- self.rank(),
normalize_basis=False)
(_,x,_,_) = J._charpoly_matrix_system()
p = J._charpoly_coeff(i)
def __init__(self, n, field=AA, **kwargs):
basis = self._denormalized_basis(n, field)
- super(RealSymmetricEJA, self).__init__(field, basis, n, **kwargs)
+ super(RealSymmetricEJA, self).__init__(field, basis, **kwargs)
+ self.rank.set_cache(n)
class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
def __init__(self, n, field=AA, **kwargs):
basis = self._denormalized_basis(n,field)
- super(ComplexHermitianEJA,self).__init__(field, basis, n, **kwargs)
+ super(ComplexHermitianEJA,self).__init__(field, basis, **kwargs)
+ self.rank.set_cache(n)
class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
def __init__(self, n, field=AA, **kwargs):
basis = self._denormalized_basis(n,field)
- super(QuaternionHermitianEJA,self).__init__(field, basis, n, **kwargs)
+ super(QuaternionHermitianEJA,self).__init__(field, basis, **kwargs)
+ self.rank.set_cache(n)
class BilinearFormEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA):
# one-dimensional ambient space (because the rank is bounded
# by the ambient dimension).
fdeja = super(BilinearFormEJA, self)
- return fdeja.__init__(field, mult_table, rank=min(n,2), **kwargs)
+ fdeja.__init__(field, mult_table, **kwargs)
+ self.rank.set_cache(min(n,2))
def inner_product(self, x, y):
r"""
fdeja = super(TrivialEJA, self)
# The rank is zero using my definition, namely the dimension of the
# largest subalgebra generated by any element.
- return fdeja.__init__(field, mult_table, rank=0, **kwargs)
+ fdeja.__init__(field, mult_table, **kwargs)
+ self.rank.set_cache(0)