]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
eja: rewrite the operator class again to eliminate VectorSpaceMorphisms.
authorMichael Orlitzky <michael@orlitzky.com>
Sun, 28 Jul 2019 04:36:43 +0000 (00:36 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Mon, 29 Jul 2019 03:19:01 +0000 (23:19 -0400)
Wherever possible, I'd like to eliminate row-vector APIs from sneaking
into userland. There's no reason for us to subclass VectorSpaceMorphism,
because left-multiplication by a scalar doesn't work anyway. So, just
drop it, and keep our own matrix variable around instead.

mjo/eja/euclidean_jordan_algebra.py

index 31717eeeb8e6b9daf37f8992eaa7f52fc1127470..5f9d5ba22435bb4c0de7391b35f18bc4d00d53fd 100644 (file)
@@ -6,8 +6,7 @@ what can be supported in a general Jordan Algebra.
 """
 
 from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis
-from sage.categories.morphism import SetMorphism
-from sage.modules.vector_space_morphism import VectorSpaceMorphism
+from sage.categories.map import Map
 from sage.structure.element import is_Matrix
 from sage.structure.category_object import normalize_names
 
@@ -15,23 +14,36 @@ from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import
 from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement
 
 
-class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
+class FiniteDimensionalEuclideanJordanAlgebraOperator(Map):
     def __init__(self, domain_eja, codomain_eja, mat):
-        # We save these so that we can output them as part of our
-        # text representation. Overriding the domain/codomain methods
-        # doesn't work because the EJAs aren't (directly) vector spaces.
-        self._domain_eja = domain_eja
-        self._codomain_eja = codomain_eja
-
-        # Otherwise, we just feed everything to the vector space morphism
-        # constructor.
-        V = domain_eja.vector_space()
-        W = codomain_eja.vector_space()
-        homspace = V.Hom(W)
-        VectorSpaceMorphism.__init__(self, homspace, mat)
-
-
-    def __call__(self, x):
+        if not (
+          isinstance(domain_eja, FiniteDimensionalEuclideanJordanAlgebra) and
+          isinstance(codomain_eja, FiniteDimensionalEuclideanJordanAlgebra) ):
+            raise ValueError('(co)domains must be finite-dimensional Euclidean '
+                             'Jordan algebras')
+
+        F = domain_eja.base_ring()
+        if not (F == codomain_eja.base_ring()):
+            raise ValueError("domain and codomain must have the same base ring")
+
+        # We need to supply something here to avoid getting the
+        # default Homset of the parent FiniteDimensionalAlgebra class,
+        # which messes up e.g. equality testing.
+        parent = Hom(domain_eja, codomain_eja, VectorSpaces(F))
+
+        # The Map initializer will set our parent to a homset, which
+        # is explicitly NOT what we want, because these ain't algebra
+        # homomorphisms.
+        super(FiniteDimensionalEuclideanJordanAlgebraOperator,self).__init__(parent)
+
+        # Keep a matrix around to do all of the real work. It would
+        # be nice if we could use a VectorSpaceMorphism instead, but
+        # those use row vectors that we don't want to accidentally
+        # expose to our users.
+        self._matrix = mat
+
+
+    def _call_(self, x):
         """
         Allow this operator to be called only on elements of an EJA.
 
@@ -45,42 +57,10 @@ class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
             True
 
         """
-        # Overriding the single-underscore _call_ didn't work?
-        if x not in self._domain_eja:
-            raise ValueError("argument does not live in the operator's domain")
-        return self._codomain_eja(self.matrix()*x.vector())
-
-
-    def _repr_(self):
-        r"""
-
-        A text representation of this linear operator on a Euclidean
-        Jordan Algebra.
-
-        EXAMPLES::
-
-            sage: J = JordanSpinEJA(2)
-            sage: id = identity_matrix(J.base_ring(), J.dimension())
-            sage: FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
-            Linear operator between finite-dimensional Euclidean Jordan
-            algebras represented by the matrix:
-            [1 0]
-            [0 1]
-            Domain: Euclidean Jordan algebra of degree 2 over Rational Field
-            Codomain: Euclidean Jordan algebra of degree 2 over Rational Field
-
-        """
-        msg = ("Linear operator between finite-dimensional Euclidean Jordan "
-                "algebras represented by the matrix:\n",
-               "{!r}\n",
-               "Domain: {}\n",
-               "Codomain: {}")
-        return ''.join(msg).format(self.matrix(),
-                                   self._domain_eja,
-                                   self._codomain_eja)
+        return self.codomain()(self.matrix()*x.vector())
 
 
-    def __add__(self, other):
+    def _add_(self, other):
         """
         Add the ``other`` EJA operator to this one.
 
@@ -112,17 +92,56 @@ class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
             sage: f + g
             Traceback (most recent call last):
             ...
-            ValueError: operator (co)domains must match
+            TypeError: unsupported operand parent(s) for +: ...
 
         """
-        if not (self._domain_eja == other._domain_eja and
-                self._codomain_eja == other._codomain_eja):
-            raise ValueError("operator (co)domains must match")
         return FiniteDimensionalEuclideanJordanAlgebraOperator(
-                self._domain_eja,
-                self._codomain_eja,
-                VectorSpaceMorphism.__add__(self,other))
+                self.domain(),
+                self.codomain(),
+                self.matrix() + other.matrix())
+
+
+    def _composition_(self, other, homset):
+        """
+        Compose two EJA operators to get another one (and NOT a formal
+        composite object) back.
+
+        EXAMPLES::
 
+            sage: J1 = JordanSpinEJA(3)
+            sage: J2 = RealCartesianProductEJA(2)
+            sage: J3 = RealSymmetricEJA(1)
+            sage: mat1 = matrix(QQ, [[1,2,3],
+            ....:                    [4,5,6]])
+            sage: mat2 = matrix(QQ, [[7,8]])
+            sage: g = FiniteDimensionalEuclideanJordanAlgebraOperator(J1,
+            ....:                                                     J2,
+            ....:                                                     mat1)
+            sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J2,
+            ....:                                                     J3,
+            ....:                                                     mat2)
+            sage: f*g
+            Linear operator between finite-dimensional Euclidean Jordan
+            algebras represented by the matrix:
+            [39 54 69]
+            Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+            Codomain: Euclidean Jordan algebra of degree 1 over Rational Field
+
+        """
+        return FiniteDimensionalEuclideanJordanAlgebraOperator(
+          other.domain(),
+          self.codomain(),
+          self.matrix()*other.matrix())
+
+
+    def __eq__(self, other):
+        if self.domain() != other.domain():
+            return False
+        if self.codomain() != other.codomain():
+            return False
+        if self.matrix() != other.matrix():
+            return False
+        return True
 
     def __invert__(self):
         """
@@ -144,31 +163,58 @@ class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
 
         """
         return FiniteDimensionalEuclideanJordanAlgebraOperator(
-                self._codomain_eja,
-                self._domain_eja,
-                VectorSpaceMorphism.__invert__(self))
+                self.codomain(),
+                self.domain(),
+                ~self.matrix())
+
 
     def __mul__(self, other):
         """
-        Compose this EJA operator with the ``other`` one, or scale it by
-        an element of its base ring.
+        Compose two EJA operators, or scale myself by an element of the
+        ambient vector space.
+
+        We need to override the real ``__mul__`` function to prevent the
+        coercion framework from throwing an error when it fails to convert
+        a base ring element into a morphism.
+
+        EXAMPLES:
+
+        We can scale an operator on a rational algebra by a rational number::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: e0,e1,e2 = J.gens()
+            sage: x = 2*e0 + 4*e1 + 16*e2
+            sage: x.operator()
+            Linear operator between finite-dimensional Euclidean Jordan algebras
+            represented by the matrix:
+            [ 2  4  0]
+            [ 2  9  2]
+            [ 0  4 16]
+            Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+            Codomain: Euclidean Jordan algebra of degree 3 over Rational Field
+            sage: x.operator()*(1/2)
+            Linear operator between finite-dimensional Euclidean Jordan algebras
+            represented by the matrix:
+            [  1   2   0]
+            [  1 9/2   1]
+            [  0   2   8]
+            Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+            Codomain: Euclidean Jordan algebra of degree 3 over Rational Field
+
         """
-        if other in self._codomain_eja.base_ring():
+        if other in self.codomain().base_ring():
             return FiniteDimensionalEuclideanJordanAlgebraOperator(
-                self._domain_eja,
-                self._codomain_eja,
-                self._matrix*other)
-
-        if not (self._domain_eja == other._codomain_eja):
-            raise ValueError("operator (co)domains must be compatible")
+                self.domain(),
+                self.codomain(),
+                self.matrix()*other)
 
-        return FiniteDimensionalEuclideanJordanAlgebraOperator(
-                other._domain_eja,
-                self._codomain_eja,
-                VectorSpaceMorphism.__mul__(self,other))
+        # This should eventually delegate to _composition_ after performing
+        # some sanity checks for us.
+        mor = super(FiniteDimensionalEuclideanJordanAlgebraOperator,self)
+        return mor.__mul__(other)
 
 
-    def __neg__(self):
+    def _neg_(self):
         """
         Negate this EJA operator.
 
@@ -188,9 +234,9 @@ class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
 
         """
         return FiniteDimensionalEuclideanJordanAlgebraOperator(
-                self._domain_eja,
-                self._codomain_eja,
-                VectorSpaceMorphism.__neg__(self))
+                self.domain(),
+                self.codomain(),
+                -self.matrix())
 
 
     def __pow__(self, n):
@@ -224,20 +270,84 @@ class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
             cols = self.domain().dimension()
             mat = matrix.identity(self.base_ring(), rows, cols)
         else:
-            mat = VectorSpaceMorphism.__pow__(self,n)
+            mat = self.matrix()**n
 
         return FiniteDimensionalEuclideanJordanAlgebraOperator(
-                 self._domain_eja,
-                 self._codomain_eja,
+                 self.domain(),
+                 self.codomain(),
                  mat)
 
-    def __sub__(self, other):
+
+    def _repr_(self):
+        r"""
+
+        A text representation of this linear operator on a Euclidean
+        Jordan Algebra.
+
+        EXAMPLES::
+
+            sage: J = JordanSpinEJA(2)
+            sage: id = identity_matrix(J.base_ring(), J.dimension())
+            sage: FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
+            Linear operator between finite-dimensional Euclidean Jordan
+            algebras represented by the matrix:
+            [1 0]
+            [0 1]
+            Domain: Euclidean Jordan algebra of degree 2 over Rational Field
+            Codomain: Euclidean Jordan algebra of degree 2 over Rational Field
+
+        """
+        msg = ("Linear operator between finite-dimensional Euclidean Jordan "
+                "algebras represented by the matrix:\n",
+               "{!r}\n",
+               "Domain: {}\n",
+               "Codomain: {}")
+        return ''.join(msg).format(self.matrix(),
+                                   self.domain(),
+                                   self.codomain())
+
+
+    def _sub_(self, other):
         """
         Subtract ``other`` from this EJA operator.
+
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: id = identity_matrix(J.base_ring(),J.dimension())
+            sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
+            sage: f - (f*2)
+            Linear operator between finite-dimensional Euclidean Jordan
+            algebras represented by the matrix:
+            [-1  0  0]
+            [ 0 -1  0]
+            [ 0  0 -1]
+            Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+            Codomain: Euclidean Jordan algebra of degree 3 over Rational Field
+
         """
         return (self + (-other))
 
 
+    def matrix(self):
+        """
+        Return the matrix representation of this operator with respect
+        to the default bases of its (co)domain.
+
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: mat = matrix(J.base_ring(), J.dimension(), range(9))
+            sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,mat)
+            sage: f.matrix()
+            [0 1 2]
+            [3 4 5]
+            [6 7 8]
+
+        """
+        return self._matrix
+
+
 class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
     @staticmethod
     def __classcall_private__(cls,