]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
eja: replace the rank accessor with the full computation.
authorMichael Orlitzky <michael@orlitzky.com>
Tue, 3 Nov 2020 12:12:14 +0000 (07:12 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Tue, 3 Nov 2020 12:12:14 +0000 (07:12 -0500)
mjo/eja/eja_algebra.py

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