X-Git-Url: http://gitweb.michael.orlitzky.com/?p=sage.d.git;a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=5bf597565b2ae292032c3f0a532763d7889cd2a8;hp=c55061e34fb2b7a4d04f127d74148984ce21f9fa;hb=db1f7761ebf564221669137ae07476ea45d82a2c;hpb=f98ab4d7afa92a853e7ddc75cdac803d2da4fcb9 diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index c55061e..5bf5975 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -152,11 +152,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # we've specified a real embedding. raise ValueError("scalar field is not real") - from mjo.eja.eja_utils import _change_ring - # If the basis given to us wasn't over the field that it's - # supposed to be over, fix that. Or, you know, crash. - basis = tuple( _change_ring(b, field) for b in basis ) - if check_axioms: # Check commutativity of the Jordan and inner-products. # This has to be done before we build the multiplication @@ -1730,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().abs() + +class RealEmbeddedMatrixEJA(MatrixEJA): @staticmethod def dimension_over_reals(): r""" @@ -1775,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): @@ -1786,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) @@ -1844,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 @@ -1917,7 +1899,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): """ @classmethod - def _denormalized_basis(cls, n): + def _denormalized_basis(cls, n, field): """ Return a basis for the space of real symmetric n-by-n matrices. @@ -1929,7 +1911,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): sage: set_random_seed() sage: n = ZZ.random_element(1,5) - sage: B = RealSymmetricEJA._denormalized_basis(n) + sage: B = RealSymmetricEJA._denormalized_basis(n,ZZ) sage: all( M.is_symmetric() for M in B) True @@ -1939,7 +1921,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): S = [] for i in range(n): for j in range(i+1): - Eij = matrix(ZZ, n, lambda k,l: k==i and l==j) + Eij = matrix(field, n, lambda k,l: k==i and l==j) if i == j: Sij = Eij else: @@ -1960,7 +1942,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): n = ZZ.random_element(cls._max_random_instance_size() + 1) return cls(n, **kwargs) - def __init__(self, n, **kwargs): + def __init__(self, n, field=AA, **kwargs): # 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 @@ -1969,9 +1951,10 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA): if n <= 1: associative = True - super().__init__(self._denormalized_basis(n), + super().__init__(self._denormalized_basis(n,field), self.jordan_product, self.trace_inner_product, + field=field, associative=associative, **kwargs) @@ -1979,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 = {} @@ -2191,7 +2174,7 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): """ @classmethod - def _denormalized_basis(cls, n): + def _denormalized_basis(cls, n, field): """ Returns a basis for the space of complex Hermitian n-by-n matrices. @@ -2209,15 +2192,14 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): sage: set_random_seed() sage: n = ZZ.random_element(1,5) - sage: B = ComplexHermitianEJA._denormalized_basis(n) + sage: B = ComplexHermitianEJA._denormalized_basis(n,ZZ) sage: all( M.is_symmetric() for M in B) True """ - field = ZZ - R = PolynomialRing(field, 'z') + R = PolynomialRing(ZZ, 'z') z = R.gen() - F = field.extension(z**2 + 1, 'I') + F = ZZ.extension(z**2 + 1, 'I') I = F.gen(1) # This is like the symmetric case, but we need to be careful: @@ -2248,12 +2230,12 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): # "erase" E_ij Eij[i,j] = 0 - # Since we embedded these, we can drop back to the "field" that we - # started with instead of the complex extension "F". + # Since we embedded the entries, we can drop back to the + # desired real "field" instead of the extension "F". return tuple( s.change_ring(field) for s in S ) - def __init__(self, n, **kwargs): + def __init__(self, n, field=AA, **kwargs): # 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 @@ -2262,9 +2244,10 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA): if n <= 1: associative = True - super().__init__(self._denormalized_basis(n), + super().__init__(self._denormalized_basis(n,field), self.jordan_product, self.trace_inner_product, + field=field, associative=associative, **kwargs) # TODO: this could be factored out somehow, but is left here @@ -2286,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. @@ -2492,7 +2475,7 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): """ @classmethod - def _denormalized_basis(cls, n): + def _denormalized_basis(cls, n, field): """ Returns a basis for the space of quaternion Hermitian n-by-n matrices. @@ -2510,12 +2493,11 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): sage: set_random_seed() sage: n = ZZ.random_element(1,5) - sage: B = QuaternionHermitianEJA._denormalized_basis(n) + sage: B = QuaternionHermitianEJA._denormalized_basis(n,ZZ) sage: all( M.is_symmetric() for M in B ) True """ - field = ZZ Q = QuaternionAlgebra(QQ,-1,-1) I,J,K = Q.gens() @@ -2559,12 +2541,12 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): # "erase" E_ij Eij[i,j] = 0 - # Since we embedded these, we can drop back to the "field" that we - # started with instead of the quaternion algebra "Q". + # Since we embedded the entries, we can drop back to the + # desired real "field" instead of the quaternion algebra "Q". return tuple( s.change_ring(field) for s in S ) - def __init__(self, n, **kwargs): + def __init__(self, n, field=AA, **kwargs): # 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 @@ -2573,9 +2555,10 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA): if n <= 1: associative = True - super().__init__(self._denormalized_basis(n), + super().__init__(self._denormalized_basis(n,field), self.jordan_product, self.trace_inner_product, + field=field, associative=associative, **kwargs) @@ -2643,7 +2626,7 @@ class HadamardEJA(ConcreteEJA): (r0, r1, r2) """ - def __init__(self, n, **kwargs): + def __init__(self, n, field=AA, **kwargs): if n == 0: jordan_product = lambda x,y: x inner_product = lambda x,y: x @@ -2664,10 +2647,12 @@ class HadamardEJA(ConcreteEJA): if "orthonormalize" not in kwargs: kwargs["orthonormalize"] = False if "check_axioms" not in kwargs: kwargs["check_axioms"] = False - column_basis = tuple( b.column() for b in FreeModule(ZZ, n).basis() ) + column_basis = tuple( b.column() + for b in FreeModule(field, n).basis() ) super().__init__(column_basis, jordan_product, inner_product, + field=field, associative=True, **kwargs) self.rank.set_cache(n) @@ -2775,7 +2760,7 @@ class BilinearFormEJA(ConcreteEJA): True """ - def __init__(self, B, **kwargs): + def __init__(self, B, field=AA, **kwargs): # The matrix "B" is supplied by the user in most cases, # so it makes sense to check whether or not its positive- # definite unless we are specifically asked not to... @@ -2803,7 +2788,8 @@ class BilinearFormEJA(ConcreteEJA): return P([z0] + zbar.list()) n = B.nrows() - column_basis = tuple( b.column() for b in FreeModule(ZZ, n).basis() ) + column_basis = tuple( b.column() + for b in FreeModule(field, n).basis() ) # TODO: I haven't actually checked this, but it seems legit. associative = False @@ -2813,6 +2799,7 @@ class BilinearFormEJA(ConcreteEJA): super().__init__(column_basis, jordan_product, inner_product, + field=field, associative=associative, **kwargs) @@ -2908,7 +2895,7 @@ class JordanSpinEJA(BilinearFormEJA): True """ - def __init__(self, n, **kwargs): + def __init__(self, n, *args, **kwargs): # This is a special case of the BilinearFormEJA with the # identity matrix as its bilinear form. B = matrix.identity(ZZ, n) @@ -2919,7 +2906,7 @@ class JordanSpinEJA(BilinearFormEJA): # But also don't pass check_field=False here, because the user # can pass in a field! - super().__init__(B, **kwargs) + super().__init__(B, *args, **kwargs) @staticmethod def _max_random_instance_size():