From: Michael Orlitzky Date: Mon, 5 Aug 2019 18:06:24 +0000 (-0400) Subject: eja: ensure that Sage doesn't think EJAs are associative. X-Git-Url: http://gitweb.michael.orlitzky.com/?p=sage.d.git;a=commitdiff_plain;h=17aee61574caf7f62a70d181840c2be69879a3e7 eja: ensure that Sage doesn't think EJAs are associative. It turns out that the FiniteDimensionalAlgebrasWithBasis category somehow has both the legacy Algebras() category and the newer MagmaticAlgebras() category as super-categories. Problem is, the legacy one is associative! To fix that, we now use MagmaticAlgebras directly. Of course, we have to reimplement some of the stuff that was done for us before... and we have to add a bunch of hacks for parts of Sage that break when you don't have a ring.... and we can't use a matrix for our multiplication table any more. But it was all doable. --- diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 28e86c2..8c66de6 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -6,11 +6,12 @@ what can be supported in a general Jordan Algebra. """ from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra -from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis +from sage.categories.magmatic_algebras import MagmaticAlgebras from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.prandom import choice +from sage.misc.table import table from sage.modules.free_module import VectorSpace from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import QuadraticField @@ -22,6 +23,14 @@ from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement from mjo.eja.eja_utils import _mat2vec class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): + # This is an ugly hack needed to prevent the category framework + # from implementing a coercion from our base ring (e.g. the + # rationals) into the algebra. First of all -- such a coercion is + # nonsense to begin with. But more importantly, it tries to do so + # in the category of rings, and since our algebras aren't + # associative they generally won't be rings. + _no_generic_basering_coercion = True + def __init__(self, field, mult_table, @@ -50,7 +59,9 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): self._natural_basis = natural_basis if category is None: - category = FiniteDimensionalAlgebrasWithBasis(field).Unital() + category = MagmaticAlgebras(field).FiniteDimensional() + category = category.WithBasis().Unital() + fda = super(FiniteDimensionalEuclideanJordanAlgebra, self) fda.__init__(field, range(len(mult_table)), @@ -117,6 +128,11 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): True """ + if elt == 0: + # The superclass implementation of random_element() + # needs to be able to coerce "0" into the algebra. + return self.zero() + natural_basis = self.natural_basis() if elt not in natural_basis[0].matrix_space(): raise ValueError("not a naturally-represented algebra element") @@ -381,37 +397,36 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): def multiplication_table(self): """ - Return a readable matrix representation of this algebra's - multiplication table. The (i,j)th entry in the matrix contains - the product of the ith basis element with the jth. - - This is not extraordinarily useful, but it overrides a superclass - method that would otherwise just crash and complain about the - algebra being infinite. + Return a visual representation of this algebra's multiplication + table (on basis elements). SETUP:: - sage: from mjo.eja.eja_algebra import (JordanSpinEJA, - ....: RealCartesianProductEJA) + sage: from mjo.eja.eja_algebra import JordanSpinEJA EXAMPLES:: - sage: J = RealCartesianProductEJA(3) - sage: J.multiplication_table() - [e0 0 0] - [ 0 e1 0] - [ 0 0 e2] - - :: - - sage: J = JordanSpinEJA(3) + sage: J = JordanSpinEJA(4) sage: J.multiplication_table() - [e0 e1 e2] - [e1 e0 0] - [e2 0 e0] + +----++----+----+----+----+ + | * || e0 | e1 | e2 | e3 | + +====++====+====+====+====+ + | e0 || e0 | e1 | e2 | e3 | + +----++----+----+----+----+ + | e1 || e1 | e0 | 0 | 0 | + +----++----+----+----+----+ + | e2 || e2 | 0 | e0 | 0 | + +----++----+----+----+----+ + | e3 || e3 | 0 | 0 | e0 | + +----++----+----+----+----+ """ - return matrix(self._multiplication_table) + M = list(self._multiplication_table) # copy + for i in range(len(M)): + # M had better be "square" + M[i] = [self.monomial(i)] + M[i] + M = [["*"] + list(self.gens())] + M + return table(M, header_row=True, header_column=True, frame=True) def natural_basis(self): diff --git a/mjo/eja/eja_element.py b/mjo/eja/eja_element.py index e38012e..287a217 100644 --- a/mjo/eja/eja_element.py +++ b/mjo/eja/eja_element.py @@ -490,7 +490,9 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(IndexedFreeModuleElement): False """ - zero = self.parent().zero() + # In fact, we only need to know if the constant term is non-zero, + # so we can pass in the field's zero element instead. + zero = self.base_ring().zero() p = self.minimal_polynomial() return not (p(zero) == zero) @@ -801,10 +803,12 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(IndexedFreeModuleElement): """ P = self.parent() + left_mult_by_self = lambda y: self*y + L = P.module_morphism(function=left_mult_by_self, codomain=P) return FiniteDimensionalEuclideanJordanAlgebraOperator( P, P, - self.to_matrix() ) + L.matrix() ) def quadratic_representation(self, other=None): diff --git a/mjo/eja/eja_subalgebra.py b/mjo/eja/eja_subalgebra.py index 7451e47..a3ad92a 100644 --- a/mjo/eja/eja_subalgebra.py +++ b/mjo/eja/eja_subalgebra.py @@ -157,6 +157,12 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide :: """ + if elt == 0: + # Just as in the superalgebra class, we need to hack + # this special case to ensure that random_element() can + # coerce a ring zero into the algebra. + return self.zero() + if elt in self.superalgebra(): coords = self.vector_space().coordinate_vector(elt.to_vector()) return self.from_vector(coords)