]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/euclidean_jordan_algebra.py
eja: fix an erroneous test case.
[sage.d.git] / mjo / eja / euclidean_jordan_algebra.py
index fa11aed040a152de2cfe8f9c398b77af39c33276..30d04f4a352b7d553470bd456d18d224e2ec5428 100644 (file)
@@ -5,19 +5,100 @@ are used in optimization, and have some additional nice methods beyond
 what can be supported in a general Jordan Algebra.
 """
 
 what can be supported in a general Jordan Algebra.
 """
 
-from sage.categories.magmatic_algebras import MagmaticAlgebras
+from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis
+from sage.categories.morphism import SetMorphism
 from sage.structure.element import is_Matrix
 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.structure.element import is_Matrix
 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
+from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_morphism import FiniteDimensionalAlgebraMorphism, FiniteDimensionalAlgebraHomset
+
+
+class FiniteDimensionalEuclideanJordanAlgebraHomset(FiniteDimensionalAlgebraHomset):
+
+    def has_coerce_map_from(self, S):
+        """
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: H = J.Hom(J)
+            sage: H.has_coerce_map_from(QQ)
+            True
+
+        """
+        try:
+            # The Homset classes override has_coerce_map_from() with
+            # something that crashes when it's given e.g. QQ.
+            if S.is_subring(self.codomain().base_ring()):
+                return True
+        except:
+            pclass = super(FiniteDimensionalEuclideanJordanAlgebraHomset, self)
+            return pclass.has_coerce_map_from(S)
+
+
+    def _coerce_map_from_(self, S):
+        """
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: H = J.Hom(J)
+            sage: H.coerce(2)
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [2 0 0]
+            [0 2 0]
+            [0 0 2]
+
+        """
+        C = self.codomain()
+        R = C.base_ring()
+        if S.is_subring(R):
+            h = S.hom(self.codomain())
+            return SetMorphism(Hom(S,C), lambda x: h(x).operator())
+
+
+    def __call__(self, x):
+        """
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: H = J.Hom(J)
+            sage: H(2)
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [2 0 0]
+            [0 2 0]
+            [0 0 2]
+
+        """
+        if x in self.base_ring():
+            cols = self.domain().dimension()
+            rows = self.codomain().dimension()
+            x = x*identity_matrix(self.codomain().base_ring(), rows, cols)
+        return FiniteDimensionalEuclideanJordanAlgebraMorphism(self, x)
+
+
+    def one(self):
+        """
+        Return the identity morphism, but as a member of the right
+        space (so that we can add it, multiply it, etc.)
+        """
+        cols = self.domain().dimension()
+        rows = self.codomain().dimension()
+        mat = identity_matrix(self.base_ring(), rows, cols)
+        return FiniteDimensionalEuclideanJordanAlgebraMorphism(self, mat)
+
 
 
 class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMorphism):
     """
 
 
 class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMorphism):
     """
-    A very thin wrapper around FiniteDimensionalAlgebraMorphism that
-    does only three things:
+    A linear map between two finite-dimensional EJAs.
+
+    This is a very thin wrapper around FiniteDimensionalAlgebraMorphism
+    that does only a few things:
 
       1. Avoids the ``unitary`` and ``check`` arguments to the constructor
          that will always be ``False``. This is necessary because these
 
       1. Avoids the ``unitary`` and ``check`` arguments to the constructor
          that will always be ``False``. This is necessary because these
@@ -28,17 +109,15 @@ class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMo
       2. Inputs and outputs the underlying matrix with respect to COLUMN
          vectors, unlike the parent class.
 
       2. Inputs and outputs the underlying matrix with respect to COLUMN
          vectors, unlike the parent class.
 
-      3. Allows us to add morphisms in the obvious way.
-
-      4. Allows us to invert morphisms.
+      3. Allows us to add, subtract, negate, multiply (compose), and
+         invert morphisms in the obvious way.
 
     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
 
     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, and you can't add/invert them.
+    our EJA, and you can't add/multiply/etc. them.
     """
     """
-
-    def __add__(self, other):
+    def _add_(self, other):
         """
         Add two EJA morphisms in the obvious way.
 
         """
         Add two EJA morphisms in the obvious way.
 
@@ -85,6 +164,175 @@ class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMo
                                                   check=False)
 
 
                                                   check=False)
 
 
+    def __invert__(self):
+        """
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: x = J.linear_combination(zip(range(len(J.gens())), J.gens()))
+            sage: x.is_invertible()
+            True
+            sage: ~x.operator()
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [-3/2    2 -1/2]
+            [   1    0    0]
+            [-1/2    0  1/2]
+            sage: x.operator_matrix().inverse()
+            [-3/2    2 -1/2]
+            [   1    0    0]
+            [-1/2    0  1/2]
+
+        TESTS::
+
+            sage: set_random_seed()
+            sage: J = random_eja()
+            sage: x = J.random_element()
+            sage: not x.is_invertible() or (
+            ....:   (~x.operator()).matrix() == x.operator_matrix().inverse() )
+            True
+
+        """
+        A = self.matrix()
+        if not A.is_invertible():
+            raise ValueError("morphism is not invertible")
+
+        P = self.parent()
+        return FiniteDimensionalEuclideanJordanAlgebraMorphism(self.parent(),
+                                                                A.inverse())
+
+    def _lmul_(self, right):
+        """
+        Compose two EJA morphisms using multiplicative notation.
+
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: x = J.zero()
+            sage: y = J.one()
+            sage: x.operator() * y.operator()
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [0 0 0]
+            [0 0 0]
+            [0 0 0]
+
+        ::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: x = J.linear_combination(zip(range(len(J.gens())), J.gens()))
+            sage: x.operator()
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [  0   1   0]
+            [1/2   1 1/2]
+            [  0   1   2]
+            sage: 2*x.operator()
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [0 2 0]
+            [1 2 1]
+            [0 2 4]
+            sage: x.operator()*2
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [0 2 0]
+            [1 2 1]
+            [0 2 4]
+
+        TESTS::
+
+            sage: set_random_seed()
+            sage: J = random_eja()
+            sage: x = J.random_element()
+            sage: y = J.random_element()
+            sage: (x.operator() * y.operator()) in J.Hom(J)
+            True
+
+        """
+        try:
+            # I think the morphism classes break the coercion framework
+            # somewhere along the way, so we have to do this ourselves.
+            right = self.parent().coerce(right)
+        except:
+            pass
+
+        if not right.codomain() is self.domain():
+            raise ValueError("(co)domains must agree for composition")
+
+        return FiniteDimensionalEuclideanJordanAlgebraMorphism(
+                 self.parent(),
+                 self.matrix()*right.matrix() )
+
+    __mul__ = _lmul_
+
+
+    def __pow__(self, n):
+        """
+
+        TESTS::
+
+            sage: J = JordanSpinEJA(4)
+            sage: e0,e1,e2,e3 = J.gens()
+            sage: x = -5/2*e0 + 1/2*e2 + 20*e3
+            sage: Qx = x.quadratic_representation()
+            sage: Qx^0
+            Morphism from Euclidean Jordan algebra of degree 4 over Rational
+            Field to Euclidean Jordan algebra of degree 4 over Rational Field
+            given by matrix
+            [1 0 0 0]
+            [0 1 0 0]
+            [0 0 1 0]
+            [0 0 0 1]
+            sage: (x^0).quadratic_representation() == Qx^0
+            True
+
+        """
+        if n == 0:
+            # We get back the stupid identity morphism which doesn't
+            # live in the right space.
+            return self.parent().one()
+        elif n == 1:
+            return self
+        else:
+            return FiniteDimensionalAlgebraMorphism.__pow__(self,n)
+
+
+    def _neg_(self):
+        """
+        Negate this morphism.
+
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: x = J.one()
+            sage: -x.operator()
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational Field
+            given by matrix
+            [-1  0  0]
+            [ 0 -1  0]
+            [ 0  0 -1]
+
+        TESTS::
+
+            sage: set_random_seed()
+            sage: J = random_eja()
+            sage: x = J.random_element()
+            sage: -x.operator() in J.Hom(J)
+            True
+
+        """
+        return FiniteDimensionalEuclideanJordanAlgebraMorphism(
+                  self.parent(),
+                  -self.matrix() )
+
+
     def _repr_(self):
         """
         We override only the representation that is shown to the user,
     def _repr_(self):
         """
         We override only the representation that is shown to the user,
@@ -119,6 +367,36 @@ class FiniteDimensionalEuclideanJordanAlgebraMorphism(FiniteDimensionalAlgebraMo
         return "Morphism from {} to {} given by matrix\n{}".format(
             self.domain(), self.codomain(), self.matrix())
 
         return "Morphism from {} to {} given by matrix\n{}".format(
             self.domain(), self.codomain(), self.matrix())
 
+
+    def __sub__(self, other):
+        """
+        Subtract one morphism from another using addition and negation.
+
+        EXAMPLES::
+
+            sage: J = RealSymmetricEJA(2)
+            sage: L1 = J.one().operator()
+            sage: L1 - L1
+            Morphism from Euclidean Jordan algebra of degree 3 over Rational
+            Field to Euclidean Jordan algebra of degree 3 over Rational
+            Field given by matrix
+            [0 0 0]
+            [0 0 0]
+            [0 0 0]
+
+        TESTS::
+
+            sage: set_random_seed()
+            sage: J = random_eja()
+            sage: x = J.random_element()
+            sage: y = J.random_element()
+            sage: x.operator() - y.operator() in J.Hom(J)
+            True
+
+        """
+        return self + (-other)
+
+
     def matrix(self):
         """
         Return the matrix of this morphism with respect to a left-action
     def matrix(self):
         """
         Return the matrix of this morphism with respect to a left-action
@@ -145,7 +423,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                 raise ValueError("input is not a multiplication table")
         mult_table = tuple(mult_table)
 
                 raise ValueError("input is not a multiplication table")
         mult_table = tuple(mult_table)
 
-        cat = MagmaticAlgebras(field).FiniteDimensional().WithBasis()
+        cat = FiniteDimensionalAlgebrasWithBasis(field)
         cat.or_subcategory(category)
         if assume_associative:
             cat = cat.Associative()
         cat.or_subcategory(category)
         if assume_associative:
             cat = cat.Associative()
@@ -163,6 +441,15 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                                  natural_basis=natural_basis)
 
 
                                  natural_basis=natural_basis)
 
 
+    def _Hom_(self, B, cat):
+        """
+        Construct a homset of ``self`` and ``B``.
+        """
+        return FiniteDimensionalEuclideanJordanAlgebraHomset(self,
+                                                             B,
+                                                             category=cat)
+
+
     def __init__(self,
                  field,
                  mult_table,
     def __init__(self,
                  field,
                  mult_table,
@@ -797,12 +1084,12 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                 sage: n = ZZ.random_element(1,10)
                 sage: J = JordanSpinEJA(n)
                 sage: x = J.random_element()
                 sage: n = ZZ.random_element(1,10)
                 sage: J = JordanSpinEJA(n)
                 sage: x = J.random_element()
-                sage: while x.is_zero():
+                sage: while not x.is_invertible():
                 ....:     x = J.random_element()
                 sage: x_vec = x.vector()
                 sage: x0 = x_vec[0]
                 sage: x_bar = x_vec[1:]
                 ....:     x = J.random_element()
                 sage: x_vec = x.vector()
                 sage: x0 = x_vec[0]
                 sage: x_bar = x_vec[1:]
-                sage: coeff = 1/(x0^2 - x_bar.inner_product(x_bar))
+                sage: coeff = ~(x0^2 - x_bar.inner_product(x_bar))
                 sage: inv_vec = x_vec.parent()([x0] + (-x_bar).list())
                 sage: x_inverse = coeff*inv_vec
                 sage: x.inverse() == J(x_inverse)
                 sage: inv_vec = x_vec.parent()([x0] + (-x_bar).list())
                 sage: x_inverse = coeff*inv_vec
                 sage: x.inverse() == J(x_inverse)
@@ -845,8 +1132,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             if not self.is_invertible():
                 raise ValueError("element is not invertible")
 
             if not self.is_invertible():
                 raise ValueError("element is not invertible")
 
-            P = self.parent()
-            return P(self.quadratic_representation().inverse()*self.vector())
+            return (~self.quadratic_representation())(self)
 
 
         def is_invertible(self):
 
 
         def is_invertible(self):
@@ -1231,7 +1517,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                 sage: D = (x0^2 - x_bar.inner_product(x_bar))*D
                 sage: D = D + 2*x_bar.tensor_product(x_bar)
                 sage: Q = block_matrix(2,2,[A,B,C,D])
                 sage: D = (x0^2 - x_bar.inner_product(x_bar))*D
                 sage: D = D + 2*x_bar.tensor_product(x_bar)
                 sage: Q = block_matrix(2,2,[A,B,C,D])
-                sage: Q == x.quadratic_representation()
+                sage: Q == x.quadratic_representation().matrix()
                 True
 
             Test all of the properties from Theorem 11.2 in Alizadeh::
                 True
 
             Test all of the properties from Theorem 11.2 in Alizadeh::
@@ -1240,8 +1526,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                 sage: J = random_eja()
                 sage: x = J.random_element()
                 sage: y = J.random_element()
                 sage: J = random_eja()
                 sage: x = J.random_element()
                 sage: y = J.random_element()
-                sage: Lx = x.operator_matrix()
-                sage: Lxx = (x*x).operator_matrix()
+                sage: Lx = x.operator()
+                sage: Lxx = (x*x).operator()
                 sage: Qx = x.quadratic_representation()
                 sage: Qy = y.quadratic_representation()
                 sage: Qxy = x.quadratic_representation(y)
                 sage: Qx = x.quadratic_representation()
                 sage: Qy = y.quadratic_representation()
                 sage: Qxy = x.quadratic_representation(y)
@@ -1262,17 +1548,16 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             Property 3:
 
 
             Property 3:
 
-                sage: not x.is_invertible() or (
-                ....:     Qx*x.inverse().vector() == x.vector() )
+                sage: not x.is_invertible() or ( Qx(x.inverse()) == x )
                 True
 
                 sage: not x.is_invertible() or (
                 True
 
                 sage: not x.is_invertible() or (
-                ....:   Qx.inverse()
+                ....:   ~Qx
                 ....:   ==
                 ....:   x.inverse().quadratic_representation() )
                 True
 
                 ....:   ==
                 ....:   x.inverse().quadratic_representation() )
                 True
 
-                sage: Qxy*(J.one().vector()) == (x*y).vector()
+                sage: Qxy(J.one()) == x*y
                 True
 
             Property 4:
                 True
 
             Property 4:
@@ -1285,15 +1570,15 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
                 sage: not x.is_invertible() or (
                 ....:   x.quadratic_representation(x.inverse())*Qx
                 ....:   ==
                 sage: not x.is_invertible() or (
                 ....:   x.quadratic_representation(x.inverse())*Qx
                 ....:   ==
-                ....:   2*x.operator_matrix()*Qex - Qx )
+                ....:   2*x.operator()*Qex - Qx )
                 True
 
                 True
 
-                sage: 2*x.operator_matrix()*Qex - Qx == Lxx
+                sage: 2*x.operator()*Qex - Qx == Lxx
                 True
 
             Property 5:
 
                 True
 
             Property 5:
 
-                sage: J(Qy*x.vector()).quadratic_representation() == Qy*Qx*Qy
+                sage: Qy(x).quadratic_representation() == Qy*Qx*Qy
                 True
 
             Property 6:
                 True
 
             Property 6:
@@ -1304,13 +1589,13 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             Property 7:
 
                 sage: not x.is_invertible() or (
             Property 7:
 
                 sage: not x.is_invertible() or (
-                ....:   Qx*x.inverse().operator_matrix() == Lx )
+                ....:   Qx*x.inverse().operator() == Lx )
                 True
 
             Property 8:
 
                 sage: not x.operator_commutes_with(y) or (
                 True
 
             Property 8:
 
                 sage: not x.operator_commutes_with(y) or (
-                ....:   J(Qx*y.vector())^n == J(Qxn*(y^n).vector()) )
+                ....:   Qx(y)^n == Qxn(y^n) )
                 True
 
             """
                 True
 
             """
@@ -1319,9 +1604,9 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             elif not other in self.parent():
                 raise TypeError("'other' must live in the same algebra")
 
             elif not other in self.parent():
                 raise TypeError("'other' must live in the same algebra")
 
-            L = self.operator_matrix()
-            M = other.operator_matrix()
-            return ( L*M + M*L - (self*other).operator_matrix() )
+            L = self.operator()
+            M = other.operator()
+            return ( L*M + M*L - (self*other).operator() )
 
 
         def span_of_powers(self):
 
 
         def span_of_powers(self):