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.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):
"""
- 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
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.
+ 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 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.
- EXAMPLES:
+ EXAMPLES::
sage: J = RealSymmetricEJA(3)
sage: x = J.zero()
- sage: y = 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
- [0 0 0 0 0 0]
- [0 0 0 0 0 0]
- [0 0 0 0 0 0]
- [0 0 0 0 0 0]
- [0 0 0 0 0 0]
- [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 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()
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,
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
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()
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,
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:]
- 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)
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):
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::
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)
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 (
- ....: Qx.inverse()
+ ....: ~Qx
....: ==
....: x.inverse().quadratic_representation() )
True
- sage: Qxy*(J.one().vector()) == (x*y).vector()
+ sage: Qxy(J.one()) == x*y
True
Property 4:
sage: not x.is_invertible() or (
....: x.quadratic_representation(x.inverse())*Qx
....: ==
- ....: 2*x.operator_matrix()*Qex - Qx )
+ ....: 2*x.operator()*Qex - Qx )
True
- sage: 2*x.operator_matrix()*Qex - Qx == Lxx
+ sage: 2*x.operator()*Qex - Qx == Lxx
True
Property 5:
- sage: J(Qy*x.vector()).quadratic_representation() == Qy*Qx*Qy
+ sage: Qy(x).quadratic_representation() == Qy*Qx*Qy
True
Property 6:
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 (
- ....: J(Qx*y.vector())^n == J(Qxn*(y^n).vector()) )
+ ....: Qx(y)^n == Qxn(y^n) )
True
"""
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):