From 5ec7a343dda81259a73a04a6e4c2073c77ec7938 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 31 Oct 2020 20:19:42 -0400 Subject: [PATCH] eja: use now somewhat-proven and fast rank computation. --- mjo/eja/eja_algebra.py | 118 +++++++++++------------------------------ 1 file changed, 32 insertions(+), 86 deletions(-) diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 34fc0c3..7436ed3 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -794,30 +794,9 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): return tuple( self.random_element() for idx in range(count) ) - def _rank_computation1(self): + def _rank_computation(self): r""" - Compute the rank of this algebra using highly suspicious voodoo. - - ALGORITHM: - - We first compute the basis representation of the operator L_x - using polynomial indeterminates are placeholders for the - coordinates of "x", which is arbitrary. We then use that - matrix to compute the (polynomial) entries of x^0, x^1, ..., - x^d,... for increasing values of "d", starting at zero. The - idea is that. If we also add "coefficient variables" a_0, - a_1,... to the ring, we can form the linear combination - a_0*x^0 + ... + a_d*x^d = 0, and ask what dimension the - solution space has as an affine variety. When "d" is smaller - than the rank, we expect that dimension to be the number of - coordinates of "x", since we can set *those* to whatever we - want, but linear independence forces the coefficients a_i to - be zero. Eventually, when "d" passes the rank, the dimension - of the solution space begins to grow, because we can *still* - set the coordinates of "x" arbitrarily, but now there are some - coefficients that make the sum zero as well. So, when the - dimension of the variety jumps, we return the corresponding - "d" as the rank of the algebra. This appears to work. + Compute the rank of this algebra. SETUP:: @@ -829,77 +808,22 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): EXAMPLES:: - sage: J = HadamardEJA(5) + sage: J = HadamardEJA(4) sage: J._rank_computation() == J.rank() True - sage: J = JordanSpinEJA(5) + sage: J = JordanSpinEJA(4) sage: J._rank_computation() == J.rank() True - sage: J = RealSymmetricEJA(4) + sage: J = RealSymmetricEJA(3) sage: J._rank_computation() == J.rank() True - sage: J = ComplexHermitianEJA(3) + 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() - var_names = [ "X" + str(z) for z in range(1,n+1) ] - d = 0 - ideal_dim = len(var_names) - 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[d+k]*self.monomial(k).operator().matrix()[i,j] - for k in range(n) ) - - while ideal_dim == len(var_names): - coeff_names = [ "a" + str(z) for z in range(d) ] - R = PolynomialRing(self.base_ring(), coeff_names + var_names) - vars = R.gens() - 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(d) ] - eqs = [ sum(x_powers[k][j] for k in range(d)) for j in range(n) ] - ideal_dim = R.ideal(eqs).dimension() - d += 1 - - # Subtract one because we increment one too many times, and - # subtract another one because "d" is one greater than the - # answer anyway; when d=3, we go up to x^2. - return d-2 - - def _rank_computation2(self): - r""" - Instead of using the dimension of an ideal, find the rank of a - matrix containing polynomials. - """ - n = self.dimension() - 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()).row() - for k in range(n) ] - - from sage.matrix.constructor import block_matrix - M = block_matrix(n,1,x_powers) - return M.rank() - - def _rank_computation3(self): - r""" - Similar to :meth:`_rank_computation2`, but it stops echelonizing - as soon as it hits the first zero row. """ n = self.dimension() if n == 0: @@ -922,13 +846,11 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): for k in range(n) ] # Can assume n >= 2 - rows = [x_powers[0]] - M = matrix(rows) + M = matrix([x_powers[0]]) old_rank = 1 for d in range(1,n): - rows = M.rows() + [x_powers[d]] - M = matrix(rows) + M = matrix(M.rows() + [x_powers[d]]) M.echelonize() new_rank = M.rank() if new_rank == old_rank: @@ -936,6 +858,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): else: old_rank = new_rank + return n + def rank(self): """ Return the rank of this EJA. @@ -1211,6 +1135,28 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): **kwargs) + def _rank_computation(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() + else: + basis = ( (b/n) for (b,n) in zip(self.natural_basis(), + self._basis_normalizers) ) + + # Do this over the rationals and convert back at the end. + # Only works because we know the entries of the basis are + # integers. + J = MatrixEuclideanJordanAlgebra(QQ, + basis, + self.rank(), + normalize_basis=False) + return J._rank_computation() + @cached_method def _charpoly_coeff(self, i): """ -- 2.44.2