X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=0f2b655f21795cb1feb0e8b41cfc878261a67962;hb=c4203897950b84665ea41ed103f87f68aee0852e;hp=12207b7c5a8e897738ab21a73361883cae03626f;hpb=5d646c586de50b571d2983b546a05899bf0c20c2;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 12207b7..0f2b655 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -5,7 +5,7 @@ are used in optimization, and have some additional nice methods beyond what can be supported in a general Jordan Algebra. """ -from itertools import izip, repeat +from itertools import repeat from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra from sage.categories.magmatic_algebras import MagmaticAlgebras @@ -13,6 +13,7 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix from sage.matrix.matrix_space import MatrixSpace from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import lazy_import from sage.misc.prandom import choice from sage.misc.table import table from sage.modules.free_module import FreeModule, VectorSpace @@ -20,6 +21,8 @@ from sage.rings.all import (ZZ, QQ, RR, RLF, CLF, PolynomialRing, QuadraticField) from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement +lazy_import('mjo.eja.eja_subalgebra', + 'FiniteDimensionalEuclideanJordanSubalgebra') from mjo.eja.eja_utils import _mat2vec class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): @@ -631,6 +634,119 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): return self.linear_combination(zip(self.gens(), coeffs)) + def peirce_decomposition(self, c): + """ + The Peirce decomposition of this algebra relative to the + idempotent ``c``. + + In the future, this can be extended to a complete system of + orthogonal idempotents. + + INPUT: + + - ``c`` -- an idempotent of this algebra. + + OUTPUT: + + A triple (J0, J5, J1) containing two subalgebras and one subspace + of this algebra, + + - ``J0`` -- the algebra on the eigenspace of ``c.operator()`` + corresponding to the eigenvalue zero. + + - ``J5`` -- the eigenspace (NOT a subalgebra) of ``c.operator()`` + corresponding to the eigenvalue one-half. + + - ``J1`` -- the algebra on the eigenspace of ``c.operator()`` + corresponding to the eigenvalue one. + + These are the only possible eigenspaces for that operator, and this + algebra is a direct sum of them. The spaces ``J0`` and ``J1`` are + orthogonal, and are subalgebras of this algebra with the appropriate + restrictions. + + SETUP:: + + sage: from mjo.eja.eja_algebra import random_eja, RealSymmetricEJA + + EXAMPLES: + + The canonical example comes from the symmetric matrices, which + decompose into diagonal and off-diagonal parts:: + + sage: J = RealSymmetricEJA(3) + sage: C = matrix(QQ, [ [1,0,0], + ....: [0,1,0], + ....: [0,0,0] ]) + sage: c = J(C) + sage: J0,J5,J1 = J.peirce_decomposition(c) + sage: J0 + Euclidean Jordan algebra of dimension 1... + sage: J5 + Vector space of degree 6 and dimension 2... + sage: J1 + Euclidean Jordan algebra of dimension 3... + + TESTS: + + 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 + True + sage: J1.superalgebra() == J and J1.dimension() == J.dimension() + True + + The identity 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(): + ....: while x.is_nilpotent(): + ....: x = J.random_element() + sage: c = x.subalgebra_idempotent() + sage: J0,J5,J1 = J.peirce_decomposition(c) + sage: J1(c) == J1.one() + True + sage: J0(J.one() - c) == J0.one() + True + + """ + if not c.is_idempotent(): + raise ValueError("element is not idempotent: %s" % c) + + # Default these to what they should be if they turn out to be + # trivial, because eigenspaces_left() won't return eigenvalues + # corresponding to trivial spaces (e.g. it returns only the + # eigenspace corresponding to lambda=1 if you take the + # decomposition relative to the identity element). + trivial = FiniteDimensionalEuclideanJordanSubalgebra(self, ()) + J0 = trivial # eigenvalue zero + J5 = VectorSpace(self.base_ring(), 0) # eigenvalue one-half + J1 = trivial # eigenvalue one + + for (eigval, eigspace) in c.operator().matrix().left_eigenspaces(): + if eigval == ~(self.base_ring()(2)): + J5 = eigspace + else: + gens = tuple( self.from_vector(b) for b in eigspace.basis() ) + subalg = FiniteDimensionalEuclideanJordanSubalgebra(self, gens) + if eigval == 0: + J0 = subalg + elif eigval == 1: + J1 = subalg + else: + raise ValueError("unexpected eigenvalue: %s" % eigval) + + return (J0, J5, J1) + + def random_elements(self, count): """ Return ``count`` random elements as a tuple. @@ -916,7 +1032,7 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): basis = tuple( s.change_ring(field) for s in basis ) self._basis_normalizers = tuple( ~(self.natural_inner_product(s,s).sqrt()) for s in basis ) - basis = tuple(s*c for (s,c) in izip(basis,self._basis_normalizers)) + basis = tuple(s*c for (s,c) in zip(basis,self._basis_normalizers)) Qs = self.multiplication_table_from_matrix_basis(basis) @@ -939,8 +1055,8 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): # with had entries in a nice field. return super(MatrixEuclideanJordanAlgebra, self)._charpoly_coeff(i) else: - basis = ( (b/n) for (b,n) in izip(self.natural_basis(), - self._basis_normalizers) ) + basis = ( (b/n) for (b,n) in zip(self.natural_basis(), + self._basis_normalizers) ) # Do this over the rationals and convert back at the end. J = MatrixEuclideanJordanAlgebra(QQ, @@ -950,7 +1066,7 @@ class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): (_,x,_,_) = J._charpoly_matrix_system() p = J._charpoly_coeff(i) # p might be missing some vars, have to substitute "optionally" - pairs = izip(x.base_ring().gens(), self._basis_normalizers) + pairs = zip(x.base_ring().gens(), self._basis_normalizers) substitutions = { v: v*c for (v,c) in pairs } result = p.subs(substitutions)