X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=ae41b8c6e12415d4b940958ca3798749a4e8c7bf;hb=e05c37cc70d3ca1a3df761d92e46e93490716063;hp=5910221ef8f8807dce7fee30eeb2da9045114399;hpb=3f1bc1ad064d41c956b9034edb950e6dbd8eb585;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 5910221..ae41b8c 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -737,7 +737,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): if self.is_trivial(): return MatrixSpace(self.base_ring(), 0) else: - return self._matrix_basis[0].matrix_space() + return self.matrix_basis()[0].parent() @cached_method @@ -1667,6 +1667,38 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): class ComplexMatrixEJA(MatrixEJA): + # A manual dictionary-cache for the complex_extension() method, + # since apparently @classmethods can't also be @cached_methods. + _complex_extension = {} + + @classmethod + def complex_extension(cls,field): + r""" + The complex field that we embed/unembed, as an extension + of the given ``field``. + """ + if field in cls._complex_extension: + return cls._complex_extension[field] + + # Sage doesn't know how to adjoin the complex "i" (the root of + # x^2 + 1) to a field in a general way. Here, we just enumerate + # all of the cases that I have cared to support so far. + if field is AA: + # Sage doesn't know how to embed AA into QQbar, i.e. how + # to adjoin sqrt(-1) to AA. + F = QQbar + elif not field.is_exact(): + # RDF or RR + F = field.complex_field() + else: + # Works for QQ and... maybe some other fields. + R = PolynomialRing(field, 'z') + z = R.gen() + F = field.extension(z**2 + 1, 'I', embedding=CLF(-1).sqrt()) + + cls._complex_extension[field] = F + return F + @staticmethod def dimension_over_reals(): return 2 @@ -1721,9 +1753,10 @@ class ComplexMatrixEJA(MatrixEJA): blocks = [] for z in M.list(): - a = z.list()[0] # real part, I guess - b = z.list()[1] # imag part, I guess - blocks.append(matrix(field, 2, [[a,b],[-b,a]])) + a = z.real() + b = z.imag() + blocks.append(matrix(field, 2, [ [ a, b], + [-b, a] ])) return matrix.block(field, n, blocks) @@ -1762,26 +1795,7 @@ class ComplexMatrixEJA(MatrixEJA): super(ComplexMatrixEJA,cls).real_unembed(M) n = ZZ(M.nrows()) d = cls.dimension_over_reals() - - # If "M" was normalized, its base ring might have roots - # adjoined and they can stick around after unembedding. - field = M.base_ring() - R = PolynomialRing(field, 'z') - z = R.gen() - - # Sage doesn't know how to adjoin the complex "i" (the root of - # x^2 + 1) to a field in a general way. Here, we just enumerate - # all of the cases that I have cared to support so far. - if field is AA: - # Sage doesn't know how to embed AA into QQbar, i.e. how - # to adjoin sqrt(-1) to AA. - F = QQbar - elif not field.is_exact(): - # RDF or RR - F = field.complex_field() - else: - # Works for QQ and... maybe some other fields. - F = field.extension(z**2 + 1, 'I', embedding=CLF(-1).sqrt()) + F = cls.complex_extension(M.base_ring()) i = F.gen() # Go top-left to bottom-right (reading order), converting every @@ -1950,6 +1964,25 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): return cls(n, **kwargs) class QuaternionMatrixEJA(MatrixEJA): + + # A manual dictionary-cache for the quaternion_extension() method, + # since apparently @classmethods can't also be @cached_methods. + _quaternion_extension = {} + + @classmethod + def quaternion_extension(cls,field): + r""" + The quaternion field that we embed/unembed, as an extension + of the given ``field``. + """ + if field in cls._quaternion_extension: + return cls._quaternion_extension[field] + + Q = QuaternionAlgebra(field,-1,-1) + + cls._quaternion_extension[field] = Q + return Q + @staticmethod def dimension_over_reals(): return 4 @@ -2054,8 +2087,7 @@ class QuaternionMatrixEJA(MatrixEJA): # Use the base ring of the matrix to ensure that its entries can be # multiplied by elements of the quaternion algebra. - field = M.base_ring() - Q = QuaternionAlgebra(field,-1,-1) + Q = cls.quaternion_extension(M.base_ring()) i,j,k = Q.gens() # Go top-left to bottom-right (reading order), converting every @@ -2620,100 +2652,84 @@ class TrivialEJA(ConcreteEJA): # inappropriate for us. return cls(**kwargs) -# class DirectSumEJA(ConcreteEJA): -# 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 - -# 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() - -# self._factors = (J1, J2) -# n1 = J1.dimension() -# n2 = J2.dimension() -# n = n1+n2 -# V = VectorSpace(field, n) -# mult_table = [ [ V.zero() for j in range(i+1) ] -# for i in range(n) ] -# for i in range(n1): -# for j in range(i+1): -# p = (J1.monomial(i)*J1.monomial(j)).to_vector() -# mult_table[i][j] = V(p.list() + [field.zero()]*n2) - -# for i in range(n2): -# for j in range(i+1): -# p = (J2.monomial(i)*J2.monomial(j)).to_vector() -# mult_table[n1+i][n1+j] = V([field.zero()]*n1 + p.list()) - -# # TODO: build the IP table here from the two constituent IP -# # matrices (it'll be block diagonal, I think). -# ip_table = [ [ field.zero() for j in range(i+1) ] -# for i in range(n) ] -# super(DirectSumEJA, self).__init__(field, -# mult_table, -# ip_table, -# check_axioms=False, -# **kwargs) -# self.rank.set_cache(J1.rank() + J2.rank()) - - -# def factors(self): -# r""" -# Return the pair of this algebra's factors. +class DirectSumEJA(ConcreteEJA): + 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:: + SETUP:: -# sage: from mjo.eja.eja_algebra import (HadamardEJA, -# ....: JordanSpinEJA, -# ....: DirectSumEJA) + sage: from mjo.eja.eja_algebra import (random_eja, + ....: HadamardEJA, + ....: RealSymmetricEJA, + ....: DirectSumEJA) -# EXAMPLES:: + EXAMPLES:: -# sage: J1 = HadamardEJA(2, field=QQ) -# sage: J2 = JordanSpinEJA(3, field=QQ) -# sage: J = DirectSumEJA(J1,J2) -# sage: J.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(3) + sage: J = DirectSumEJA(J1,J2) + sage: J.dimension() + 8 + sage: J.rank() + 5 -# """ -# return self._factors + 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() + self._factors = (J1, J2) + basis = tuple( (a,b) for a in J1.basis() for b in J2.basis() ) + + def jordan_product(x,y): + return (x[0]*y[0], x[1]*y[1]) + + def inner_product(x,y): + return x[0].inner_product(y[0]) + x[1].inner_product(y[1]) + + super().__init__(basis, jordan_product, inner_product) + + self.rank.set_cache(J1.rank() + J2.rank()) + + + def 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.factors() + (Euclidean Jordan algebra of dimension 2 over Rational Field, + Euclidean Jordan algebra of dimension 3 over Rational Field) + + """ + return self._factors # def projections(self): # r"""