X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=cbe5c08a344a95248e0b7c15e94de80b0c662816;hb=c49406021cb8496ead26ef89865f8afbdc96ea6c;hp=ee34e532fa7f4efb3d5aa7d89001f0430f2df52d;hpb=da5bbdc1e0e0a2e020a15b48a3e90d8cee0dfb10;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index ee34e53..cbe5c08 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -112,6 +112,23 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): generally rules out using the rationals as your ``field``, but is required for spectral decompositions. + SETUP:: + + sage: from mjo.eja.eja_algebra import random_eja + + TESTS: + + We should compute that an element subalgebra is associative even + if we circumvent the element method:: + + sage: set_random_seed() + sage: J = random_eja(field=QQ,orthonormalize=False) + sage: x = J.random_element() + sage: A = x.subalgebra_generated_by(orthonormalize=False) + sage: basis = tuple(b.superalgebra_element() for b in A.basis()) + sage: J.subalgebra(basis, orthonormalize=False).is_associative() + True + """ Element = FiniteDimensionalEJAElement @@ -121,7 +138,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): inner_product, field=AA, orthonormalize=True, - associative=False, + associative=None, cartesian_product=False, check_field=True, check_axioms=True, @@ -180,6 +197,18 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): category = MagmaticAlgebras(field).FiniteDimensional() category = category.WithBasis().Unital().Commutative() + if associative is None: + # We should figure it out. As with check_axioms, we have to do + # this without the help of the _jordan_product_is_associative() + # method because we need to know the category before we + # initialize the algebra. + associative = all( jordan_product(jordan_product(bi,bj),bk) + == + jordan_product(bi,jordan_product(bj,bk)) + for bi in basis + for bj in basis + for bk in basis) + if associative: # Element subalgebras can take advantage of this. category = category.Associative() @@ -431,9 +460,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): this algebra was constructed with ``check_axioms=False`` and passed an invalid multiplication table. """ - return all( self.product_on_basis(i,j) == self.product_on_basis(i,j) - for i in range(self.dimension()) - for j in range(self.dimension()) ) + return all( x*y == y*x for x in self.gens() for y in self.gens() ) def _is_jordanian(self): r""" @@ -464,7 +491,8 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): SETUP:: - sage: from mjo.eja.eja_algebra import (RealSymmetricEJA, + sage: from mjo.eja.eja_algebra import (random_eja, + ....: RealSymmetricEJA, ....: ComplexHermitianEJA, ....: QuaternionHermitianEJA) @@ -498,6 +526,16 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): sage: A._jordan_product_is_associative() True + TESTS: + + The values we've presupplied to the constructors agree with + the computation:: + + sage: set_random_seed() + sage: J = random_eja() + sage: J.is_associative() == J._jordan_product_is_associative() + True + """ R = self.base_ring() @@ -891,7 +929,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # And to each subsequent row, prepend an entry that belongs to # the left-side "header column." - M += [ [self.gens()[i]] + [ self.product_on_basis(i,j) + M += [ [self.gens()[i]] + [ self.gens()[i]*self.gens()[j] for j in range(n) ] for i in range(n) ] @@ -1562,6 +1600,13 @@ class RationalBasisEJA(FiniteDimensionalEJA): if not all( all(b_i in QQ for b_i in b.list()) for b in basis ): raise TypeError("basis not rational") + super().__init__(basis, + jordan_product, + inner_product, + field=field, + check_field=check_field, + **kwargs) + self._rational_algebra = None if field is not QQ: # There's no point in constructing the extra algebra if this @@ -1575,17 +1620,11 @@ class RationalBasisEJA(FiniteDimensionalEJA): jordan_product, inner_product, field=QQ, + associative=self.is_associative(), orthonormalize=False, check_field=False, check_axioms=False) - super().__init__(basis, - jordan_product, - inner_product, - field=field, - check_field=check_field, - **kwargs) - @cached_method def _charpoly_coefficients(self): r""" @@ -1949,10 +1988,15 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False - super(RealSymmetricEJA, self).__init__(self._denormalized_basis(n), - self.jordan_product, - self.trace_inner_product, - **kwargs) + associative = False + if n <= 1: + associative = True + + super().__init__(self._denormalized_basis(n), + self.jordan_product, + self.trace_inner_product, + associative=associative, + **kwargs) # TODO: this could be factored out somehow, but is left here # because the MatrixEJA is not presently a subclass of the @@ -2042,7 +2086,7 @@ class ComplexMatrixEJA(MatrixEJA): True """ - super(ComplexMatrixEJA,cls).real_embed(M) + super().real_embed(M) n = M.nrows() # We don't need any adjoined elements... @@ -2089,7 +2133,7 @@ class ComplexMatrixEJA(MatrixEJA): True """ - super(ComplexMatrixEJA,cls).real_unembed(M) + super().real_unembed(M) n = ZZ(M.nrows()) d = cls.dimension_over_reals() F = cls.complex_extension(M.base_ring()) @@ -2237,10 +2281,15 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False - super(ComplexHermitianEJA, self).__init__(self._denormalized_basis(n), - self.jordan_product, - self.trace_inner_product, - **kwargs) + associative = False + if n <= 1: + associative = True + + super().__init__(self._denormalized_basis(n), + self.jordan_product, + self.trace_inner_product, + associative=associative, + **kwargs) # TODO: this could be factored out somehow, but is left here # because the MatrixEJA is not presently a subclass of the # FDEJA class that defines rank() and one(). @@ -2323,7 +2372,7 @@ class QuaternionMatrixEJA(MatrixEJA): True """ - super(QuaternionMatrixEJA,cls).real_embed(M) + super().real_embed(M) quaternions = M.base_ring() n = M.nrows() @@ -2378,7 +2427,7 @@ class QuaternionMatrixEJA(MatrixEJA): True """ - super(QuaternionMatrixEJA,cls).real_unembed(M) + super().real_unembed(M) n = ZZ(M.nrows()) d = cls.dimension_over_reals() @@ -2543,10 +2592,16 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False - super(QuaternionHermitianEJA, self).__init__(self._denormalized_basis(n), - self.jordan_product, - self.trace_inner_product, - **kwargs) + associative = False + if n <= 1: + associative = True + + super().__init__(self._denormalized_basis(n), + self.jordan_product, + self.trace_inner_product, + associative=associative, + **kwargs) + # TODO: this could be factored out somehow, but is left here # because the MatrixEJA is not presently a subclass of the # FDEJA class that defines rank() and one(). @@ -2772,10 +2827,17 @@ class BilinearFormEJA(ConcreteEJA): n = B.nrows() column_basis = tuple( b.column() for b in FreeModule(ZZ, n).basis() ) - super(BilinearFormEJA, self).__init__(column_basis, - jordan_product, - inner_product, - **kwargs) + + # TODO: I haven't actually checked this, but it seems legit. + associative = False + if n <= 2: + associative = True + + super().__init__(column_basis, + jordan_product, + inner_product, + associative=associative, + **kwargs) # The rank of this algebra is two, unless we're in a # one-dimensional ambient space (because the rank is bounded @@ -2880,7 +2942,7 @@ class JordanSpinEJA(BilinearFormEJA): # But also don't pass check_field=False here, because the user # can pass in a field! - super(JordanSpinEJA, self).__init__(B, **kwargs) + super().__init__(B, **kwargs) @staticmethod def _max_random_instance_size(): @@ -2938,10 +3000,12 @@ class TrivialEJA(ConcreteEJA): if "orthonormalize" not in kwargs: kwargs["orthonormalize"] = False if "check_axioms" not in kwargs: kwargs["check_axioms"] = False - super(TrivialEJA, self).__init__(basis, - jordan_product, - inner_product, - **kwargs) + super().__init__(basis, + jordan_product, + inner_product, + associative=True, + **kwargs) + # The rank is zero using my definition, namely the dimension of the # largest subalgebra generated by any element. self.rank.set_cache(0) @@ -3132,6 +3196,65 @@ class CartesianProductEJA(CombinatorialFreeModule_CartesianProduct, self.one.set_cache(self._cartesian_product_of_elements(ones)) self.rank.set_cache(sum(J.rank() for J in algebras)) + def _monomial_to_generator(self, mon): + r""" + Convert a monomial index into a generator index. + + SETUP:: + + sage: from mjo.eja.eja_algebra import random_eja() + + TESTS:: + + sage: J1 = random_eja(field=QQ, orthonormalize=False) + sage: J2 = random_eja(field=QQ, orthonormalize=False) + sage: J = cartesian_product([J1,J2]) + sage: all( J.monomial(m) + ....: == + ....: J.gens()[J._monomial_to_generator(m)] + ....: for m in J.basis().keys() ) + + """ + # The superclass method indexes into a matrix, so we have to + # turn the tuples i and j into integers. This is easy enough + # given that the first coordinate of i and j corresponds to + # the factor, and the second coordinate corresponds to the + # index of the generator within that factor. + factor = mon[0] + idx_in_factor = mon[1] + + offset = sum( f.dimension() + for f in self.cartesian_factors()[:factor] ) + return offset + idx_in_factor + + def product_on_basis(self, i, j): + r""" + Return the product of the monomials indexed by ``i`` and ``j``. + + This overrides the superclass method because here, both ``i`` + and ``j`` will be ordered pairs. + + SETUP:: + + sage: from mjo.eja.eja_algebra import (QuaternionHermitianEJA, + ....: RealSymmetricEJA) + + TESTS:: + + sage: J1 = RealSymmetricEJA(1,field=QQ) + sage: J2 = QuaternionHermitianEJA(1,field=QQ) + sage: J = cartesian_product([J1,J2]) + sage: x = sum(J.gens()) + sage: x == J.one() + True + sage: x*x == x + True + + """ + l = self._monomial_to_generator(i) + m = self._monomial_to_generator(j) + return FiniteDimensionalEJA.product_on_basis(self, l, m) + def matrix_space(self): r""" Return the space that our matrix basis lives in as a Cartesian