From 3c7644ecfe369b6f83aa707b87d7a1f9aa246e27 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 2 Dec 2020 23:37:01 -0500 Subject: [PATCH] eja: move the "field" argument to (usually passed through) kwargs. --- mjo/eja/TODO | 6 +- mjo/eja/eja_algebra.py | 184 +++++++++++++++++++------------------- mjo/eja/eja_subalgebra.py | 2 +- 3 files changed, 94 insertions(+), 98 deletions(-) diff --git a/mjo/eja/TODO b/mjo/eja/TODO index c9c264c..08b8e81 100644 --- a/mjo/eja/TODO +++ b/mjo/eja/TODO @@ -24,9 +24,5 @@ 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 7. Figure out if CombinatorialFreeModule's use of IndexedGenerators can be used to replace the matrix_basis(). -8. Move the "field" argument to a keyword after basis, jp, and ip. - 9. Add back the check_field=False and check_axioms=False parameters - for the EJAs we've constructed ourselves. We can probably pass - the value of "check_axioms" to .span_of_basis() to skip - the linear-independence check. + for the EJAs we've constructed ourselves. diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 56d91bd..e32bc24 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -119,11 +119,11 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): The ``field`` we're given must be real with ``check_field=True``:: - sage: JordanSpinEJA(2, QQbar) + sage: JordanSpinEJA(2, field=QQbar) Traceback (most recent call last): ... ValueError: scalar field is not real - sage: JordanSpinEJA(2, QQbar, check_field=False) + sage: JordanSpinEJA(2, field=QQbar, check_field=False) Euclidean Jordan algebra of dimension 2 over Algebraic Field The multiplication table must be square with ``check_axioms=True``:: @@ -293,6 +293,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): sage: x = J.random_element() sage: J(x.to_vector().column()) == x True + """ msg = "not an element of this algebra" if elt == 0: @@ -399,7 +400,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): # Used to check whether or not something is zero in an inexact # ring. This number is sufficient to allow the construction of - # QuaternionHermitianEJA(2, RDF) with check_axioms=True. + # QuaternionHermitianEJA(2, field=RDF) with check_axioms=True. epsilon = 1e-16 for i in range(self.dimension()): @@ -496,7 +497,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): sage: J = HadamardEJA(2) sage: J.coordinate_polynomial_ring() Multivariate Polynomial Ring in X1, X2... - sage: J = RealSymmetricEJA(3,QQ,orthonormalize=False) + sage: J = RealSymmetricEJA(3,field=QQ,orthonormalize=False) sage: J.coordinate_polynomial_ring() Multivariate Polynomial Ring in X1, X2, X3, X4, X5, X6... @@ -1161,10 +1162,10 @@ class RationalBasisEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebr """ def __init__(self, - field, basis, jordan_product, inner_product, + field=AA, orthonormalize=True, prefix='e', category=None, @@ -1177,6 +1178,10 @@ class RationalBasisEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebr if not all( all(b_i in QQ for b_i in b.list()) for b in basis ): raise TypeError("basis not rational") + # Temporary(?) hack to ensure that the matrix and vector bases + # are over the same ring. + basis = tuple( b.change_ring(field) for b in basis ) + n = len(basis) vector_basis = basis @@ -1215,10 +1220,10 @@ class RationalBasisEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebr # because they are necessarily defined with respect to # ambient coordinates and not any particular basis. self._rational_algebra = RationalBasisEuclideanJordanAlgebra( - QQ, basis, jordan_product, inner_product, + field=QQ, orthonormalize=False, prefix=prefix, category=category, @@ -1539,9 +1544,9 @@ class RealSymmetricEJA(ConcreteEuclideanJordanAlgebra, In theory, our "field" can be any subfield of the reals:: - sage: RealSymmetricEJA(2, RDF) + sage: RealSymmetricEJA(2, field=RDF) Euclidean Jordan algebra of dimension 3 over Real Double Field - sage: RealSymmetricEJA(2, RR) + sage: RealSymmetricEJA(2, field=RR) Euclidean Jordan algebra of dimension 3 over Real Field with 53 bits of precision @@ -1582,7 +1587,7 @@ class RealSymmetricEJA(ConcreteEuclideanJordanAlgebra, """ @classmethod - def _denormalized_basis(cls, n, field): + def _denormalized_basis(cls, n): """ Return a basis for the space of real symmetric n-by-n matrices. @@ -1594,7 +1599,7 @@ class RealSymmetricEJA(ConcreteEuclideanJordanAlgebra, sage: set_random_seed() sage: n = ZZ.random_element(1,5) - sage: B = RealSymmetricEJA._denormalized_basis(n,QQ) + sage: B = RealSymmetricEJA._denormalized_basis(n) sage: all( M.is_symmetric() for M in B) True @@ -1604,7 +1609,7 @@ class RealSymmetricEJA(ConcreteEuclideanJordanAlgebra, S = [] for i in range(n): for j in range(i+1): - Eij = matrix(field, n, lambda k,l: k==i and l==j) + Eij = matrix(ZZ, n, lambda k,l: k==i and l==j) if i == j: Sij = Eij else: @@ -1618,22 +1623,20 @@ class RealSymmetricEJA(ConcreteEuclideanJordanAlgebra, return 4 # Dimension 10 @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): """ Return a random instance of this type of algebra. """ n = ZZ.random_element(cls._max_random_instance_size() + 1) - return cls(n, field, **kwargs) + return cls(n, **kwargs) - def __init__(self, n, field=AA, **kwargs): - basis = self._denormalized_basis(n, field) - super(RealSymmetricEJA, self).__init__(field, - basis, + def __init__(self, n, **kwargs): + super(RealSymmetricEJA, self).__init__(self._denormalized_basis(n), self.jordan_product, self.trace_inner_product, **kwargs) self.rank.set_cache(n) - self.one.set_cache(self(matrix.identity(field,n))) + self.one.set_cache(self(matrix.identity(ZZ,n))) class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): @@ -1810,9 +1813,9 @@ class ComplexHermitianEJA(ConcreteEuclideanJordanAlgebra, In theory, our "field" can be any subfield of the reals:: - sage: ComplexHermitianEJA(2, RDF) + sage: ComplexHermitianEJA(2, field=RDF) Euclidean Jordan algebra of dimension 4 over Real Double Field - sage: ComplexHermitianEJA(2, RR) + sage: ComplexHermitianEJA(2, field=RR) Euclidean Jordan algebra of dimension 4 over Real Field with 53 bits of precision @@ -1854,7 +1857,7 @@ class ComplexHermitianEJA(ConcreteEuclideanJordanAlgebra, """ @classmethod - def _denormalized_basis(cls, n, field): + def _denormalized_basis(cls, n): """ Returns a basis for the space of complex Hermitian n-by-n matrices. @@ -1873,15 +1876,16 @@ class ComplexHermitianEJA(ConcreteEuclideanJordanAlgebra, sage: set_random_seed() sage: n = ZZ.random_element(1,5) sage: field = QuadraticField(2, 'sqrt2') - sage: B = ComplexHermitianEJA._denormalized_basis(n, field) + sage: B = ComplexHermitianEJA._denormalized_basis(n) sage: all( M.is_symmetric() for M in B) True """ + field = ZZ R = PolynomialRing(field, 'z') z = R.gen() F = field.extension(z**2 + 1, 'I') - I = F.gen() + I = F.gen(1) # This is like the symmetric case, but we need to be careful: # @@ -1907,13 +1911,11 @@ class ComplexHermitianEJA(ConcreteEuclideanJordanAlgebra, return tuple( s.change_ring(field) for s in S ) - def __init__(self, n, field=AA, **kwargs): - basis = self._denormalized_basis(n,field) - super(ComplexHermitianEJA, self).__init__(field, - basis, - self.jordan_product, - self.trace_inner_product, - **kwargs) + def __init__(self, n, **kwargs): + super(ComplexHermitianEJA, self).__init__(self._denormalized_basis(n), + self.jordan_product, + self.trace_inner_product, + **kwargs) self.rank.set_cache(n) # TODO: pre-cache the identity! @@ -1922,12 +1924,12 @@ class ComplexHermitianEJA(ConcreteEuclideanJordanAlgebra, return 3 # Dimension 9 @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): """ Return a random instance of this type of algebra. """ n = ZZ.random_element(cls._max_random_instance_size() + 1) - return cls(n, field, **kwargs) + return cls(n, **kwargs) class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): @staticmethod @@ -2107,9 +2109,9 @@ class QuaternionHermitianEJA(ConcreteEuclideanJordanAlgebra, In theory, our "field" can be any subfield of the reals:: - sage: QuaternionHermitianEJA(2, RDF) + sage: QuaternionHermitianEJA(2, field=RDF) Euclidean Jordan algebra of dimension 6 over Real Double Field - sage: QuaternionHermitianEJA(2, RR) + sage: QuaternionHermitianEJA(2, field=RR) Euclidean Jordan algebra of dimension 6 over Real Field with 53 bits of precision @@ -2150,7 +2152,7 @@ class QuaternionHermitianEJA(ConcreteEuclideanJordanAlgebra, """ @classmethod - def _denormalized_basis(cls, n, field): + def _denormalized_basis(cls, n): """ Returns a basis for the space of quaternion Hermitian n-by-n matrices. @@ -2168,11 +2170,12 @@ class QuaternionHermitianEJA(ConcreteEuclideanJordanAlgebra, sage: set_random_seed() sage: n = ZZ.random_element(1,5) - sage: B = QuaternionHermitianEJA._denormalized_basis(n,QQ) + sage: B = QuaternionHermitianEJA._denormalized_basis(n) sage: all( M.is_symmetric() for M in B ) True """ + field = ZZ Q = QuaternionAlgebra(QQ,-1,-1) I,J,K = Q.gens() @@ -2205,10 +2208,8 @@ class QuaternionHermitianEJA(ConcreteEuclideanJordanAlgebra, return tuple( s.change_ring(field) for s in S ) - def __init__(self, n, field=AA, **kwargs): - basis = self._denormalized_basis(n,field) - super(QuaternionHermitianEJA, self).__init__(field, - basis, + def __init__(self, n, **kwargs): + super(QuaternionHermitianEJA, self).__init__(self._denormalized_basis(n), self.jordan_product, self.trace_inner_product, **kwargs) @@ -2223,12 +2224,12 @@ class QuaternionHermitianEJA(ConcreteEuclideanJordanAlgebra, return 2 # Dimension 6 @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): """ Return a random instance of this type of algebra. """ n = ZZ.random_element(cls._max_random_instance_size() + 1) - return cls(n, field, **kwargs) + return cls(n, **kwargs) class HadamardEJA(ConcreteEuclideanJordanAlgebra): @@ -2271,24 +2272,24 @@ class HadamardEJA(ConcreteEuclideanJordanAlgebra): (r0, r1, r2) """ - def __init__(self, n, field=AA, **kwargs): - V = VectorSpace(field, n) - basis = V.basis() - + def __init__(self, n, **kwargs): def jordan_product(x,y): - return V([ xi*yi for (xi,yi) in zip(x,y) ]) + P = x.parent() + return P(tuple( xi*yi for (xi,yi) in zip(x,y) )) def inner_product(x,y): return x.inner_product(y) # Don't orthonormalize because our basis is already - # orthonormal with respect to our inner-product. But also - # don't pass check_field=False here, because the user can pass - # in a field! - super(HadamardEJA, self).__init__(field, - basis, + # orthonormal with respect to our inner-product. + if not 'orthonormalize' in kwargs: + kwargs['orthonormalize'] = False + + # But also don't pass check_field=False here, because the user + # can pass in a field! + standard_basis = FreeModule(ZZ, n).basis() + super(HadamardEJA, self).__init__(standard_basis, jordan_product, inner_product, - orthonormalize=False, check_axioms=False, **kwargs) self.rank.set_cache(n) @@ -2306,12 +2307,12 @@ class HadamardEJA(ConcreteEuclideanJordanAlgebra): return 5 @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): """ Return a random instance of this type of algebra. """ n = ZZ.random_element(cls._max_random_instance_size() + 1) - return cls(n, field, **kwargs) + return cls(n, **kwargs) class BilinearFormEJA(ConcreteEuclideanJordanAlgebra): @@ -2395,27 +2396,26 @@ class BilinearFormEJA(ConcreteEuclideanJordanAlgebra): sage: actual == expected True """ - def __init__(self, B, field=AA, **kwargs): + def __init__(self, B, **kwargs): if not B.is_positive_definite(): raise ValueError("bilinear form is not positive-definite") - n = B.nrows() - V = VectorSpace(field, n) - def inner_product(x,y): return (B*x).inner_product(y) def jordan_product(x,y): + P = x.parent() x0 = x[0] xbar = x[1:] y0 = y[0] ybar = y[1:] z0 = inner_product(x,y) zbar = y0*xbar + x0*ybar - return V([z0] + zbar.list()) + return P((z0,) + tuple(zbar)) - super(BilinearFormEJA, self).__init__(field, - V.basis(), + n = B.nrows() + standard_basis = FreeModule(ZZ, n).basis() + super(BilinearFormEJA, self).__init__(standard_basis, jordan_product, inner_product, **kwargs) @@ -2438,28 +2438,28 @@ class BilinearFormEJA(ConcreteEuclideanJordanAlgebra): return 5 @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): """ Return a random instance of this algebra. """ n = ZZ.random_element(cls._max_random_instance_size() + 1) if n.is_zero(): - B = matrix.identity(field, n) - return cls(B, field, **kwargs) + B = matrix.identity(ZZ, n) + return cls(B, **kwargs) - B11 = matrix.identity(field,1) - M = matrix.random(field, n-1) - I = matrix.identity(field, n-1) - alpha = field.zero() + B11 = matrix.identity(ZZ, 1) + M = matrix.random(ZZ, n-1) + I = matrix.identity(ZZ, n-1) + alpha = ZZ.zero() while alpha.is_zero(): - alpha = field.random_element().abs() + alpha = ZZ.random_element().abs() B22 = M.transpose()*M + alpha*I from sage.matrix.special import block_matrix B = block_matrix(2,2, [ [B11, ZZ(0) ], [ZZ(0), B22 ] ]) - return cls(B, field, **kwargs) + return cls(B, **kwargs) class JordanSpinEJA(BilinearFormEJA): @@ -2512,18 +2512,19 @@ class JordanSpinEJA(BilinearFormEJA): True """ - def __init__(self, n, field=AA, **kwargs): + def __init__(self, n, **kwargs): # This is a special case of the BilinearFormEJA with the # identity matrix as its bilinear form. - B = matrix.identity(field, n) + B = matrix.identity(ZZ, n) # Don't orthonormalize because our basis is already - # orthonormal with respect to our inner-product. But - # also don't pass check_field=False here, because the - # user can pass in a field! + # orthonormal with respect to our inner-product. + if not 'orthonormalize' in kwargs: + kwargs['orthonormalize'] = False + + # But also don't pass check_field=False here, because the user + # can pass in a field! super(JordanSpinEJA, self).__init__(B, - field, - orthonormalize=False, check_axioms=False, **kwargs) @@ -2535,14 +2536,14 @@ class JordanSpinEJA(BilinearFormEJA): return 5 @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): """ Return a random instance of this type of algebra. Needed here to override the implementation for ``BilinearFormEJA``. """ n = ZZ.random_element(cls._max_random_instance_size() + 1) - return cls(n, field, **kwargs) + return cls(n, **kwargs) class TrivialEJA(ConcreteEuclideanJordanAlgebra): @@ -2574,12 +2575,11 @@ class TrivialEJA(ConcreteEuclideanJordanAlgebra): 0 """ - def __init__(self, field=AA, **kwargs): + def __init__(self, **kwargs): jordan_product = lambda x,y: x - inner_product = lambda x,y: field(0) + inner_product = lambda x,y: 0 basis = () - super(TrivialEJA, self).__init__(field, - basis, + super(TrivialEJA, self).__init__(basis, jordan_product, inner_product, **kwargs) @@ -2589,10 +2589,10 @@ class TrivialEJA(ConcreteEuclideanJordanAlgebra): self.one.set_cache( self.zero() ) @classmethod - def random_instance(cls, field=AA, **kwargs): + def random_instance(cls, **kwargs): # We don't take a "size" argument so the superclass method is # inappropriate for us. - return cls(field, **kwargs) + return cls(**kwargs) class DirectSumEJA(FiniteDimensionalEuclideanJordanAlgebra): r""" @@ -2625,8 +2625,8 @@ class DirectSumEJA(FiniteDimensionalEuclideanJordanAlgebra): have the same base ring; an error is raised otherwise:: sage: set_random_seed() - sage: J1 = random_eja(AA) - sage: J2 = random_eja(QQ,orthonormalize=False) + sage: J1 = random_eja(field=AA) + sage: J2 = random_eja(field=QQ,orthonormalize=False) sage: J = DirectSumEJA(J1,J2) Traceback (most recent call last): ... @@ -2679,8 +2679,8 @@ class DirectSumEJA(FiniteDimensionalEuclideanJordanAlgebra): EXAMPLES:: - sage: J1 = HadamardEJA(2,QQ) - sage: J2 = JordanSpinEJA(3,QQ) + sage: J1 = HadamardEJA(2, field=QQ) + sage: J2 = JordanSpinEJA(3, field=QQ) sage: J = DirectSumEJA(J1,J2) sage: J.factors() (Euclidean Jordan algebra of dimension 2 over Rational Field, @@ -2809,8 +2809,8 @@ class DirectSumEJA(FiniteDimensionalEuclideanJordanAlgebra): EXAMPLE:: - sage: J1 = HadamardEJA(3,QQ) - sage: J2 = QuaternionHermitianEJA(2,QQ,orthonormalize=False) + sage: J1 = HadamardEJA(3,field=QQ) + sage: J2 = QuaternionHermitianEJA(2,field=QQ,orthonormalize=False) sage: J = DirectSumEJA(J1,J2) sage: x1 = J1.one() sage: x2 = x1 diff --git a/mjo/eja/eja_subalgebra.py b/mjo/eja/eja_subalgebra.py index 8c0f909..e7308ea 100644 --- a/mjo/eja/eja_subalgebra.py +++ b/mjo/eja/eja_subalgebra.py @@ -27,7 +27,7 @@ class FiniteDimensionalEuclideanJordanSubalgebraElement(FiniteDimensionalEuclide our basis:: sage: set_random_seed() - sage: x = random_eja(AA).random_element() + sage: x = random_eja(field=AA).random_element() sage: A = x.subalgebra_generated_by(orthonormalize_basis=True) sage: y = A.random_element() sage: y.operator()(A.one()) == y -- 2.43.2