]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
eja: add voodoo _rank_computation() method for EJAs.
[sage.d.git] / mjo / eja / eja_algebra.py
index 0a260653a7c5480acea6ffc00a9158169e4e7a18..fec8a39f00840c10ee22a493756e75abcc6da317 100644 (file)
@@ -773,128 +773,104 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
         return (J0, J5, J1)
 
 
-    def a_jordan_frame(self):
-        r"""
-        Generate a Jordan frame for this algebra.
-
-        This implementation is based on the so-called "central
-        orthogonal idempotents" implemented for (semisimple) centers
-        of SageMath ``FiniteDimensionalAlgebrasWithBasis``. Since all
-        Euclidean Jordan algebas are commutative (and thus equal to
-        their own centers) and semisimple, the method should work more
-        or less as implemented, if it ever worked in the first place.
-        (I don't know the justification for the original implementation.
-        yet).
-
-        How it works: we loop through the algebras generators, looking
-        for their eigenspaces. If there's more than one eigenspace,
-        and if they result in more than one subalgebra, then we split
-        those subalgebras recursively until we get to subalgebras of
-        dimension one (whose idempotent is the unit element). Why does
-        some generator have to produce at least two subalgebras? I
-        dunno. But it seems to work.
-
-        Beware that Koecher defines the "center" of a Jordan algebra to
-        be something else, because the usual definition is stupid in a
-        (necessarily commutative) Jordan algebra.
+    def random_elements(self, count):
+        """
+        Return ``count`` random elements as a tuple.
 
         SETUP::
 
-            sage: from mjo.eja.eja_algebra import (random_eja,
-            ....:                                  JordanSpinEJA,
-            ....:                                  TrivialEJA)
-
-        EXAMPLES:
-
-        A Jordan frame for the trivial algebra has to be empty
-        (zero-length) since its rank is zero. More to the point, there
-        are no non-zero idempotents in the trivial EJA. This does not
-        cause any problems so long as we adopt the convention that the
-        empty sum is zero, since then the sole element of the trivial
-        EJA has an (empty) spectral decomposition::
-
-            sage: J = TrivialEJA()
-            sage: J.a_jordan_frame()
-            ()
-
-        A one-dimensional algebra has rank one (equal to its dimension),
-        and only one primitive idempotent, namely the algebra's unit
-        element::
-
-            sage: J = JordanSpinEJA(1)
-            sage: J.a_jordan_frame()
-            (e0,)
+            sage: from mjo.eja.eja_algebra import JordanSpinEJA
 
-        TESTS::
+        EXAMPLES::
 
-            sage: J = random_eja()
-            sage: c = J.a_jordan_frame()
-            sage: all( x^2 == x for x in c )
+            sage: J = JordanSpinEJA(3)
+            sage: x,y,z = J.random_elements(3)
+            sage: all( [ x in J, y in J, z in J ])
             True
-            sage: r = len(c)
-            sage: all( c[i]*c[j] == c[i]*(i==j) for i in range(r)
-            ....:                               for j in range(r) )
+            sage: len( J.random_elements(10) ) == 10
             True
 
         """
-        if self.dimension() == 0:
-            return ()
-        if self.dimension() == 1:
-            return (self.one(),)
-
-        for g in self.gens():
-            eigenpairs = g.operator().matrix().right_eigenspaces()
-            if len(eigenpairs) >= 2:
-                subalgebras = []
-                for eigval, eigspace in eigenpairs:
-                    # Make sub-EJAs from the matrix eigenspaces...
-                    sb = tuple( self.from_vector(b) for b in eigspace.basis() )
-                    try:
-                        # This will fail if e.g. the eigenspace basis
-                        # contains two elements and their product
-                        # isn't a linear combination of the two of
-                        # them (i.e. the generated EJA isn't actually
-                        # two dimensional).
-                        s = FiniteDimensionalEuclideanJordanSubalgebra(self, sb)
-                        subalgebras.append(s)
-                    except ArithmeticError as e:
-                        if str(e) == "vector is not in free module":
-                            # Ignore only the "not a sub-EJA" error
-                            pass
-
-                if len(subalgebras) >= 2:
-                    # apply this method recursively.
-                    return tuple( c.superalgebra_element()
-                                  for subalgebra in subalgebras
-                                  for c in subalgebra.a_jordan_frame() )
-
-        # If we got here, the algebra didn't decompose, at least not when we looked at
-        # the eigenspaces corresponding only to basis elements of the algebra. The
-        # implementation I stole says that this should work because of Schur's Lemma,
-        # so I personally blame Schur's Lemma if it does not.
-        raise Exception("Schur's Lemma didn't work!")
+        return  tuple( self.random_element() for idx in range(count) )
 
 
-    def random_elements(self, count):
-        """
-        Return ``count`` random elements as a tuple.
+    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.
 
         SETUP::
 
-            sage: from mjo.eja.eja_algebra import JordanSpinEJA
+            sage: from mjo.eja.eja_algebra import (HadamardEJA,
+            ....:                                  JordanSpinEJA,
+            ....:                                  RealSymmetricEJA,
+            ....:                                  ComplexHermitianEJA,
+            ....:                                  QuaternionHermitianEJA)
 
         EXAMPLES::
 
-            sage: J = JordanSpinEJA(3)
-            sage: x,y,z = J.random_elements(3)
-            sage: all( [ x in J, y in J, z in J ])
+            sage: J = HadamardEJA(5)
+            sage: J._rank_computation() == J.rank()
             True
-            sage: len( J.random_elements(10) ) == 10
+            sage: J = JordanSpinEJA(5)
+            sage: J._rank_computation() == J.rank()
+            True
+            sage: J = RealSymmetricEJA(4)
+            sage: J._rank_computation() == J.rank()
+            True
+            sage: J = ComplexHermitianEJA(3)
+            sage: J._rank_computation() == J.rank()
+            True
+            sage: J = QuaternionHermitianEJA(2)
+            sage: J._rank_computation() == J.rank()
             True
 
         """
-        return  tuple( self.random_element() for idx in range(count) )
-
+        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(self):
         """