X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;ds=inline;f=mjo%2Feja%2Feja_algebra.py;h=d38ba87d37d3e6bdb01e23c7a9631bf98ee2c96c;hb=5dcc3f73aba8c0b71b601c2d8bd65d9229c2ddb0;hp=9ba146bc6e8fd6dc97dcf1f7d70058cd046528a4;hpb=3baadd6fb5c765caab2bd57d1d6ed764b03d53b3;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 9ba146b..d38ba87 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -31,10 +31,9 @@ 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 (CartesianProductEJAElement, - FiniteDimensionalEJAElement) +from mjo.eja.eja_element import FiniteDimensionalEJAElement from mjo.eja.eja_operator import FiniteDimensionalEJAOperator -from mjo.eja.eja_utils import _mat2vec +from mjo.eja.eja_utils import _all2list, _mat2vec class FiniteDimensionalEJA(CombinatorialFreeModule): r""" @@ -52,15 +51,15 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): `(a,b)` into column matrices `(a,b)^{T}` after converting `a` and `b` themselves. - - jordan_product -- function of two elements (in matrix form) - that returns their jordan product in this algebra; this will - be applied to ``basis`` to compute a multiplication table for - the algebra. - - - inner_product -- function of two elements (in matrix form) that - returns their inner product. This will be applied to ``basis`` to - compute an inner-product table (basically a matrix) for this algebra. + - jordan_product -- function of two ``basis`` elements (in + matrix form) that returns their jordan product, also in matrix + form; this will be applied to ``basis`` to compute a + multiplication table for the algebra. + - inner_product -- function of two ``basis`` elements (in matrix + form) that returns their inner product. This will be applied + to ``basis`` to compute an inner-product table (basically a + matrix) for this algebra. """ Element = FiniteDimensionalEJAElement @@ -76,6 +75,18 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): check_axioms=True, prefix='e'): + # Keep track of whether or not the matrix basis consists of + # tuples, since we need special cases for them damned near + # everywhere. This is INDEPENDENT of whether or not the + # algebra is a cartesian product, since a subalgebra of a + # cartesian product will have a basis of tuples, but will not + # in general itself be a cartesian product algebra. + self._matrix_basis_is_cartesian = False + n = len(basis) + if n > 0: + if hasattr(basis[0], 'cartesian_factors'): + self._matrix_basis_is_cartesian = True + if check_field: if not field.is_subring(RR): # Note: this does return true for the real algebraic @@ -89,7 +100,13 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # The field for a cartesian product algebra comes from one # of its factors and is the same for all factors, so # there's no need to "reapply" it on product algebras. - basis = tuple( b.change_ring(field) for b in basis ) + if self._matrix_basis_is_cartesian: + # OK since if n == 0, the basis does not consist of tuples. + P = basis[0].parent() + basis = tuple( P(tuple(b_i.change_ring(field) for b_i in b)) + for b in basis ) + else: + basis = tuple( b.change_ring(field) for b in basis ) if check_axioms: @@ -118,7 +135,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # Call the superclass constructor so that we can use its from_vector() # method to build our multiplication table. - n = len(basis) CombinatorialFreeModule.__init__(self, field, range(n), @@ -133,17 +149,9 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # we see in things like x = 1*e1 + 2*e2. vector_basis = basis - def flatten(b): - # flatten a vector, matrix, or cartesian product of those - # things into a long list. - if cartesian_product: - return sum(( b_i.list() for b_i in b ), []) - else: - return b.list() - degree = 0 if n > 0: - degree = len(flatten(basis[0])) + degree = len(_all2list(basis[0])) # Build an ambient space that fits our matrix basis when # written out as "long vectors." @@ -157,7 +165,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # Save a copy of the un-orthonormalized basis for later. # Convert it to ambient V (vector) coordinates while we're # at it, because we'd have to do it later anyway. - deortho_vector_basis = tuple( V(flatten(b)) for b in basis ) + deortho_vector_basis = tuple( V(_all2list(b)) for b in basis ) from mjo.eja.eja_utils import gram_schmidt basis = tuple(gram_schmidt(basis, inner_product)) @@ -169,7 +177,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # Now create the vector space for the algebra, which will have # its own set of non-ambient coordinates (in terms of the # supplied basis). - vector_basis = tuple( V(flatten(b)) for b in basis ) + vector_basis = tuple( V(_all2list(b)) for b in basis ) W = V.span_of_basis( vector_basis, check=check_axioms) if orthonormalize: @@ -201,7 +209,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # The jordan product returns a matrixy answer, so we # have to convert it to the algebra coordinates. elt = jordan_product(q_i, q_j) - elt = W.coordinate_vector(V(flatten(elt))) + elt = W.coordinate_vector(V(_all2list(elt))) self._multiplication_table[i][j] = self.from_vector(elt) if not orthonormalize: @@ -249,6 +257,35 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): def product_on_basis(self, i, j): + r""" + Returns the Jordan product of the `i` and `j`th basis elements. + + This completely defines the Jordan product on the algebra, and + is used direclty by our superclass machinery to implement + :meth:`product`. + + SETUP:: + + sage: from mjo.eja.eja_algebra import random_eja + + TESTS:: + + sage: set_random_seed() + sage: J = random_eja() + sage: n = J.dimension() + sage: ei = J.zero() + sage: ej = J.zero() + sage: ei_ej = J.zero()*J.zero() + sage: if n > 0: + ....: i = ZZ.random_element(n) + ....: j = ZZ.random_element(n) + ....: ei = J.gens()[i] + ....: ej = J.gens()[j] + ....: ei_ej = J.product_on_basis(i,j) + sage: ei*ej == ei_ej + True + + """ # We only stored the lower-triangular portion of the # multiplication table. if j <= i: @@ -414,6 +451,15 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): ... ValueError: not an element of this algebra + Tuples work as well, provided that the matrix basis for the + algebra consists of them:: + + sage: J1 = HadamardEJA(3) + sage: J2 = RealSymmetricEJA(2) + sage: J = cartesian_product([J1,J2]) + sage: J( (J1.matrix_basis()[1], J2.matrix_basis()[2]) ) + e(0, 1) + e(1, 2) + TESTS: Ensure that we can convert any element of the two non-matrix @@ -430,13 +476,23 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): sage: J(x.to_vector().column()) == x True + We cannot coerce elements between algebras just because their + matrix representations are compatible:: + + sage: J1 = HadamardEJA(3) + sage: J2 = JordanSpinEJA(3) + sage: J2(J1.one()) + Traceback (most recent call last): + ... + ValueError: not an element of this algebra + sage: J1(J2.zero()) + Traceback (most recent call last): + ... + ValueError: not an element of this algebra + """ msg = "not an element of this algebra" - if elt == 0: - # The superclass implementation of random_element() - # needs to be able to coerce "0" into the algebra. - return self.zero() - elif elt in self.base_ring(): + if elt in self.base_ring(): # Ensure that no base ring -> algebra coercion is performed # by this method. There's some stupidity in sage that would # otherwise propagate to this method; for example, sage thinks @@ -444,9 +500,11 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): raise ValueError(msg) try: + # Try to convert a vector into a column-matrix... elt = elt.column() except (AttributeError, TypeError): - # Try to convert a vector into a column-matrix + # and ignore failure, because we weren't really expecting + # a vector as an argument anyway. pass if elt not in self.matrix_space(): @@ -459,14 +517,20 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): # closure whereas the base ring of the 3-by-3 identity matrix # could be QQ instead of QQbar. # + # And, we also have to handle Cartesian product bases (when + # the matrix basis consists of tuples) here. The "good news" + # is that we're already converting everything to long vectors, + # and that strategy works for tuples as well. + # # We pass check=False because the matrix basis is "guaranteed" # to be linearly independent... right? Ha ha. - V = VectorSpace(self.base_ring(), elt.nrows()*elt.ncols()) - W = V.span_of_basis( (_mat2vec(s) for s in self.matrix_basis()), + elt = _all2list(elt) + V = VectorSpace(self.base_ring(), len(elt)) + W = V.span_of_basis( (V(_all2list(s)) for s in self.matrix_basis()), check=False) try: - coords = W.coordinate_vector(_mat2vec(elt)) + coords = W.coordinate_vector(V(elt)) except ArithmeticError: # vector is not in free module raise ValueError(msg) @@ -764,12 +828,49 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): we think of them as matrices (including column vectors of the appropriate size). - Generally this will be an `n`-by-`1` column-vector space, + "By default" this will be an `n`-by-`1` column-matrix space, except when the algebra is trivial. There it's `n`-by-`n` (where `n` is zero), to ensure that two elements of the matrix - space (empty matrices) can be multiplied. + space (empty matrices) can be multiplied. For algebras of + matrices, this returns the space in which their + real embeddings live. + + SETUP:: + + sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA, + ....: JordanSpinEJA, + ....: QuaternionHermitianEJA, + ....: TrivialEJA) + + EXAMPLES: + + By default, the matrix representation is just a column-matrix + equivalent to the vector representation:: + + sage: J = JordanSpinEJA(3) + sage: J.matrix_space() + Full MatrixSpace of 3 by 1 dense matrices over Algebraic + Real Field + + The matrix representation in the trivial algebra is + zero-by-zero instead of the usual `n`-by-one:: + + sage: J = TrivialEJA() + sage: J.matrix_space() + Full MatrixSpace of 0 by 0 dense matrices over Algebraic + Real Field + + The matrix space for complex/quaternion Hermitian matrix EJA + is the space in which their real-embeddings live, not the + original complex/quaternion matrix space:: + + sage: J = ComplexHermitianEJA(2,field=QQ,orthonormalize=False) + sage: J.matrix_space() + Full MatrixSpace of 4 by 4 dense matrices over Rational Field + sage: J = QuaternionHermitianEJA(1,field=QQ,orthonormalize=False) + sage: J.matrix_space() + Full MatrixSpace of 4 by 4 dense matrices over Rational Field - Matrix algebras override this with something more useful. """ if self.is_trivial(): return MatrixSpace(self.base_ring(), 0) @@ -1267,9 +1368,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): def subalgebra(self, basis, **kwargs): r""" Create a subalgebra of this algebra from the given basis. - - This is a simple wrapper around a subalgebra class constructor - that can be overridden in subclasses. """ from mjo.eja.eja_subalgebra import FiniteDimensionalEJASubalgebra return FiniteDimensionalEJASubalgebra(self, basis, **kwargs) @@ -1293,7 +1391,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule): return self.zero().to_vector().parent().ambient_vector_space() - Element = FiniteDimensionalEJAElement class RationalBasisEJA(FiniteDimensionalEJA): r""" @@ -2844,6 +2941,9 @@ class CartesianProductEJA(CombinatorialFreeModule_CartesianProduct, True """ + Element = FiniteDimensionalEJAElement + + def __init__(self, algebras, **kwargs): CombinatorialFreeModule_CartesianProduct.__init__(self, algebras, @@ -3107,53 +3207,7 @@ class CartesianProductEJA(CombinatorialFreeModule_CartesianProduct, return FiniteDimensionalEJAOperator(Ji,self,Ei.matrix()) - def _element_constructor_(self, elt): - r""" - Construct an element of this algebra from an ordered tuple. - - We just apply the element constructor from each of our factors - to the corresponding component of the tuple, and package up - the result. - - SETUP:: - - sage: from mjo.eja.eja_algebra import (HadamardEJA, - ....: RealSymmetricEJA) - - EXAMPLES:: - - sage: J1 = HadamardEJA(3) - sage: J2 = RealSymmetricEJA(2) - sage: J = cartesian_product([J1,J2]) - sage: J( (J1.matrix_basis()[1], J2.matrix_basis()[2]) ) - e(0, 1) + e(1, 2) - """ - m = len(self.cartesian_factors()) - try: - z = tuple( self.cartesian_factors()[i](elt[i]) for i in range(m) ) - return self._cartesian_product_of_elements(z) - except: - raise ValueError("not an element of this algebra") - - def subalgebra(self, basis, **kwargs): - r""" - Create a subalgebra of this algebra from the given basis. - - This overrides the superclass method to use a special class - for Cartesian products. - """ - from mjo.eja.eja_subalgebra import CartesianProductEJASubalgebra - return CartesianProductEJASubalgebra(self, basis, **kwargs) - - Element = CartesianProductEJAElement - FiniteDimensionalEJA.CartesianProduct = CartesianProductEJA random_eja = ConcreteEJA.random_instance -#def random_eja(*args, **kwargs): -# from sage.categories.cartesian_product import cartesian_product -# J1 = HadamardEJA(1, **kwargs) -# J2 = RealSymmetricEJA(2, **kwargs) -# J = cartesian_product([J1,J2]) -# return J