From ad25c5b8995a1cacefbf4d677316b9e7069521ff Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Nov 2020 22:19:20 -0500 Subject: [PATCH] eja: begin generalizing the charpoly-over-QQ optimizations. As a very simple first step, encode the fact that HadamardEJA and BilinearFormEJA have rational bases within the type system. This allows us to compute their charpolys over the rationals instead of whatever their user-given base ring is. --- mjo/eja/eja_algebra.py | 191 +++++++++++++++++++++++++---------------- 1 file changed, 115 insertions(+), 76 deletions(-) diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 26fe192..1fc3618 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -1007,81 +1007,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): Element = FiniteDimensionalEuclideanJordanAlgebraElement -class HadamardEJA(FiniteDimensionalEuclideanJordanAlgebra): - """ - Return the Euclidean Jordan Algebra corresponding to the set - `R^n` under the Hadamard product. - - Note: this is nothing more than the Cartesian product of ``n`` - copies of the spin algebra. Once Cartesian product algebras - are implemented, this can go. - - SETUP:: - - sage: from mjo.eja.eja_algebra import HadamardEJA - - EXAMPLES: - - This multiplication table can be verified by hand:: - - sage: J = HadamardEJA(3) - sage: e0,e1,e2 = J.gens() - sage: e0*e0 - e0 - sage: e0*e1 - 0 - sage: e0*e2 - 0 - sage: e1*e1 - e1 - sage: e1*e2 - 0 - sage: e2*e2 - e2 - - TESTS: - - We can change the generator prefix:: - - sage: HadamardEJA(3, prefix='r').gens() - (r0, r1, r2) - - """ - def __init__(self, n, field=AA, **kwargs): - V = VectorSpace(field, n) - mult_table = [ [ V.gen(i)*(i == j) for j in range(n) ] - for i in range(n) ] - - super(HadamardEJA, self).__init__(field, - mult_table, - check_axioms=False, - **kwargs) - self.rank.set_cache(n) - - def inner_product(self, x, y): - """ - Faster to reimplement than to use natural representations. - - SETUP:: - - sage: from mjo.eja.eja_algebra import HadamardEJA - - TESTS: - - Ensure that this is the usual inner product for the algebras - over `R^n`:: - - sage: set_random_seed() - sage: J = HadamardEJA.random_instance() - sage: x,y = J.random_elements(2) - sage: X = x.natural_representation() - sage: Y = y.natural_representation() - sage: x.inner_product(y) == J.natural_inner_product(X,Y) - True - - """ - return x.to_vector().inner_product(y.to_vector()) - def random_eja(field=AA): """ @@ -1108,6 +1033,44 @@ def random_eja(field=AA): +class RationalBasisEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): + r""" + Algebras whose basis consists of vectors with rational + entries. Equivalently, algebras whose multiplication tables + contain only rational coefficients. + + When an EJA has a basis that can be made rational, we can speed up + the computation of its characteristic polynomial by doing it over + ``QQ``. All of the named EJA constructors that we provide fall + into this category. + """ + @cached_method + def _charpoly_coefficients(self): + r""" + Override the parent method with something that tries to compute + over a faster (non-extension) field. + """ + if self.base_ring() is QQ: + # There's no need to construct *another* algebra over the + # rationals if this one is already over the rationals. + superclass = super(RationalBasisEuclideanJordanAlgebra, self) + return superclass._charpoly_coefficients() + + mult_table = tuple( + map(lambda x: x.to_vector(), ls) + for ls in self._multiplication_table + ) + + # Do the computation over the rationals. The answer will be + # the same, because our basis coordinates are (essentially) + # rational. + J = FiniteDimensionalEuclideanJordanAlgebra(QQ, + mult_table, + check_field=False, + check_axioms=False) + return J._charpoly_coefficients() + + class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): @staticmethod def _max_test_case_size(): @@ -2021,7 +1984,83 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra): self.rank.set_cache(n) -class BilinearFormEJA(FiniteDimensionalEuclideanJordanAlgebra): +class HadamardEJA(RationalBasisEuclideanJordanAlgebra): + """ + Return the Euclidean Jordan Algebra corresponding to the set + `R^n` under the Hadamard product. + + Note: this is nothing more than the Cartesian product of ``n`` + copies of the spin algebra. Once Cartesian product algebras + are implemented, this can go. + + SETUP:: + + sage: from mjo.eja.eja_algebra import HadamardEJA + + EXAMPLES: + + This multiplication table can be verified by hand:: + + sage: J = HadamardEJA(3) + sage: e0,e1,e2 = J.gens() + sage: e0*e0 + e0 + sage: e0*e1 + 0 + sage: e0*e2 + 0 + sage: e1*e1 + e1 + sage: e1*e2 + 0 + sage: e2*e2 + e2 + + TESTS: + + We can change the generator prefix:: + + sage: HadamardEJA(3, prefix='r').gens() + (r0, r1, r2) + + """ + def __init__(self, n, field=AA, **kwargs): + V = VectorSpace(field, n) + mult_table = [ [ V.gen(i)*(i == j) for j in range(n) ] + for i in range(n) ] + + super(HadamardEJA, self).__init__(field, + mult_table, + check_axioms=False, + **kwargs) + self.rank.set_cache(n) + + def inner_product(self, x, y): + """ + Faster to reimplement than to use natural representations. + + SETUP:: + + sage: from mjo.eja.eja_algebra import HadamardEJA + + TESTS: + + Ensure that this is the usual inner product for the algebras + over `R^n`:: + + sage: set_random_seed() + sage: J = HadamardEJA.random_instance() + sage: x,y = J.random_elements(2) + sage: X = x.natural_representation() + sage: Y = y.natural_representation() + sage: x.inner_product(y) == J.natural_inner_product(X,Y) + True + + """ + return x.to_vector().inner_product(y.to_vector()) + + +class BilinearFormEJA(RationalBasisEuclideanJordanAlgebra): r""" The rank-2 simple EJA consisting of real vectors ``x=(x0, x_bar)`` with the half-trace inner product and jordan product ``x*y = -- 2.43.2