]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
eja: use now somewhat-proven and fast rank computation.
authorMichael Orlitzky <michael@orlitzky.com>
Sun, 1 Nov 2020 00:19:42 +0000 (20:19 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Sun, 1 Nov 2020 00:19:42 +0000 (20:19 -0400)
mjo/eja/eja_algebra.py

index 34fc0c3bc7b1dfe250fd5f7ea5de4fbbc52c66e0..7436ed36ce676dd9bbf0eda9922ac2aecacc6e6e 100644 (file)
@@ -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):
         """