X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=afe0a677aaafd9ddf67355ed979d5ef023beb9c0;hb=0d7746bc8dbe22bd5ce4ece76354e34454eda5d2;hp=4d0c802c38c8320a8f10f8faeb50678a85d84e95;hpb=ff8c9b19da5ed821366a491a95b4f6c946f315ae;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 4d0c802..afe0a67 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -1725,6 +1725,21 @@ class ConcreteEJA(RationalBasisEJA): class MatrixEJA: + @staticmethod + def jordan_product(X,Y): + return (X*Y + Y*X)/2 + + @staticmethod + def trace_inner_product(X,Y): + r""" + A trace inner-product for matrices that aren't embedded in the + reals. + """ + # We take the norm (absolute value) because Octonions() isn't + # smart enough yet to coerce its one() into the base field. + return (X*Y).trace().real().abs() + +class RealEmbeddedMatrixEJA(MatrixEJA): @staticmethod def dimension_over_reals(): r""" @@ -1770,9 +1785,6 @@ class MatrixEJA: raise ValueError("the matrix 'M' must be a real embedding") return M - @staticmethod - def jordan_product(X,Y): - return (X*Y + Y*X)/2 @classmethod def trace_inner_product(cls,X,Y): @@ -1781,29 +1793,11 @@ class MatrixEJA: SETUP:: - sage: from mjo.eja.eja_algebra import (RealSymmetricEJA, - ....: ComplexHermitianEJA, + sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA, ....: QuaternionHermitianEJA) EXAMPLES:: - This gives the same answer as it would if we computed the trace - from the unembedded (original) matrices:: - - sage: set_random_seed() - sage: J = RealSymmetricEJA.random_instance() - sage: x,y = J.random_elements(2) - sage: Xe = x.to_matrix() - sage: Ye = y.to_matrix() - sage: X = J.real_unembed(Xe) - sage: Y = J.real_unembed(Ye) - sage: expected = (X*Y).trace() - sage: actual = J.trace_inner_product(Xe,Ye) - sage: actual == expected - True - - :: - sage: set_random_seed() sage: J = ComplexHermitianEJA.random_instance() sage: x,y = J.random_elements(2) @@ -1839,14 +1833,7 @@ class MatrixEJA: # as a REAL matrix will be 2*a = 2*Re(z_1). And so forth. return (X*Y).trace()/cls.dimension_over_reals() - -class RealMatrixEJA(MatrixEJA): - @staticmethod - def dimension_over_reals(): - return 1 - - -class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): +class RealSymmetricEJA(ConcreteEJA, MatrixEJA): """ The rank-n simple EJA consisting of real symmetric n-by-n matrices, the usual symmetric Jordan product, and the trace inner @@ -1975,12 +1962,12 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): # because the MatrixEJA is not presently a subclass of the # FDEJA class that defines rank() and one(). self.rank.set_cache(n) - idV = matrix.identity(ZZ, self.dimension_over_reals()*n) + idV = self.matrix_space().one() self.one.set_cache(self(idV)) -class ComplexMatrixEJA(MatrixEJA): +class ComplexMatrixEJA(RealEmbeddedMatrixEJA): # A manual dictionary-cache for the complex_extension() method, # since apparently @classmethods can't also be @cached_methods. _complex_extension = {} @@ -2282,7 +2269,7 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): n = ZZ.random_element(cls._max_random_instance_size() + 1) return cls(n, **kwargs) -class QuaternionMatrixEJA(MatrixEJA): +class QuaternionMatrixEJA(RealEmbeddedMatrixEJA): # A manual dictionary-cache for the quaternion_extension() method, # since apparently @classmethods can't also be @cached_methods. @@ -2598,6 +2585,156 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): n = ZZ.random_element(cls._max_random_instance_size() + 1) return cls(n, **kwargs) +class OctonionHermitianEJA(FiniteDimensionalEJA, MatrixEJA): + r""" + SETUP:: + + sage: from mjo.eja.eja_algebra import (FiniteDimensionalEJA, + ....: OctonionHermitianEJA) + + EXAMPLES: + + The 3-by-3 algebra satisfies the axioms of an EJA:: + + sage: OctonionHermitianEJA(3, # long time + ....: field=QQ, # long time + ....: orthonormalize=False, # long time + ....: check_axioms=True) # long time + Euclidean Jordan algebra of dimension 27 over Rational Field + + After a change-of-basis, the 2-by-2 algebra has the same + multiplication table as the ten-dimensional Jordan spin algebra:: + + sage: b = OctonionHermitianEJA._denormalized_basis(2,QQ) + sage: basis = (b[0] + b[9],) + b[1:9] + (b[0] - b[9],) + sage: jp = OctonionHermitianEJA.jordan_product + sage: ip = OctonionHermitianEJA.trace_inner_product + sage: J = FiniteDimensionalEJA(basis, + ....: jp, + ....: ip, + ....: field=QQ, + ....: orthonormalize=False) + sage: J.multiplication_table() + +----++----+----+----+----+----+----+----+----+----+----+ + | * || b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 | + +====++====+====+====+====+====+====+====+====+====+====+ + | b0 || b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b1 || b1 | b0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b2 || b2 | 0 | b0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b3 || b3 | 0 | 0 | b0 | 0 | 0 | 0 | 0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b4 || b4 | 0 | 0 | 0 | b0 | 0 | 0 | 0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b5 || b5 | 0 | 0 | 0 | 0 | b0 | 0 | 0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b6 || b6 | 0 | 0 | 0 | 0 | 0 | b0 | 0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b7 || b7 | 0 | 0 | 0 | 0 | 0 | 0 | b0 | 0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b8 || b8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | b0 | 0 | + +----++----+----+----+----+----+----+----+----+----+----+ + | b9 || b9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | b0 | + +----++----+----+----+----+----+----+----+----+----+----+ + + TESTS: + + We can actually construct the 27-dimensional Albert algebra, + and we get the right unit element if we recompute it:: + + sage: J = OctonionHermitianEJA(3, # long time + ....: field=QQ, # long time + ....: orthonormalize=False) # long time + sage: J.one.clear_cache() # long time + sage: J.one() # long time + b0 + b9 + b26 + sage: J.one().to_matrix() # long time + +----+----+----+ + | e0 | 0 | 0 | + +----+----+----+ + | 0 | e0 | 0 | + +----+----+----+ + | 0 | 0 | e0 | + +----+----+----+ + + The 2-by-2 algebra is isomorphic to the ten-dimensional Jordan + spin algebra, but just to be sure, we recompute its rank:: + + sage: J = OctonionHermitianEJA(2, # long time + ....: field=QQ, # long time + ....: orthonormalize=False) # long time + sage: J.rank.clear_cache() # long time + sage: J.rank() # long time + 2 + """ + def __init__(self, n, field=AA, **kwargs): + if n > 3: + # Otherwise we don't get an EJA. + raise ValueError("n cannot exceed 3") + + # We know this is a valid EJA, but will double-check + # if the user passes check_axioms=True. + if "check_axioms" not in kwargs: kwargs["check_axioms"] = False + + super().__init__(self._denormalized_basis(n,field), + self.jordan_product, + self.trace_inner_product, + field=field, + **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(). + self.rank.set_cache(n) + idV = self.matrix_space().one() + self.one.set_cache(self(idV)) + + + @classmethod + def _denormalized_basis(cls, n, field): + """ + Returns a basis for the space of octonion Hermitian n-by-n + matrices. + + SETUP:: + + sage: from mjo.eja.eja_algebra import OctonionHermitianEJA + + EXAMPLES:: + + sage: B = OctonionHermitianEJA._denormalized_basis(3,QQ) + sage: all( M.is_hermitian() for M in B ) + True + sage: len(B) + 27 + + """ + from mjo.octonions import OctonionMatrixAlgebra + MS = OctonionMatrixAlgebra(n, scalars=field) + es = MS.entry_algebra().gens() + + basis = [] + for i in range(n): + for j in range(i+1): + if i == j: + E_ii = MS.monomial( (i,j,es[0]) ) + basis.append(E_ii) + else: + for e in es: + E_ij = MS.monomial( (i,j,e) ) + ec = e.conjugate() + # If the conjugate has a negative sign in front + # of it, (j,i,ec) won't be a monomial! + if (j,i,ec) in MS.indices(): + E_ij += MS.monomial( (j,i,ec) ) + else: + E_ij -= MS.monomial( (j,i,-ec) ) + basis.append(E_ij) + + return tuple( basis ) + class HadamardEJA(ConcreteEJA): """