+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):
+ """
+ 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
+ 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.
+
+ 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
+ our EJA, and you can't add/multiply/etc. them.
+ """
+ def _add_(self, other):
+ """
+ Add two EJA morphisms in the obvious way.
+
+ EXAMPLES::
+
+ sage: J = RealSymmetricEJA(3)
+ sage: x = J.zero()
+ sage: y = J.one()
+ sage: x.operator() + y.operator()
+ Morphism from Euclidean Jordan algebra of degree 6 over Rational
+ Field to Euclidean Jordan algebra of degree 6 over Rational Field
+ given by matrix
+ [1 0 0 0 0 0]
+ [0 1 0 0 0 0]
+ [0 0 1 0 0 0]
+ [0 0 0 1 0 0]
+ [0 0 0 0 1 0]
+ [0 0 0 0 0 1]
+
+ 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
+
+ """
+ P = self.parent()
+ if not other in P:
+ raise ValueError("summands must live in the same space")
+
+ return FiniteDimensionalEuclideanJordanAlgebraMorphism(
+ P,
+ self.matrix() + other.matrix() )
+
+
+ def __init__(self, parent, f):
+ FiniteDimensionalAlgebraMorphism.__init__(self,
+ parent,
+ f.transpose(),
+ unitary=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,
+ 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 __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
+ on column vectors.
+ """
+ return FiniteDimensionalAlgebraMorphism.matrix(self).transpose()
+