SETUP::
- sage: from mjo.eja.eja_algebra import (CartesianProductEJA,
+ sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: CartesianProductEJA,
....: HadamardEJA,
....: JordanSpinEJA,
....: RealSymmetricEJA)
...
ValueError: all factors must share the same base field
+ The "cached" Jordan and inner products are the componentwise
+ ones::
+
+ sage: set_random_seed()
+ sage: J1 = random_eja()
+ sage: J2 = random_eja()
+ sage: J = cartesian_product([J1,J2])
+ sage: x,y = J.random_elements(2)
+ sage: x*y == J.cartesian_jordan_product(x,y)
+ True
+ sage: x.inner_product(y) == J.cartesian_inner_product(x,y)
+ True
+
"""
def __init__(self, modules, **kwargs):
- CombinatorialFreeModule_CartesianProduct.__init__(self, modules, **kwargs)
+ CombinatorialFreeModule_CartesianProduct.__init__(self,
+ modules,
+ **kwargs)
field = modules[0].base_ring()
if not all( J.base_ring() == field for J in modules ):
raise ValueError("all factors must share the same base field")
- M = cartesian_product( [J.matrix_space() for J in modules] )
+ basis = tuple( b.to_vector().column() for b in self.basis() )
- m = len(modules)
- W = VectorSpace(field,m)
- self._matrix_basis = []
- for k in range(m):
- for a in modules[k].matrix_basis():
- v = W.zero().list()
- v[k] = a
- self._matrix_basis.append(M(v))
+ # Define jordan/inner products that operate on the basis.
+ def jordan_product(x_mat,y_mat):
+ x = self.from_vector(_mat2vec(x_mat))
+ y = self.from_vector(_mat2vec(y_mat))
+ return self.cartesian_jordan_product(x,y).to_vector().column()
- self._matrix_basis = tuple(self._matrix_basis)
+ def inner_product(x_mat, y_mat):
+ x = self.from_vector(_mat2vec(x_mat))
+ y = self.from_vector(_mat2vec(y_mat))
+ return self.cartesian_inner_product(x,y)
- n = len(self._matrix_basis)
- # TODO:
- #
- # Initialize the FDEJA class, too. Does this override the
- # initialization that we did for the
- # CombinatorialFreeModule_CartesianProduct class? If not, we
- # will probably have to duplicate some of the work (i.e. one
- # of the constructors). Since the CartesianProduct one is
- # smaller, that makes the most sense to copy/paste if it comes
- # down to that.
+ # Use whatever category the superclass came up with. Usually
+ # some join of the EJA and Cartesian product
+ # categories. There's no need to check the field since it
+ # already came from an EJA.
#
+ # If you want the basis to be orthonormalized, orthonormalize
+ # the factors.
+ FiniteDimensionalEJA.__init__(self,
+ basis,
+ jordan_product,
+ inner_product,
+ field=field,
+ orthonormalize=False,
+ check_field=False,
+ check_axioms=False,
+ category=self.category())
self.rank.set_cache(sum(J.rank() for J in modules))
SETUP::
sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: JordanSpinEJA,
....: HadamardEJA,
- ....: RealSymmetricEJA)
+ ....: RealSymmetricEJA,
+ ....: ComplexHermitianEJA)
- EXAMPLES::
+ EXAMPLES:
+
+ The projection morphisms are Euclidean Jordan algebra
+ operators::
sage: J1 = HadamardEJA(2)
sage: J2 = RealSymmetricEJA(2)
Codomain: Euclidean Jordan algebra of dimension 3 over Algebraic
Real Field
+ The projections work the way you'd expect on the vector
+ representation of an element::
+
+ sage: J1 = JordanSpinEJA(2)
+ sage: J2 = ComplexHermitianEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: pi_left = J.cartesian_projection(0)
+ sage: pi_right = J.cartesian_projection(1)
+ sage: pi_left(J.one()).to_vector()
+ (1, 0)
+ sage: pi_right(J.one()).to_vector()
+ (1, 0, 0, 1)
+ sage: J.one().to_vector()
+ (1, 0, 1, 0, 0, 1)
+
TESTS:
The answer never changes::
"""
Ji = self.cartesian_factors()[i]
- # We reimplement the CombinatorialFreeModule superclass method
- # because if we don't, something gets messed up with the caching
- # and the answer changes the second time you run it. See the TESTS.
- Pi = self._module_morphism(lambda j_t: Ji.monomial(j_t[1])
- if i == j_t[0] else Ji.zero(),
- codomain=Ji)
+ # Requires the fix on Trac 31421/31422 to work!
+ Pi = super().cartesian_projection(i)
return FiniteDimensionalEJAOperator(self,Ji,Pi.matrix())
@cached_method
SETUP::
sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: JordanSpinEJA,
....: HadamardEJA,
....: RealSymmetricEJA)
- EXAMPLES::
+ EXAMPLES:
+
+ The embedding morphisms are Euclidean Jordan algebra
+ operators::
sage: J1 = HadamardEJA(2)
sage: J2 = RealSymmetricEJA(2)
Algebraic Real Field (+) Euclidean Jordan algebra of
dimension 3 over Algebraic Real Field
+ The embeddings work the way you'd expect on the vector
+ representation of an element::
+
+ sage: J1 = JordanSpinEJA(3)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: iota_left = J.cartesian_embedding(0)
+ sage: iota_right = J.cartesian_embedding(1)
+ 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:
The answer never changes::
sage: E0 == E1
True
+ 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 = cartesian_product([J1,J2])
+ sage: iota_left = J.cartesian_embedding(0)
+ sage: iota_right = J.cartesian_embedding(1)
+ sage: pi_left = J.cartesian_projection(0)
+ sage: pi_right = J.cartesian_projection(1)
+ 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
+
"""
Ji = self.cartesian_factors()[i]
- # We reimplement the CombinatorialFreeModule superclass method
- # because if we don't, something gets messed up with the caching
- # and the answer changes the second time you run it. See the TESTS.
- Ei = Ji._module_morphism(lambda t: self.monomial((i, t)), codomain=self)
+ # Requires the fix on Trac 31421/31422 to work!
+ Ei = super().cartesian_embedding(i)
return FiniteDimensionalEJAOperator(Ji,self,Ei.matrix())
return sum( P(x).inner_product(P(y)) for P in projections )
-FiniteDimensionalEJA.CartesianProduct = CartesianProductEJA
-
-
-# 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)
-
-
+ Element = FiniteDimensionalEJAElement
+FiniteDimensionalEJA.CartesianProduct = CartesianProductEJA
random_eja = ConcreteEJA.random_instance