X-Git-Url: http://gitweb.michael.orlitzky.com/?p=sage.d.git;a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=af4080b0807d6ac6848849f23edd237657896413;hp=79efd3876b6fadd0d2a7494a45856bf46bf3a621;hb=6d6af7c2560b2886cd47a2c8f3c0b9d1b843f649;hpb=4d2f874e306baa8d99fd938e978b8c968ca3a82e diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 79efd38..af4080b 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -1743,6 +1743,15 @@ class RationalBasisEJA(FiniteDimensionalEJA): check_field=False, check_axioms=False) + def rational_algebra(self): + # Using None as a flag here (rather than just assigning "self" + # to self._rational_algebra by default) feels a little bit + # more sane to me in a garbage-collected environment. + if self._rational_algebra is None: + return self + else: + return self._rational_algebra + @cached_method def _charpoly_coefficients(self): r""" @@ -1767,18 +1776,15 @@ class RationalBasisEJA(FiniteDimensionalEJA): Algebraic Real Field """ - if self._rational_algebra is None: - # There's no need to construct *another* algebra over the - # rationals if this one is already over the - # rationals. Likewise, if we never orthonormalized our - # basis, we might as well just use the given one. + if self.rational_algebra() is self: + # Bypass the hijinks if they won't benefit us. return super()._charpoly_coefficients() # Do the computation over the rationals. The answer will be # the same, because all we've done is a change of basis. # Then, change back from QQ to our real base ring a = ( a_i.change_ring(self.base_ring()) - for a_i in self._rational_algebra._charpoly_coefficients() ) + for a_i in self.rational_algebra()._charpoly_coefficients() ) # Otherwise, convert the coordinate variables back to the # deorthonormalized ones. @@ -2144,10 +2150,7 @@ class RealSymmetricEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): from mjo.eja.eja_cache import real_symmetric_eja_coeffs a = real_symmetric_eja_coeffs(self) if a is not None: - if self._rational_algebra is None: - self._charpoly_coefficients.set_cache(a) - else: - self._rational_algebra._charpoly_coefficients.set_cache(a) + self.rational_algebra()._charpoly_coefficients.set_cache(a) @@ -2235,10 +2238,7 @@ class ComplexHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): from mjo.eja.eja_cache import complex_hermitian_eja_coeffs a = complex_hermitian_eja_coeffs(self) if a is not None: - if self._rational_algebra is None: - self._charpoly_coefficients.set_cache(a) - else: - self._rational_algebra._charpoly_coefficients.set_cache(a) + self.rational_algebra()._charpoly_coefficients.set_cache(a) @staticmethod def _max_random_instance_size(max_dimension): @@ -2328,10 +2328,7 @@ class QuaternionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): from mjo.eja.eja_cache import quaternion_hermitian_eja_coeffs a = quaternion_hermitian_eja_coeffs(self) if a is not None: - if self._rational_algebra is None: - self._charpoly_coefficients.set_cache(a) - else: - self._rational_algebra._charpoly_coefficients.set_cache(a) + self.rational_algebra()._charpoly_coefficients.set_cache(a) @@ -2487,10 +2484,7 @@ class OctonionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): from mjo.eja.eja_cache import octonion_hermitian_eja_coeffs a = octonion_hermitian_eja_coeffs(self) if a is not None: - if self._rational_algebra is None: - self._charpoly_coefficients.set_cache(a) - else: - self._rational_algebra._charpoly_coefficients.set_cache(a) + self.rational_algebra()._charpoly_coefficients.set_cache(a) class AlbertEJA(OctonionHermitianEJA): @@ -2949,6 +2943,7 @@ class CartesianProductEJA(FiniteDimensionalEJA): sage: from mjo.eja.eja_algebra import (random_eja, ....: CartesianProductEJA, + ....: ComplexHermitianEJA, ....: HadamardEJA, ....: JordanSpinEJA, ....: RealSymmetricEJA) @@ -3060,6 +3055,28 @@ class CartesianProductEJA(FiniteDimensionalEJA): | b2 || 0 | 0 | b2 | +----++----+----+----+ + The "matrix space" of a Cartesian product always consists of + ordered pairs (or triples, or...) whose components are the + matrix spaces of its factors:: + + sage: J1 = HadamardEJA(2) + sage: J2 = ComplexHermitianEJA(2) + sage: J = cartesian_product([J1,J2]) + sage: J.matrix_space() + The Cartesian product of (Full MatrixSpace of 2 by 1 dense + matrices over Algebraic Real Field, Module of 2 by 2 matrices + with entries in Algebraic Field over the scalar ring Algebraic + Real Field) + sage: J.one().to_matrix()[0] + [1] + [1] + sage: J.one().to_matrix()[1] + +---+---+ + | 1 | 0 | + +---+---+ + | 0 | 1 | + +---+---+ + TESTS: All factors must share the same base field:: @@ -3082,7 +3099,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): sage: expected = J.one() # long time sage: actual == expected # long time True - """ def __init__(self, factors, **kwargs): m = len(factors) @@ -3101,8 +3117,13 @@ class CartesianProductEJA(FiniteDimensionalEJA): if associative: category = category.Associative() category = category.join([category, category.CartesianProducts()]) - # Compute my matrix space. This category isn't perfect, but - # is good enough for what we need to do. + # Compute my matrix space. We don't simply use the + # ``cartesian_product()`` functor here because it acts + # differently on SageMath MatrixSpaces and our custom + # MatrixAlgebras, which are CombinatorialFreeModules. We + # always want the result to be represented (and indexed) as an + # ordered tuple. This category isn't perfect, but is good + # enough for what we need to do. MS_cat = MagmaticAlgebras(field).FiniteDimensional().WithBasis() MS_cat = MS_cat.Unital().CartesianProducts() MS_factors = tuple( J.matrix_space() for J in factors ) @@ -3153,6 +3174,8 @@ class CartesianProductEJA(FiniteDimensionalEJA): self._inner_product_matrix = matrix.block_diagonal( [J._inner_product_matrix for J in factors] ) + self._inner_product_matrix._cache = {'hermitian': True} + self._inner_product_matrix.set_immutable() # Building the multiplication table is a bit more tricky # because we have to embed the entries of the factors' @@ -3196,65 +3219,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): return cartesian_product.symbol.join("%s" % factor for factor in self._sets) - def matrix_space(self): - r""" - Return the space that our matrix basis lives in as a Cartesian - product. - - We don't simply use the ``cartesian_product()`` functor here - because it acts differently on SageMath MatrixSpaces and our - custom MatrixAlgebras, which are CombinatorialFreeModules. We - always want the result to be represented (and indexed) as - an ordered tuple. - - SETUP:: - - sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA, - ....: HadamardEJA, - ....: OctonionHermitianEJA, - ....: RealSymmetricEJA) - - EXAMPLES:: - - sage: J1 = HadamardEJA(1) - sage: J2 = RealSymmetricEJA(2) - sage: J = cartesian_product([J1,J2]) - sage: J.matrix_space() - The Cartesian product of (Full MatrixSpace of 1 by 1 dense - matrices over Algebraic Real Field, Full MatrixSpace of 2 - by 2 dense matrices over Algebraic Real Field) - - :: - - sage: J1 = ComplexHermitianEJA(1) - sage: J2 = ComplexHermitianEJA(1) - sage: J = cartesian_product([J1,J2]) - sage: J.one().to_matrix()[0] - +---+ - | 1 | - +---+ - sage: J.one().to_matrix()[1] - +---+ - | 1 | - +---+ - - :: - - sage: J1 = OctonionHermitianEJA(1) - sage: J2 = OctonionHermitianEJA(1) - sage: J = cartesian_product([J1,J2]) - sage: J.one().to_matrix()[0] - +----+ - | e0 | - +----+ - sage: J.one().to_matrix()[1] - +----+ - | e0 | - +----+ - - """ - return super().matrix_space() - @cached_method def cartesian_projection(self, i): @@ -3456,9 +3420,9 @@ class RationalBasisCartesianProductEJA(CartesianProductEJA, SETUP:: - sage: from mjo.eja.eja_algebra import (HadamardEJA, + sage: from mjo.eja.eja_algebra import (FiniteDimensionalEJA, + ....: HadamardEJA, ....: JordanSpinEJA, - ....: OctonionHermitianEJA, ....: RealSymmetricEJA) EXAMPLES: @@ -3479,28 +3443,38 @@ class RationalBasisCartesianProductEJA(CartesianProductEJA, The ``cartesian_product()`` function only uses the first factor to decide where the result will live; thus we have to be careful to - check that all factors do indeed have a `_rational_algebra` member - before we try to access it:: - - sage: J1 = OctonionHermitianEJA(1) # no rational basis - sage: J2 = HadamardEJA(2) - sage: cartesian_product([J1,J2]) - Euclidean Jordan algebra of dimension 1 over Algebraic Real Field - (+) Euclidean Jordan algebra of dimension 2 over Algebraic Real Field - sage: cartesian_product([J2,J1]) - Euclidean Jordan algebra of dimension 2 over Algebraic Real Field - (+) Euclidean Jordan algebra of dimension 1 over Algebraic Real Field + check that all factors do indeed have a ``rational_algebra()`` method + before we construct an algebra that claims to have a rational basis:: + + sage: J1 = HadamardEJA(2) + sage: jp = lambda X,Y: X*Y + sage: ip = lambda X,Y: X[0,0]*Y[0,0] + sage: b1 = matrix(QQ, [[1]]) + sage: J2 = FiniteDimensionalEJA((b1,), jp, ip) + sage: cartesian_product([J2,J1]) # factor one not RationalBasisEJA + Euclidean Jordan algebra of dimension 1 over Algebraic Real + Field (+) Euclidean Jordan algebra of dimension 2 over Algebraic + Real Field + sage: cartesian_product([J1,J2]) # factor one is RationalBasisEJA + Traceback (most recent call last): + ... + ValueError: factor not a RationalBasisEJA """ def __init__(self, algebras, **kwargs): + if not all( hasattr(r, "rational_algebra") for r in algebras ): + raise ValueError("factor not a RationalBasisEJA") + CartesianProductEJA.__init__(self, algebras, **kwargs) - self._rational_algebra = None - if self.vector_space().base_field() is not QQ: - if all( hasattr(r, "_rational_algebra") for r in algebras ): - self._rational_algebra = cartesian_product([ - r._rational_algebra for r in algebras - ]) + @cached_method + def rational_algebra(self): + if self.base_ring() is QQ: + return self + + return cartesian_product([ + r.rational_algebra() for r in self.cartesian_factors() + ]) RationalBasisEJA.CartesianProduct = RationalBasisCartesianProductEJA