X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=1ccbf2e302e7cd43ae1877e3fda6fdcaa3f5e5de;hb=928b7d49fda98ff105c92293b5797bb7a2b9873a;hp=af4080b0807d6ac6848849f23edd237657896413;hpb=6d6af7c2560b2886cd47a2c8f3c0b9d1b843f649;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index af4080b..1ccbf2e 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -166,9 +166,10 @@ from sage.modules.free_module import FreeModule, VectorSpace from sage.rings.all import (ZZ, QQ, AA, QQbar, RR, RLF, CLF, PolynomialRing, QuadraticField) -from mjo.eja.eja_element import FiniteDimensionalEJAElement +from mjo.eja.eja_element import (CartesianProductEJAElement, + FiniteDimensionalEJAElement) from mjo.eja.eja_operator import FiniteDimensionalEJAOperator -from mjo.eja.eja_utils import _all2list, _mat2vec +from mjo.eja.eja_utils import _all2list def EuclideanJordanAlgebras(field): r""" @@ -229,7 +230,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): We should compute that an element subalgebra is associative even if we circumvent the element method:: - sage: set_random_seed() sage: J = random_eja(field=QQ,orthonormalize=False) sage: x = J.random_element() sage: A = x.subalgebra_generated_by(orthonormalize=False) @@ -366,7 +366,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): if orthonormalize: # Now "self._matrix_span" is the vector space of our - # algebra coordinates. The variables "X1", "X2",... refer + # algebra coordinates. The variables "X0", "X1",... refer # to the entries of vectors in self._matrix_span. Thus to # convert back and forth between the orthonormal # coordinates and the given ones, we need to stick the @@ -431,7 +431,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): TESTS:: - sage: set_random_seed() sage: J = random_eja() sage: J(1) Traceback (most recent call last): @@ -456,7 +455,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): TESTS:: - sage: set_random_seed() sage: J = random_eja() sage: n = J.dimension() sage: bi = J.zero() @@ -498,7 +496,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): Our inner product is "associative," which means the following for a symmetric bilinear form:: - sage: set_random_seed() sage: J = random_eja() sage: x,y,z = J.random_elements(3) sage: (x*y).inner_product(z) == y.inner_product(x*z) @@ -509,7 +506,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): 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: actual = x.inner_product(y) @@ -522,7 +518,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): one). This is in Faraut and Koranyi, and also my "On the symmetry..." paper:: - sage: set_random_seed() sage: J = BilinearFormEJA.random_instance() sage: n = J.dimension() sage: x = J.random_element() @@ -635,7 +630,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): The values we've presupplied to the constructors agree with the computation:: - sage: set_random_seed() sage: J = random_eja() sage: J.is_associative() == J._jordan_product_is_associative() True @@ -757,7 +751,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): Ensure that we can convert any element back and forth faithfully between its matrix and algebra representations:: - sage: set_random_seed() sage: J = random_eja() sage: x = J.random_element() sage: J(x.to_matrix()) == x @@ -870,7 +863,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): sage: J = JordanSpinEJA(3) sage: p = J.characteristic_polynomial_of(); p - X1^2 - X2^2 - X3^2 + (-2*t)*X1 + t^2 + X0^2 - X1^2 - X2^2 + (-2*t)*X0 + t^2 sage: xvec = J.one().to_vector() sage: p(*xvec) t^2 - 2*t + 1 @@ -919,13 +912,13 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): sage: J = HadamardEJA(2) sage: J.coordinate_polynomial_ring() - Multivariate Polynomial Ring in X1, X2... + Multivariate Polynomial Ring in X0, X1... sage: J = RealSymmetricEJA(3,field=QQ,orthonormalize=False) sage: J.coordinate_polynomial_ring() - Multivariate Polynomial Ring in X1, X2, X3, X4, X5, X6... + Multivariate Polynomial Ring in X0, X1, X2, X3, X4, X5... """ - var_names = tuple( "X%d" % z for z in range(1, self.dimension()+1) ) + var_names = tuple( "X%d" % z for z in range(self.dimension()) ) return PolynomialRing(self.base_ring(), var_names) def inner_product(self, x, y): @@ -947,7 +940,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): Our inner product is "associative," which means the following for a symmetric bilinear form:: - sage: set_random_seed() sage: J = random_eja() sage: x,y,z = J.random_elements(3) sage: (x*y).inner_product(z) == y.inner_product(x*z) @@ -958,7 +950,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): 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: actual = x.inner_product(y) @@ -971,7 +962,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): one). This is in Faraut and Koranyi, and also my "On the symmetry..." paper:: - sage: set_random_seed() sage: J = BilinearFormEJA.random_instance() sage: n = J.dimension() sage: x = J.random_element() @@ -1199,7 +1189,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): The identity element acts like the identity, regardless of whether or not we orthonormalize:: - sage: set_random_seed() sage: J = random_eja() sage: x = J.random_element() sage: J.one()*x == x and x*J.one() == x @@ -1211,7 +1200,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): :: - sage: set_random_seed() sage: J = random_eja(field=QQ, orthonormalize=False) sage: x = J.random_element() sage: J.one()*x == x and x*J.one() == x @@ -1225,7 +1213,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): regardless of the base field and whether or not we orthonormalize:: - sage: set_random_seed() sage: J = random_eja() sage: actual = J.one().operator().matrix() sage: expected = matrix.identity(J.base_ring(), J.dimension()) @@ -1240,7 +1227,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): :: - sage: set_random_seed() sage: J = random_eja(field=QQ, orthonormalize=False) sage: actual = J.one().operator().matrix() sage: expected = matrix.identity(J.base_ring(), J.dimension()) @@ -1256,7 +1242,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): Ensure that the cached unit element (often precomputed by hand) agrees with the computed one:: - sage: set_random_seed() sage: J = random_eja() sage: cached = J.one() sage: J.one.clear_cache() @@ -1265,7 +1250,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): :: - sage: set_random_seed() sage: J = random_eja(field=QQ, orthonormalize=False) sage: cached = J.one() sage: J.one.clear_cache() @@ -1281,7 +1265,9 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # # Of course, matrices aren't vectors in sage, so we have to # appeal to the "long vectors" isometry. - oper_vecs = [ _mat2vec(g.operator().matrix()) for g in self.gens() ] + + V = VectorSpace(self.base_ring(), self.dimension()**2) + oper_vecs = [ V(g.operator().matrix().list()) for g in self.gens() ] # Now we use basic linear algebra to find the coefficients, # of the matrices-as-vectors-linear-combination, which should @@ -1291,7 +1277,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # We used the isometry on the left-hand side already, but we # still need to do it for the right-hand side. Recall that we # wanted something that summed to the identity matrix. - b = _mat2vec( matrix.identity(self.base_ring(), self.dimension()) ) + b = V( matrix.identity(self.base_ring(), self.dimension()).list() ) # Now if there's an identity element in the algebra, this # should work. We solve on the left to avoid having to @@ -1376,7 +1362,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): Every algebra decomposes trivially with respect to its identity element:: - sage: set_random_seed() sage: J = random_eja() sage: J0,J5,J1 = J.peirce_decomposition(J.one()) sage: J0.dimension() == 0 and J5.dimension() == 0 @@ -1389,7 +1374,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): elements in the two subalgebras are the projections onto their respective subspaces of the superalgebra's identity element:: - sage: set_random_seed() sage: J = random_eja() sage: x = J.random_element() sage: if not J.is_trivial(): @@ -1515,6 +1499,64 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): for idx in range(count) ) + def operator_polynomial_matrix(self): + r""" + Return the matrix of polynomials (over this algebra's + :meth:`coordinate_polynomial_ring`) that, when evaluated at + the basis coordinates of an element `x`, produces the basis + representation of `L_{x}`. + + SETUP:: + + sage: from mjo.eja.eja_algebra import (HadamardEJA, + ....: JordanSpinEJA) + + EXAMPLES:: + + sage: J = HadamardEJA(4) + sage: L_x = J.operator_polynomial_matrix() + sage: L_x + [X0 0 0 0] + [ 0 X1 0 0] + [ 0 0 X2 0] + [ 0 0 0 X3] + sage: x = J.one() + sage: d = zip(J.coordinate_polynomial_ring().gens(), x.to_vector()) + sage: L_x.subs(dict(d)) + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + + :: + + sage: J = JordanSpinEJA(4) + sage: L_x = J.operator_polynomial_matrix() + sage: L_x + [X0 X1 X2 X3] + [X1 X0 0 0] + [X2 0 X0 0] + [X3 0 0 X0] + sage: x = J.one() + sage: d = zip(J.coordinate_polynomial_ring().gens(), x.to_vector()) + sage: L_x.subs(dict(d)) + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + + """ + R = self.coordinate_polynomial_ring() + + def L_x_i_j(i,j): + # From a result in my book, these are the entries of the + # basis representation of L_x. + return sum( v*self.monomial(k).operator().matrix()[i,j] + for (k,v) in enumerate(R.gens()) ) + + n = self.dimension() + return matrix(R, n, n, L_x_i_j) + @cached_method def _charpoly_coefficients(self): r""" @@ -1530,7 +1572,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): The theory shows that these are all homogeneous polynomials of a known degree:: - sage: set_random_seed() sage: J = random_eja() sage: all(p.is_homogeneous() for p in J._charpoly_coefficients()) True @@ -1538,16 +1579,9 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): """ n = self.dimension() R = self.coordinate_polynomial_ring() - vars = R.gens() F = R.fraction_field() - def L_x_i_j(i,j): - # From a result in my book, these are the entries of the - # basis representation of L_x. - return sum( vars[k]*self.monomial(k).operator().matrix()[i,j] - for k in range(n) ) - - L_x = matrix(F, n, n, L_x_i_j) + L_x = self.operator_polynomial_matrix() r = None if self.rank.is_in_cache(): @@ -1628,7 +1662,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): positive integer rank, unless the algebra is trivial in which case its rank will be zero:: - sage: set_random_seed() sage: J = random_eja() sage: r = J.rank() sage: r in ZZ @@ -1639,7 +1672,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): Ensure that computing the rank actually works, since the ranks of all simple algebras are known and will be cached by default:: - sage: set_random_seed() # long time sage: J = random_eja() # long time sage: cached = J.rank() # long time sage: J.rank.clear_cache() # long time @@ -1768,7 +1800,7 @@ class RationalBasisEJA(FiniteDimensionalEJA): sage: J = JordanSpinEJA(3) sage: J._charpoly_coefficients() - (X1^2 - X2^2 - X3^2, -2*X1) + (X0^2 - X1^2 - X2^2, -2*X0) sage: a0 = J._charpoly_coefficients()[0] sage: J.base_ring() Algebraic Real Field @@ -1814,7 +1846,6 @@ class ConcreteEJA(FiniteDimensionalEJA): Our basis is normalized with respect to the algebra's inner product, unless we specify otherwise:: - sage: set_random_seed() sage: J = ConcreteEJA.random_instance() sage: all( b.norm() == 1 for b in J.gens() ) True @@ -1825,7 +1856,6 @@ class ConcreteEJA(FiniteDimensionalEJA): natural->EJA basis representation is an isometry and within the EJA the operator is self-adjoint by the Jordan axiom:: - sage: set_random_seed() sage: J = ConcreteEJA.random_instance() sage: x = J.random_element() sage: x.operator().is_self_adjoint() @@ -1920,7 +1950,6 @@ class MatrixEJA(FiniteDimensionalEJA): TESTS:: - sage: set_random_seed() sage: n = ZZ.random_element(1,5) sage: A = MatrixSpace(QQ, n) sage: B = MatrixEJA._denormalized_basis(A) @@ -1929,7 +1958,6 @@ class MatrixEJA(FiniteDimensionalEJA): :: - sage: set_random_seed() sage: n = ZZ.random_element(1,5) sage: A = ComplexMatrixAlgebra(n, scalars=QQ) sage: B = MatrixEJA._denormalized_basis(A) @@ -1938,7 +1966,6 @@ class MatrixEJA(FiniteDimensionalEJA): :: - sage: set_random_seed() sage: n = ZZ.random_element(1,5) sage: A = QuaternionMatrixAlgebra(n, scalars=QQ) sage: B = MatrixEJA._denormalized_basis(A) @@ -1947,7 +1974,6 @@ class MatrixEJA(FiniteDimensionalEJA): :: - sage: set_random_seed() sage: n = ZZ.random_element(1,5) sage: A = OctonionMatrixAlgebra(n, scalars=QQ) sage: B = MatrixEJA._denormalized_basis(A) @@ -2045,7 +2071,6 @@ class MatrixEJA(FiniteDimensionalEJA): # if the user passes check_axioms=True. if "check_axioms" not in kwargs: kwargs["check_axioms"] = False - super().__init__(self._denormalized_basis(matrix_space), self.jordan_product, self.trace_inner_product, @@ -2089,7 +2114,6 @@ class RealSymmetricEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): The dimension of this algebra is `(n^2 + n) / 2`:: - sage: set_random_seed() sage: d = RealSymmetricEJA._max_random_instance_dimension() sage: n = RealSymmetricEJA._max_random_instance_size(d) sage: J = RealSymmetricEJA(n) @@ -2098,7 +2122,6 @@ class RealSymmetricEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): The Jordan multiplication is what we think it is:: - sage: set_random_seed() sage: J = RealSymmetricEJA.random_instance() sage: x,y = J.random_elements(2) sage: actual = (x*y).to_matrix() @@ -2140,10 +2163,6 @@ class RealSymmetricEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): return cls(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 - A = MatrixSpace(field, n) super().__init__(A, **kwargs) @@ -2194,7 +2213,6 @@ class ComplexHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): The dimension of this algebra is `n^2`:: - sage: set_random_seed() sage: d = ComplexHermitianEJA._max_random_instance_dimension() sage: n = ComplexHermitianEJA._max_random_instance_size(d) sage: J = ComplexHermitianEJA(n) @@ -2203,7 +2221,6 @@ class ComplexHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): The Jordan multiplication is what we think it is:: - sage: set_random_seed() sage: J = ComplexHermitianEJA.random_instance() sage: x,y = J.random_elements(2) sage: actual = (x*y).to_matrix() @@ -2227,10 +2244,6 @@ class ComplexHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): """ 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 - from mjo.hurwitz import ComplexMatrixAlgebra A = ComplexMatrixAlgebra(n, scalars=field) super().__init__(A, **kwargs) @@ -2284,7 +2297,6 @@ class QuaternionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): The dimension of this algebra is `2*n^2 - n`:: - sage: set_random_seed() sage: d = QuaternionHermitianEJA._max_random_instance_dimension() sage: n = QuaternionHermitianEJA._max_random_instance_size(d) sage: J = QuaternionHermitianEJA(n) @@ -2293,7 +2305,6 @@ class QuaternionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): The Jordan multiplication is what we think it is:: - sage: set_random_seed() sage: J = QuaternionHermitianEJA.random_instance() sage: x,y = J.random_elements(2) sage: actual = (x*y).to_matrix() @@ -2317,10 +2328,6 @@ class QuaternionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA): """ 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 - from mjo.hurwitz import QuaternionMatrixAlgebra A = QuaternionMatrixAlgebra(n, scalars=field) super().__init__(A, **kwargs) @@ -2670,7 +2677,6 @@ class BilinearFormEJA(RationalBasisEJA, ConcreteEJA): matrix. We opt not to orthonormalize the basis, because if we did, we would have to normalize the `s_{i}` in a similar manner:: - sage: set_random_seed() sage: n = ZZ.random_element(5) sage: M = matrix.random(QQ, max(0,n-1), algorithm='unimodular') sage: B11 = matrix.identity(QQ,1) @@ -2832,7 +2838,6 @@ class JordanSpinEJA(BilinearFormEJA): Ensure that we have the usual inner product on `R^n`:: - sage: set_random_seed() sage: J = JordanSpinEJA.random_instance() sage: x,y = J.random_elements(2) sage: actual = x.inner_product(y) @@ -2953,7 +2958,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): The Jordan product is inherited from our factors and implemented by our CombinatorialFreeModule Cartesian product superclass:: - sage: set_random_seed() sage: J1 = HadamardEJA(2) sage: J2 = RealSymmetricEJA(2) sage: J = cartesian_product([J1,J2]) @@ -3090,7 +3094,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): The cached unit element is the same one that would be computed:: - sage: set_random_seed() # long time sage: J1 = random_eja() # long time sage: J2 = random_eja() # long time sage: J = cartesian_product([J1,J2]) # long time @@ -3100,6 +3103,7 @@ class CartesianProductEJA(FiniteDimensionalEJA): sage: actual == expected # long time True """ + Element = CartesianProductEJAElement def __init__(self, factors, **kwargs): m = len(factors) if m == 0: @@ -3203,6 +3207,34 @@ class CartesianProductEJA(FiniteDimensionalEJA): ones = tuple(J.one().to_matrix() for J in factors) self.one.set_cache(self(ones)) + def _sets_keys(self): + r""" + + SETUP:: + + sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA, + ....: RealSymmetricEJA) + + TESTS: + + The superclass uses ``_sets_keys()`` to implement its + ``cartesian_factors()`` method:: + + sage: J1 = RealSymmetricEJA(2, + ....: field=QQ, + ....: orthonormalize=False, + ....: prefix="a") + sage: J2 = ComplexHermitianEJA(2,field=QQ,orthonormalize=False) + sage: J = cartesian_product([J1,J2]) + sage: x = sum(i*J.gens()[i] for i in range(len(J.gens()))) + sage: x.cartesian_factors() + (a1 + 2*a2, 3*b0 + 4*b1 + 5*b2 + 6*b3) + + """ + # Copy/pasted from CombinatorialFreeModule_CartesianProduct, + # but returning a tuple instead of a list. + return tuple(range(len(self.cartesian_factors()))) + def cartesian_factors(self): # Copy/pasted from CombinatorialFreeModule_CartesianProduct. return self._sets @@ -3280,7 +3312,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): The answer never changes:: - sage: set_random_seed() sage: J1 = random_eja() sage: J2 = random_eja() sage: J = cartesian_product([J1,J2]) @@ -3370,7 +3401,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): The answer never changes:: - sage: set_random_seed() sage: J1 = random_eja() sage: J2 = random_eja() sage: J = cartesian_product([J1,J2]) @@ -3383,7 +3413,6 @@ class CartesianProductEJA(FiniteDimensionalEJA): produce the identity map, and mismatching them should produce the zero map:: - sage: set_random_seed() sage: J1 = random_eja() sage: J2 = random_eja() sage: J = cartesian_product([J1,J2]) @@ -3488,7 +3517,6 @@ def random_eja(max_dimension=None, *args, **kwargs): TESTS:: - sage: set_random_seed() sage: n = ZZ.random_element(1,5) sage: J = random_eja(max_dimension=n, field=QQ, orthonormalize=False) sage: J.dimension() <= n