From 073f207e9cb5f9c7f1fa347cb24866dbd7c05a0c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 19 Feb 2021 23:29:59 -0500 Subject: [PATCH] eja: begin rework of Cartesian product EJA. --- mjo/eja/eja_algebra.py | 249 +++++++++++++++++++++++++++++------------ 1 file changed, 175 insertions(+), 74 deletions(-) diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index c35bf25..c7c0df8 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -20,7 +20,9 @@ from itertools import repeat 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 @@ -687,7 +689,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): 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. @@ -2675,117 +2677,216 @@ class TrivialEJA(ConcreteEJA): 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): -- 2.43.2