from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
from sage.categories.magmatic_algebras import MagmaticAlgebras
-from sage.combinat.free_module import CombinatorialFreeModule
+from sage.categories.sets_cat import cartesian_product
+from sage.combinat.free_module import (CombinatorialFreeModule,
+ CombinatorialFreeModule_CartesianProduct)
from sage.matrix.constructor import matrix
from sage.matrix.matrix_space import MatrixSpace
from sage.misc.cachefunc import cached_method
Why implement this for non-matrix algebras? Avoiding special
cases for the :class:`BilinearFormEJA` pays with simplicity in
its own right. But mainly, we would like to be able to assume
- that elements of a :class:`DirectSumEJA` can be displayed
+ that elements of a :class:`CartesianProductEJA` can be displayed
nicely, without having to have special classes for direct sums
one of whose components was a matrix algebra.
return cls(**kwargs)
-class DirectSumEJA(FiniteDimensionalEJA):
+class CartesianProductEJA(CombinatorialFreeModule_CartesianProduct,
+ 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.
+ The external (orthogonal) direct sum of two or more Euclidean
+ Jordan algebras. Every Euclidean Jordan algebra decomposes into an
+ orthogonal direct sum of simple Euclidean Jordan algebras which is
+ then isometric to a Cartesian product, so no generality is lost by
+ providing only this construction.
SETUP::
- sage: from mjo.eja.eja_algebra import (random_eja,
+ sage: from mjo.eja.eja_algebra import (CartesianProductEJA,
....: HadamardEJA,
- ....: RealSymmetricEJA,
- ....: DirectSumEJA)
+ ....: JordanSpinEJA,
+ ....: RealSymmetricEJA)
- EXAMPLES::
+ EXAMPLES:
+ The Jordan product is inherited from our factors and implemented by
+ our CombinatorialFreeModule Cartesian product superclass::
+
+ sage: set_random_seed()
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)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: x,y = J.random_elements(2)
+ sage: x*y in J
+ True
+
+ The ability to retrieve the original factors is implemented by our
+ CombinatorialFreeModule Cartesian product superclass::
+
+ sage: J1 = HadamardEJA(2, field=QQ)
+ sage: J2 = JordanSpinEJA(3, field=QQ)
+ sage: J = cartesian_product([J1,J2])
+ sage: J.cartesian_factors()
+ (Euclidean Jordan algebra of dimension 2 over Rational Field,
+ Euclidean Jordan algebra of dimension 3 over Rational Field)
TESTS:
- The external direct sum construction is only valid when the two factors
- have the same base ring; an error is raised otherwise::
+ All factors must share the same base field::
- sage: set_random_seed()
- sage: J1 = random_eja(field=AA)
- sage: J2 = random_eja(field=QQ,orthonormalize=False)
- sage: J = DirectSumEJA(J1,J2)
+ sage: J1 = HadamardEJA(2, field=QQ)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: CartesianProductEJA((J1,J2))
Traceback (most recent call last):
...
- ValueError: algebras must share the same base field
-
+ ValueError: all factors 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()
+ def __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 = J1.matrix_space().cartesian_product(J2.matrix_space())
- self._cartprod_algebra = J1.cartesian_product(J2)
+ M = cartesian_product( [J.matrix_space() for J in modules] )
- self._matrix_basis = tuple( [M((a,0)) for a in J1.matrix_basis()] +
- [M((0,b)) for b in J2.matrix_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))
- 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())
+ self._matrix_basis = tuple(self._matrix_basis)
+ 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.
+ #
+ self.rank.set_cache(sum(J.rank() for J in modules))
- def product(self,x,y):
+ @cached_method
+ def cartesian_projection(self, i):
r"""
SETUP::
- sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
- ....: ComplexHermitianEJA,
- ....: DirectSumEJA)
+ sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: HadamardEJA,
+ ....: RealSymmetricEJA)
- TESTS::
+ EXAMPLES::
+
+ sage: J1 = HadamardEJA(2)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: J.cartesian_projection(0)
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [1 0 0 0 0]
+ [0 1 0 0 0]
+ Domain: Euclidean Jordan algebra of dimension 2 over Algebraic
+ Real Field (+) Euclidean Jordan algebra of dimension 3 over
+ Algebraic Real Field
+ Codomain: Euclidean Jordan algebra of dimension 2 over Algebraic
+ Real Field
+ sage: J.cartesian_projection(1)
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [0 0 1 0 0]
+ [0 0 0 1 0]
+ [0 0 0 0 1]
+ Domain: Euclidean Jordan algebra of dimension 2 over Algebraic
+ Real Field (+) Euclidean Jordan algebra of dimension 3 over
+ Algebraic Real Field
+ Codomain: Euclidean Jordan algebra of dimension 3 over Algebraic
+ Real Field
+
+ TESTS:
+
+ The answer never changes::
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
+ sage: J1 = random_eja()
+ sage: J2 = random_eja()
+ sage: J = cartesian_product([J1,J2])
+ sage: P0 = J.cartesian_projection(0)
+ sage: P1 = J.cartesian_projection(0)
+ sage: P0 == P1
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())
+ 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)
+ return FiniteDimensionalEJAOperator(self,Ji,Pi.matrix())
-
- def cartesian_factors(self):
+ @cached_method
+ def cartesian_embedding(self, i):
r"""
- Return the pair of this algebra's factors.
-
SETUP::
- sage: from mjo.eja.eja_algebra import (HadamardEJA,
- ....: JordanSpinEJA,
- ....: DirectSumEJA)
+ sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: HadamardEJA,
+ ....: RealSymmetricEJA)
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)
+ sage: J1 = HadamardEJA(2)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: J
+ foo
+ sage: J.cartesian_embedding
+ bar
+ sage: J.cartesian_embedding(0)
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [1 0]
+ [0 1]
+ [0 0]
+ [0 0]
+ [0 0]
+ Domain: Euclidean Jordan algebra of dimension 2 over
+ Algebraic Real Field
+ Codomain: Euclidean Jordan algebra of dimension 2 over
+ Algebraic Real Field (+) Euclidean Jordan algebra of
+ dimension 3 over Algebraic Real Field
+ sage: J.cartesian_embedding(1)
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [0 0 0]
+ [0 0 0]
+ [1 0 0]
+ [0 1 0]
+ [0 0 1]
+ Domain: Euclidean Jordan algebra of dimension 3 over
+ Algebraic Real Field
+ Codomain: Euclidean Jordan algebra of dimension 2 over
+ Algebraic Real Field (+) Euclidean Jordan algebra of
+ dimension 3 over Algebraic Real Field
+
+ TESTS:
+
+ The answer never changes::
+
+ sage: set_random_seed()
+ sage: J1 = random_eja()
+ sage: J2 = random_eja()
+ sage: J = cartesian_product([J1,J2])
+ sage: E0 = J.cartesian_embedding(0)
+ sage: E1 = J.cartesian_embedding(0)
+ sage: E0 == E1
+ True
"""
- return self._cartprod_algebra.cartesian_factors()
+ 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)
+ return FiniteDimensionalEJAOperator(Ji,self,Ei.matrix())
+
+
+FiniteDimensionalEJA.CartesianProduct = CartesianProductEJA
# def projections(self):