From cd164e717e7224ec1af16d31152555f0c7cf49cf Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 3 Nov 2020 07:12:14 -0500 Subject: [PATCH] eja: replace the rank accessor with the full computation. --- mjo/eja/eja_algebra.py | 154 ++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 7436ed3..ff2b5d7 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -791,85 +791,21 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): True """ - return tuple( self.random_element() for idx in range(count) ) - - - def _rank_computation(self): - r""" - Compute the rank of this algebra. - - SETUP:: - - sage: from mjo.eja.eja_algebra import (HadamardEJA, - ....: JordanSpinEJA, - ....: RealSymmetricEJA, - ....: ComplexHermitianEJA, - ....: QuaternionHermitianEJA) - - EXAMPLES:: - - sage: J = HadamardEJA(4) - sage: J._rank_computation() == J.rank() - True - sage: J = JordanSpinEJA(4) - sage: J._rank_computation() == J.rank() - True - sage: J = RealSymmetricEJA(3) - sage: J._rank_computation() == J.rank() - True - sage: J = ComplexHermitianEJA(2) - sage: J._rank_computation() == J.rank() - True - sage: J = QuaternionHermitianEJA(2) - sage: J._rank_computation() == J.rank() - True - - """ - n = self.dimension() - if n == 0: - return 0 - elif n == 1: - return 1 - - var_names = [ "X" + str(z) for z in range(1,n+1) ] - R = PolynomialRing(self.base_ring(), var_names) - vars = R.gens() - - def L_x_i_j(i,j): - # From a result in my book, these are the entries of the - # basis representation of L_x. - return sum( vars[k]*self.monomial(k).operator().matrix()[i,j] - for k in range(n) ) - - L_x = matrix(R, n, n, L_x_i_j) - x_powers = [ vars[k]*(L_x**k)*self.one().to_vector() - for k in range(n) ] - - # Can assume n >= 2 - M = matrix([x_powers[0]]) - old_rank = 1 - - for d in range(1,n): - M = matrix(M.rows() + [x_powers[d]]) - M.echelonize() - new_rank = M.rank() - if new_rank == old_rank: - return new_rank - else: - old_rank = new_rank - - return n + return tuple( self.random_element() for idx in range(count) ) + @cached_method 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. + 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:: @@ -914,8 +850,80 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): 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.clear_cache() + sage: J.rank() + 3 + + :: + + sage: J = ComplexHermitianEJA(2) + sage: J.rank.clear_cache() + sage: J.rank() + 2 + + :: + + sage: J = QuaternionHermitianEJA(2) + sage: J.rank.clear_cache() + sage: J.rank() + 2 + """ - return self._rank + n = self.dimension() + if n == 0: + return 0 + elif n == 1: + return 1 + + var_names = [ "X" + str(z) for z in range(1,n+1) ] + R = PolynomialRing(self.base_ring(), var_names) + vars = R.gens() + + def L_x_i_j(i,j): + # From a result in my book, these are the entries of the + # basis representation of L_x. + return sum( vars[k]*self.monomial(k).operator().matrix()[i,j] + for k in range(n) ) + + L_x = matrix(R, n, n, L_x_i_j) + x_powers = [ vars[k]*(L_x**k)*self.one().to_vector() + for k in range(n) ] + + # Can assume n >= 2 + M = matrix([x_powers[0]]) + old_rank = 1 + + 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 + else: + old_rank = new_rank + + return n def vector_space(self): -- 2.43.2