]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/euclidean_jordan_algebra.py
eja: add new operator() method for elements that returns a morphism.
[sage.d.git] / mjo / eja / euclidean_jordan_algebra.py
index da3f6001e2d3878f6c5311eb309f8b2a22676a00..72e167e6811912c3b19a71b27432d052800d3051 100644 (file)
@@ -11,6 +11,77 @@ from sage.structure.category_object import normalize_names
 
 from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra
 from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement
+from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_morphism import FiniteDimensionalAlgebraMorphism
+
+
+class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMorphism):
+    """
+    A very thin wrapper around FiniteDimensionalAlgebraMorphism that
+    does only two things:
+
+      1. Avoids the ``unitary`` and ``check`` arguments to the constructor
+         that will always be ``False``. This is necessary because these
+         are homomorphisms with respect to ADDITION, but the SageMath
+         machinery wants to check that they're homomorphisms with respect
+         to (Jordan) MULTIPLICATION. That obviously doesn't work.
+
+      2. Inputs and outputs the underlying matrix with respect to COLUMN
+         vectors, unlike the parent class.
+
+    If this seems a bit heavyweight, it is. I would have been happy to
+    use a the ring morphism that underlies the finite-dimensional
+    algebra morphism, but they don't seem to be callable on elements of
+    our EJA.
+    """
+    def __init__(self, parent, f):
+        FiniteDimensionalAlgebraMorphism.__init__(self,
+                                                  parent,
+                                                  f.transpose(),
+                                                  unitary=False,
+                                                  check=False)
+
+
+    def _repr_(self):
+        """
+        We override only the representation that is shown to the user,
+        because we want the matrix to be with respect to COLUMN vectors.
+
+        TESTS:
+
+        Ensure that we see the transpose of the underlying matrix object:
+
+            sage: J = RealSymmetricEJA(3)
+            sage: x = J.linear_combination(zip(range(len(J.gens())), J.gens()))
+            sage: L = x.operator()
+            sage: L
+            Morphism from Euclidean Jordan algebra of degree 6 over Rational
+            Field to Euclidean Jordan algebra of degree 6 over Rational Field
+            given by matrix
+            [  0   1   2   0   0   0]
+            [1/2 3/2   2 1/2   1   0]
+            [  1   2 5/2   0 1/2   1]
+            [  0   1   0   3   4   0]
+            [  0   1 1/2   2   4   2]
+            [  0   0   2   0   4   5]
+            sage: L._matrix
+            [  0 1/2   1   0   0   0]
+            [  1 3/2   2   1   1   0]
+            [  2   2 5/2   0 1/2   2]
+            [  0 1/2   0   3   2   0]
+            [  0   1 1/2   4   4   4]
+            [  0   0   1   0   2   5]
+
+        """
+        return "Morphism from {} to {} given by matrix\n{}".format(
+            self.domain(), self.codomain(), self.matrix())
+
+    def matrix(self):
+        """
+        Return the matrix of this morphism with respect to a left-action
+        on column vectors.
+        """
+        return FiniteDimensionalAlgebraMorphism.matrix(self).transpose()
+
 
 class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
     @staticmethod
@@ -340,6 +411,16 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         An element of a Euclidean Jordan algebra.
         """
 
+        def __dir__(self):
+            """
+            Oh man, I should not be doing this. This hides the "disabled"
+            methods ``left_matrix`` and ``matrix`` from introspection;
+            in particular it removes them from tab-completion.
+            """
+            return filter(lambda s: s not in ['left_matrix', 'matrix'],
+                          dir(self.__class__) )
+
+
         def __init__(self, A, elt=None):
             """
             EXAMPLES:
@@ -658,8 +739,10 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             """
             Return the Jordan-multiplicative inverse of this element.
 
-            We can't use the superclass method because it relies on the
-            algebra being associative.
+            ALGORITHM:
+
+            We appeal to the quadratic representation as in Koecher's
+            Theorem 12 in Chapter III, Section 5.
 
             EXAMPLES:
 
@@ -698,34 +781,28 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                 sage: (not x.is_invertible()) or (x.inverse()*x == J.one())
                 True
 
-            """
-            if not self.is_invertible():
-                raise ValueError("element not invertible")
+            The inverse of the inverse is what we started with::
 
-            if self.parent().is_associative():
-                elt = FiniteDimensionalAlgebraElement(self.parent(), self)
-                # elt is in the right coordinates, but has the wrong class.
-                return self.parent()(elt.inverse().vector())
+                sage: set_random_seed()
+                sage: J = random_eja()
+                sage: x = J.random_element()
+                sage: (not x.is_invertible()) or (x.inverse().inverse() == x)
+                True
 
-            # We do this a little different than the usual recursive
-            # call to a finite-dimensional algebra element, because we
-            # wind up with an inverse that lives in the subalgebra and
-            # we need information about the parent to convert it back.
-            V = self.span_of_powers()
-            assoc_subalg = self.subalgebra_generated_by()
-            # Mis-design warning: the basis used for span_of_powers()
-            # and subalgebra_generated_by() must be the same, and in
-            # the same order!
-            elt = assoc_subalg(V.coordinates(self.vector()))
+            The zero element is never invertible::
+
+                sage: set_random_seed()
+                sage: J = random_eja().zero().inverse()
+                Traceback (most recent call last):
+                ...
+                ValueError: element is not invertible
 
-            # This will be in the subalgebra's coordinates...
-            fda_elt = FiniteDimensionalAlgebraElement(assoc_subalg, elt)
-            subalg_inverse = fda_elt.inverse()
+            """
+            if not self.is_invertible():
+                raise ValueError("element is not invertible")
 
-            # So we have to convert back...
-            basis = [ self.parent(v) for v in V.basis() ]
-            pairs = zip(subalg_inverse.vector(), basis)
-            return self.parent().linear_combination(pairs)
+            P = self.parent()
+            return P(self.quadratic_representation().inverse()*self.vector())
 
 
         def is_invertible(self):
@@ -866,6 +943,16 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             return self.span_of_powers().dimension()
 
 
+        def left_matrix(self):
+            """
+            Our parent class defines ``left_matrix`` and ``matrix``
+            methods whose names are misleading. We don't want them.
+            """
+            raise NotImplementedError("use operator_matrix() instead")
+
+        matrix = left_matrix
+
+
         def minimal_polynomial(self):
             """
             Return the minimal polynomial of this element,
@@ -987,14 +1074,37 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             return W.linear_combination(zip(self.vector(), B))
 
 
+        def operator(self):
+            """
+            Return the left-multiplication-by-this-element
+            operator on the ambient algebra.
+
+            TESTS::
+
+                sage: set_random_seed()
+                sage: J = random_eja()
+                sage: x = J.random_element()
+                sage: y = J.random_element()
+                sage: x.operator()(y) == x*y
+                True
+                sage: y.operator()(x) == x*y
+                True
+
+            """
+            P = self.parent()
+            return FiniteDimensionalEuclideanJordanAlgebraMorphism(
+                     Hom(P,P),
+                     self.operator_matrix() )
+
+
+
         def operator_matrix(self):
             """
             Return the matrix that represents left- (or right-)
             multiplication by this element in the parent algebra.
 
-            We have to override this because the superclass method
-            returns a matrix that acts on row vectors (that is, on
-            the right).
+            We implement this ourselves to work around the fact that
+            our parent class represents everything with row vectors.
 
             EXAMPLES: