+ self.rank.set_cache(0)
+ self.one.set_cache( self.zero() )
+
+ @classmethod
+ def random_instance(cls, **kwargs):
+ # We don't take a "size" argument so the superclass method is
+ # inappropriate for us.
+ return cls(**kwargs)
+
+
+class DirectSumEJA(FiniteDimensionalEJA):
+ r"""
+ The external (orthogonal) direct sum of two other Euclidean Jordan
+ algebras. Essentially the Cartesian product of its two factors.
+ Every Euclidean Jordan algebra decomposes into an orthogonal
+ direct sum of simple Euclidean Jordan algebras, so no generality
+ is lost by providing only this construction.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: HadamardEJA,
+ ....: RealSymmetricEJA,
+ ....: DirectSumEJA)
+
+ EXAMPLES::
+
+ sage: J1 = HadamardEJA(2)
+ sage: J2 = RealSymmetricEJA(3)
+ sage: J = DirectSumEJA(J1,J2)
+ sage: J.dimension()
+ 8
+ sage: J.rank()
+ 5
+ sage: J.matrix_space()
+ The Cartesian product of (Full MatrixSpace of 2 by 1 dense matrices
+ over Algebraic Real Field, Full MatrixSpace of 3 by 3 dense matrices
+ over Algebraic Real Field)
+
+ TESTS:
+
+ The external direct sum construction is only valid when the two factors
+ have the same base ring; an error is raised otherwise::
+
+ sage: set_random_seed()
+ sage: J1 = random_eja(field=AA)
+ sage: J2 = random_eja(field=QQ,orthonormalize=False)
+ sage: J = DirectSumEJA(J1,J2)
+ Traceback (most recent call last):
+ ...
+ ValueError: algebras must share the same base field
+
+ """
+ def __init__(self, J1, J2, **kwargs):
+ if J1.base_ring() != J2.base_ring():
+ raise ValueError("algebras must share the same base field")
+ field = J1.base_ring()
+
+ M = J1.matrix_space().cartesian_product(J2.matrix_space())
+ self._cartprod_algebra = J1.cartesian_product(J2)
+
+ self._matrix_basis = tuple( [M((a,0)) for a in J1.matrix_basis()] +
+ [M((0,b)) for b in J2.matrix_basis()] )
+
+ n = len(self._matrix_basis)
+ self._sets = None
+ CombinatorialFreeModule.__init__(
+ self,
+ field,
+ range(n),
+ category=self._cartprod_algebra.category(),
+ bracket=False,
+ **kwargs)
+ self.rank.set_cache(J1.rank() + J2.rank())
+
+
+
+ def product(self,x,y):
+ r"""
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+ ....: ComplexHermitianEJA,
+ ....: DirectSumEJA)
+
+ TESTS::
+
+ sage: set_random_seed()
+ sage: J1 = JordanSpinEJA(3, field=QQ)
+ sage: J2 = ComplexHermitianEJA(2, field=QQ, orthonormalize=False)
+ sage: J = DirectSumEJA(J1,J2)
+ sage: J.random_element()*J.random_element() in J
+ True
+
+ """
+ xv = self._cartprod_algebra.from_vector(x.to_vector())
+ yv = self._cartprod_algebra.from_vector(y.to_vector())
+ return self.from_vector((xv*yv).to_vector())
+
+
+ def cartesian_factors(self):
+ r"""
+ Return the pair of this algebra's factors.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ ....: JordanSpinEJA,
+ ....: DirectSumEJA)
+
+ EXAMPLES::
+
+ sage: J1 = HadamardEJA(2, field=QQ)
+ sage: J2 = JordanSpinEJA(3, field=QQ)
+ sage: J = DirectSumEJA(J1,J2)
+ sage: J.cartesian_factors()
+ (Euclidean Jordan algebra of dimension 2 over Rational Field,
+ Euclidean Jordan algebra of dimension 3 over Rational Field)
+
+ """
+ return self._cartprod_algebra.cartesian_factors()
+
+
+# def projections(self):
+# r"""
+# Return a pair of projections onto this algebra's factors.
+
+# SETUP::
+
+# sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+# ....: ComplexHermitianEJA,
+# ....: DirectSumEJA)
+
+# EXAMPLES::
+
+# sage: J1 = JordanSpinEJA(2)
+# sage: J2 = ComplexHermitianEJA(2)
+# sage: J = DirectSumEJA(J1,J2)
+# sage: (pi_left, pi_right) = J.projections()
+# sage: J.one().to_vector()
+# (1, 0, 1, 0, 0, 1)
+# sage: pi_left(J.one()).to_vector()
+# (1, 0)
+# sage: pi_right(J.one()).to_vector()
+# (1, 0, 0, 1)
+
+# """
+# (J1,J2) = self.factors()
+# m = J1.dimension()
+# n = J2.dimension()
+# V_basis = self.vector_space().basis()
+# # Need to specify the dimensions explicitly so that we don't
+# # wind up with a zero-by-zero matrix when we want e.g. a
+# # zero-by-two matrix (important for composing things).
+# P1 = matrix(self.base_ring(), m, m+n, V_basis[:m])
+# P2 = matrix(self.base_ring(), n, m+n, V_basis[m:])
+# pi_left = FiniteDimensionalEJAOperator(self,J1,P1)
+# pi_right = FiniteDimensionalEJAOperator(self,J2,P2)
+# return (pi_left, pi_right)
+
+# def inclusions(self):
+# r"""
+# Return the pair of inclusion maps from our factors into us.
+
+# SETUP::
+
+# sage: from mjo.eja.eja_algebra import (random_eja,
+# ....: JordanSpinEJA,
+# ....: RealSymmetricEJA,
+# ....: DirectSumEJA)
+
+# EXAMPLES::
+
+# sage: J1 = JordanSpinEJA(3)
+# sage: J2 = RealSymmetricEJA(2)
+# sage: J = DirectSumEJA(J1,J2)
+# sage: (iota_left, iota_right) = J.inclusions()
+# sage: iota_left(J1.zero()) == J.zero()
+# True
+# sage: iota_right(J2.zero()) == J.zero()
+# True
+# sage: J1.one().to_vector()
+# (1, 0, 0)
+# sage: iota_left(J1.one()).to_vector()
+# (1, 0, 0, 0, 0, 0)
+# sage: J2.one().to_vector()
+# (1, 0, 1)
+# sage: iota_right(J2.one()).to_vector()
+# (0, 0, 0, 1, 0, 1)
+# sage: J.one().to_vector()
+# (1, 0, 0, 1, 0, 1)
+
+# TESTS:
+
+# Composing a projection with the corresponding inclusion should
+# produce the identity map, and mismatching them should produce
+# the zero map::
+
+# sage: set_random_seed()
+# sage: J1 = random_eja()
+# sage: J2 = random_eja()
+# sage: J = DirectSumEJA(J1,J2)
+# sage: (iota_left, iota_right) = J.inclusions()
+# sage: (pi_left, pi_right) = J.projections()
+# sage: pi_left*iota_left == J1.one().operator()
+# True
+# sage: pi_right*iota_right == J2.one().operator()
+# True
+# sage: (pi_left*iota_right).is_zero()
+# True
+# sage: (pi_right*iota_left).is_zero()
+# True
+
+# """
+# (J1,J2) = self.factors()
+# m = J1.dimension()
+# n = J2.dimension()
+# V_basis = self.vector_space().basis()
+# # Need to specify the dimensions explicitly so that we don't
+# # wind up with a zero-by-zero matrix when we want e.g. a
+# # two-by-zero matrix (important for composing things).
+# I1 = matrix.column(self.base_ring(), m, m+n, V_basis[:m])
+# I2 = matrix.column(self.base_ring(), n, m+n, V_basis[m:])
+# iota_left = FiniteDimensionalEJAOperator(J1,self,I1)
+# iota_right = FiniteDimensionalEJAOperator(J2,self,I2)
+# return (iota_left, iota_right)
+
+# def inner_product(self, x, y):
+# r"""
+# The standard Cartesian inner-product.
+
+# We project ``x`` and ``y`` onto our factors, and add up the
+# inner-products from the subalgebras.
+
+# SETUP::
+
+
+# sage: from mjo.eja.eja_algebra import (HadamardEJA,
+# ....: QuaternionHermitianEJA,
+# ....: DirectSumEJA)
+
+# EXAMPLE::
+
+# sage: J1 = HadamardEJA(3,field=QQ)
+# sage: J2 = QuaternionHermitianEJA(2,field=QQ,orthonormalize=False)
+# sage: J = DirectSumEJA(J1,J2)
+# sage: x1 = J1.one()
+# sage: x2 = x1
+# sage: y1 = J2.one()
+# sage: y2 = y1
+# sage: x1.inner_product(x2)
+# 3
+# sage: y1.inner_product(y2)
+# 2
+# sage: J.one().inner_product(J.one())
+# 5
+
+# """
+# (pi_left, pi_right) = self.projections()
+# x1 = pi_left(x)
+# x2 = pi_right(x)
+# y1 = pi_left(y)
+# y2 = pi_right(y)
+
+# return (x1.inner_product(y1) + x2.inner_product(y2))
+
+
+
+random_eja = ConcreteEJA.random_instance