From 86ec96a9ff510b4b3d178998d63b0ce9a374c444 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 25 Feb 2021 20:53:41 -0500 Subject: [PATCH] eja: add automatic associativity detection. --- mjo/eja/TODO | 8 ++-- mjo/eja/eja_algebra.py | 84 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/mjo/eja/TODO b/mjo/eja/TODO index 0d6edf2..c92cfc9 100644 --- a/mjo/eja/TODO +++ b/mjo/eja/TODO @@ -15,8 +15,6 @@ sage: a0 = (1/4)*X[4]**2*X[6]**2 - (1/2)*X[2]*X[5]*X[6]**2 - (1/2)*X[3]*X[4]*X[6 15-dimensional QuaternionHermitianAlgebra(3)) to find out why they're so slow. -6. We should compute whether or not the algebra is associative if it - is unknown. I guess the "associative" argument should be ternary - (True, False, None)? We should also figure out the correct - True/False values for the example classes, and of course add an - _is_associative() method. +6. The _rational_algebra for a cartesian product should be a cartesian product. + +7. Use super() where it works. diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index ee34e53..40b4bca 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() @@ -464,7 +493,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 +528,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() @@ -1562,6 +1602,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 +1622,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,9 +1990,14 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False + associative = False + if n <= 1: + associative = True + super(RealSymmetricEJA, self).__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 @@ -2237,9 +2283,14 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False + associative = False + if n <= 1: + associative = True + super(ComplexHermitianEJA, self).__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 @@ -2543,10 +2594,16 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False + associative = False + if n <= 1: + associative = True + super(QuaternionHermitianEJA, self).__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,9 +2829,16 @@ class BilinearFormEJA(ConcreteEJA): n = B.nrows() column_basis = tuple( b.column() for b in FreeModule(ZZ, n).basis() ) + + # TODO: I haven't actually checked this, but it seems legit. + associative = False + if n <= 2: + associative = True + super(BilinearFormEJA, self).__init__(column_basis, jordan_product, inner_product, + associative=associative, **kwargs) # The rank of this algebra is two, unless we're in a @@ -2941,7 +3005,9 @@ class TrivialEJA(ConcreteEJA): super(TrivialEJA, self).__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) -- 2.44.2