# Ticket #6670: trac-6670-group_algebra-5.patch

File trac-6670-group_algebra-5.patch, 27.9 KB (added by David Loeffler, 12 years ago)

Apply only this -- rebased against 4.5.alpha1 and docstrings ReSTified

• ## doc/en/reference/algebras.rst

# HG changeset patch
# User David Loeffler <d.loeffler.01@cantab.net>
# Date 1278150262 -3600
diff -r e076fccdbc27 -r 276b87ad937f doc/en/reference/algebras.rst
diff -r e076fccdbc27 -r 276b87ad937f sage/algebras/group_algebra.py
 a r""" Class for group algebras of arbitrary groups (over a general commutative base ring). Group algebra of a group NOTE: -- It seems to be impossible to make this fit nicely with Sage's coercion model. The problem is that (for example) if G is the additive group (ZZ,+), and R = ZZ[G] is its group ring, then the integer 2 can be coerced into R in two ways -- via G, or via the base ring -- and *the answers are different*. In practice we get around this by preventing elements of G coercing automatically into ZZ[G], which is a shame, but makes more sense than preventing elements of the base ring doing so. This module provides functionality for constructing and working with group algebras of arbitrary groups (over a general commutative base ring). .. note:: It seems to be difficult to make this fit nicely with Sage's coercion model. The problem is that (for example) if G is the additive group (\ZZ,+), and R = \ZZ[G] is its group ring, then the integer 2 can be coerced into R in two ways -- via G, or via the base ring -- and *the answers are different*. In practice we get around this by preventing elements of G coercing automatically into \ZZ[G], which is a shame, but makes more sense than preventing elements of the base ring doing so. AUTHOR: -- David Loeffler (2008-08-24): initial version - David Loeffler (2008-08-24): initial version - Martin Raum (2009-08): update to use new coercion model """ #***************************************************************************** from sage.rings.all import IntegerRing from sage.groups.group import Group from sage.structure.formal_sum import FormalSums_generic, FormalSums, FormalSum from sage.structure.element import Element from sage.sets.set import Set from sage.misc.latex import latex from sage.categories.morphism import Morphism from sage.categories.pushout import ConstructionFunctor from sage.categories.rings import Rings class GroupAlgebra(Algebra): def __init__(self, group, base_ring = IntegerRing()): r""" Create the given group algebra. INPUT: -- (Group) group: a generic group. -- (Ring) base_ring: a commutative ring. OUTPUT: -- a GroupAlgebra instance. _cache = dict() class GroupAlgebraFunctor ( ConstructionFunctor ) : r""" A functor assigning a group algebra to its base ring. """ def __init__(self, group) : r""" INPUT : - group -- a group that will be the module basis for the group algebras in the range. EXAMPLES:: sage: GroupAlgebra(GL(3, GF(7))) Group algebra of group "General Linear Group of degree 3 over Finite Field of size 7" over base ring Integer Ring sage: GroupAlgebra(1) Traceback (most recent call last): ... TypeError: "1" is not a group sage: from sage.algebras.group_algebra import GroupAlgebraFunctor sage: F = GroupAlgebraFunctor(KleinFourGroup()) sage: loads(dumps(F)) == F True sage: GroupAlgebra(SU(2, GF(4, 'a')), IntegerModRing(12)).category() Category of group algebras over Ring of integers modulo 12 """ self.__group = group ConstructionFunctor.__init__(self, Rings(), Rings()) def group(self) : r""" Return the group which is associtated to this functor. EXAMPLES:: sage: from sage.algebras.group_algebra import GroupAlgebraFunctor sage: GroupAlgebraFunctor(CyclicPermutationGroup(17)).group() == CyclicPermutationGroup(17) True """ if not base_ring.is_commutative(): raise NotImplementedError, "Base ring must be commutative" return self.__group def __call__(self, base_ring) : r""" Create the group algeba with given base ring over self.group(). INPUT : - base_ring - the base ring of the group algebra. OUTPUT: A group algebra. EXAMPLES:: sage: from sage.algebras.group_algebra import GroupAlgebraFunctor sage: F = GroupAlgebraFunctor(CyclicPermutationGroup(17)) sage: F(QQ) Group algebra of group "Cyclic group of order 17 as a permutation group" over base ring Rational Field """ return GroupAlgebra(self.__group, base_ring) def GroupAlgebra(group, base_ring = IntegerRing()) : r""" Create the given group algebra. INPUT:: - (Group) group: a generic group. - (Ring) base_ring: a commutative ring. OUTPUT: - a GroupAlgebra instance. EXAMPLES:: sage: GroupAlgebra(GL(3, GF(7))) Group algebra of group "General Linear Group of degree 3 over Finite Field of size 7" over base ring Integer Ring sage: GroupAlgebra(1) Traceback (most recent call last): ... TypeError: "1" is not a group sage: GroupAlgebra(KleinFourGroup()) is GroupAlgebra(KleinFourGroup()) True """ global _cache if not base_ring.is_commutative(): raise NotImplementedError, "Base ring must be commutative" if not isinstance(group, Group): raise TypeError, '"%s" is not a group' % group key = (group, base_ring) try : return _cache[key] except KeyError : A = GroupAlgebra_class(group, base_ring) _cache[key] = A return A class GroupAlgebra_class ( Algebra ) : def __init__(self, group, base_ring): r""" EXAMPLES:: sage: A = GroupAlgebra(GL(3, GF(7))) sage: A.has_coerce_map_from(GL(3, GF(7))) True """ if not isinstance(group, Group): raise TypeError, '"%s" is not a group' % group ParentWithGens.__init__(self, base_ring, category = GroupAlgebras(base_ring)) self._formal_sum_module = FormalSums(base_ring) self._group = group if not base_ring.has_coerce_map_from(group) : ## some matrix groups assume that coercion is only valid to ## other matrix groups. This is a workaround ## call _element_constructor_ to coerce group elements #try : self._populate_coercion_lists_( coerce_list = [base_ring, group] ) #except TypeError : #    self._populate_coercion_lists_( coerce_list = [base_ring] ) else : self._populate_coercion_lists_( coerce_list = [base_ring] ) def ngens(self) : r""" Return the number of generators. EXAMPLES:: sage: A = GroupAlgebra(SL2Z) sage: A.ngens() 2 """ return self._group.ngens() def gen(self, i = 0) : r""" EXAMPLES:: sage: A = GroupAlgebra(GL(3, GF(7))) sage: A.gen(0) [3 0 0] [0 1 0] [0 0 1] """ return self._element_constructor(self._group.gen(i), as_group_element = True) def group(self): r""" Return the group of this group algebra. EXAMPLES: r""" Return the group of this group algebra. EXAMPLES:: sage: GroupAlgebra(GL(3, GF(11))).group() General Linear Group of degree 3 over Finite Field of size 11 sage: GroupAlgebra(SymmetricGroup(10)).group() """ return self._group ########################################################################### ## GroupAlgebra_class :: mathmatical properties ########################################################################### def is_commutative(self): r""" Return True if self is a commutative ring. True if and only if self.group() is abelian. r""" Return True if self is a commutative ring. True if and only if self.group() is abelian. EXAMPLES: EXAMPLES:: sage: GroupAlgebra(SymmetricGroup(2)).is_commutative() True sage: GroupAlgebra(SymmetricGroup(3)).is_commutative() return self.group().is_abelian() def is_field(self, proof = True): r""" Return True if self is a field. This is always false unless self.group() is trivial and self.base_ring() is a field. EXAMPLES: r""" Return True if self is a field. This is always false unless self.group() is trivial and self.base_ring() is a field. EXAMPLES:: sage: GroupAlgebra(SymmetricGroup(2)).is_field() False sage: GroupAlgebra(SymmetricGroup(1)).is_field() return (self.group().order() == 1) def is_finite(self): r""" Return True if self is finite, which is true if and only if self.group() and self.base_ring() are both finite. r""" Return True if self is finite, which is true if and only if self.group() and self.base_ring() are both finite. EXAMPLES: EXAMPLES:: sage: GroupAlgebra(SymmetricGroup(2), IntegerModRing(10)).is_finite() True sage: GroupAlgebra(SymmetricGroup(2)).is_finite() return (self.base_ring().is_finite() and self.group().is_finite()) def is_exact(self): r""" Return True if elements of self have exact representations, which is true of self if and only if it is true of self.group() and self.base_ring(). r""" Return True if elements of self have exact representations, which is true of self if and only if it is true of self.group() and self.base_ring(). EXAMPLES: EXAMPLES:: sage: GroupAlgebra(GL(3, GF(7))).is_exact() True sage: GroupAlgebra(GL(3, GF(7)), RR).is_exact() return self.group().is_exact() and self.base_ring().is_exact() def is_integral_domain(self, proof = True): r""" Return True if self is an integral domain. r""" Return True if self is an integral domain. This is false unless self.base_ring() is an integral domain, and even then it is false unless self.group() has no nontrivial elements of finite order. I don't know if this condition suffices, but it obviously does if the group is abelian and finitely generated. This is false unless self.base_ring() is an integral domain, and even then it is false unless self.group() has no nontrivial elements of finite order. I don't know if this condition suffices, but it obviously does if the group is abelian and finitely generated. EXAMPLES: EXAMPLES:: sage: GroupAlgebra(SymmetricGroup(2)).is_integral_domain() False sage: GroupAlgebra(SymmetricGroup(1)).is_integral_domain() # I don't know if that means "is canonically isomorphic to a prime field" # or "is identical to a prime field". def _coerce_impl(self, x): return self(self.base_ring().coerce(x)) ########################################################################### ## GroupAlgebra_class :: elements ########################################################################### def _an_element_impl(self): def _an_element_(self): r""" Return an element of self. EXAMPLES:: sage: A = GroupAlgebra(GL(3, GF(7))) sage: A.an_element() [3 0 0] [0 1 0] [0 0 1] """ Return an element of self. EXAMPLE: return self(self._formal_sum_module([ (self.base_ring().an_element(), self.group().an_element()) ])) def random_element(self) : r""" Return a random element of self. EXAMPLE:: sage: GroupAlgebra(SU(2, 13), QQ).an_element() # random; hideous formatting! -1/95*[       9 2*a + 12] [       0        3] - 4*[      9 9*a + 2] [3*a + 5       1] """ try: return self(self._formal_sum_module([ (self.base_ring().random_element(), self.group().random_element()), (self.base_ring().random_element(), self.group().random_element()), ])) except: # base ring or group might not implement .random_element() return self(self._formal_sum_module([ (self.base_ring().an_element(), self.group().an_element()) ])) return self(self._formal_sum_module([ (self.base_ring().random_element(), self.group().random_element()), (self.base_ring().random_element(), self.group().random_element()), ])) def __call__(self, x, check=True): def construction(self) : r""" EXAMPLES:: sage: A = GroupAlgebra(KleinFourGroup(), QQ) sage: A.construction() (GroupAlgebraFunctor, Rational Field) """ return GroupAlgebraFunctor(self._group), self.base_ring() def _element_constructor_(self, x, as_group_element = False) : r""" Create an element of this group algebra. INPUT: -- x: either a FormalSum element consisting of elements of self.group(), an element of self.base_ring(), or an element of self.group(). -- check (boolean): whether or not to check that the given elements really do lie in self.group(). Chiefly provided to speed up arithmetic operations with elements that have already been checked to lie in the group. - x: either a FormalSum element consisting of elements of self.group(), an element of self.base_ring(), or an element of self.group(). - as_group_element: if true and x may be considered as a basis element and a group element, consider it a group element OUTPUT: -- a GroupAlgebraElement instance whose parent is self. EXAMPLES: sage: G = AbelianGroup(1) sage: f = G.gen() - a GroupAlgebraElement instance whose parent is self. EXAMPLES:: sage: G = KleinFourGroup() sage: f = G.gen(0) sage: ZG = GroupAlgebra(G) sage: ZG(f) f (3,4) sage: ZG(1) == ZG(G(1)) True sage: G = AbelianGroup(1) sage: ZG = GroupAlgebra(G) sage: f = ZG.group().gen() sage: ZG(FormalSum([(1,f), (2, f**2)])) 2*f^2 + f f + 2*f^2 sage: G = GL(2,7) sage: OG = GroupAlgebra(G, ZZ[sqrt(5)]) sage: OG(2) sage: OG(FormalSum([ (1, G(2)), (2, RR(0.77)) ]) ) Traceback (most recent call last): ... TypeError: 0.770000000000000 is not an element of group General Linear Group of degree 2 over Finite Field of size 7 TypeError: Cannot coerce 0.770000000000000 to a 2-by-2 matrix over Finite Field of size 7 Ordering of elements in output unpredictable as sort order of such wildly dissimilar elements is subject to change between platforms and versions (see trac ticket \#4373). sage: OG(FormalSum([ (1, G(2)), (2, RR(0.77)) ]), check=False) # random [2 0] [0 2] + 2*0.770000000000000 :: sage: OG(OG.base_ring().gens()[1]) sqrt5*[1 0] [0 1] """ return GroupAlgebraElement(self, x, check) """ if isinstance(x, Element) : P = x.parent() if P is self.base_ring() and not as_group_element: return GroupAlgebraElement_class(self, self._formal_sum_module( [(x, self.group()(1))] ) ) elif P is self.group() : return GroupAlgebraElement_class(self, self._formal_sum_module( [(self.base_ring().one_element(), x)] ) ) elif P is self._formal_sum_module : return GroupAlgebraElement_class(self, x) elif isinstance(x, FormalSum) and self._formal_sum_module.has_coerce_map_from(P) : return GroupAlgebraElement_class(self, self._formal_sum_module( [(self.base_ring()(c), self.group()(g)) for c,g in x] ) ) def __eq__(self, other): r""" Test for equality. EXAMPLES: raise TypeError, "Don't know how to create an element of %s from %s" % \ (self, x) def __cmp__(self, other) : r""" Compare two algebras self and other. They are considered equal if and only if their base rings and their groups coincide. EXAMPLES:: sage: GroupAlgebra(AbelianGroup(1)) == GroupAlgebra(AbelianGroup(1)) True sage: GroupAlgebra(AbelianGroup(1), QQ) == GroupAlgebra(AbelianGroup(1), ZZ) False sage: GroupAlgebra(AbelianGroup(2)) == GroupAlgebra(AbelianGroup(1)) False """ if not isinstance(other, GroupAlgebra): return False else: return self.base_ring() == other.base_ring() and self.group() == other.group() sage: from sage.algebras.group_algebra import GroupAlgebra_class sage: A = GroupAlgebra_class(KleinFourGroup(), ZZ) sage: B = GroupAlgebra_class(KleinFourGroup(), QQ) sage: A == B False sage: A == A True """ c = cmp(type(self), type(other)) if c == 0 : c = cmp(self._group, other._group) if c == 0 : c = cmp(self.base_ring(), other.base_ring()) return c def _repr_(self): r""" String representation of self. See GroupAlgebra.__init__ for a doctest.""" return "Group algebra of group \"%s\" over base ring %s" % (self.group(), self.base_ring()) r""" String representation of self. See GroupAlgebra.__init__ for a doctest. EXAMPLES:: sage: A = GroupAlgebra(KleinFourGroup(), ZZ) sage: A Group algebra of group "The Klein 4 group of order 4, as a permutation group" over base ring Integer Ring """ return "Group algebra of group \"%s\" over base ring %s" % \ (self.group(), self.base_ring()) def _latex_(self): r"""Latex string of self. EXAMPLES:: sage: A = GroupAlgebra(KleinFourGroup(), ZZ) sage: latex(A) Group algebra of group \langle (3,4), (1,2) \rangle over base ring \Bold{Z} """ return "Group algebra of group %s over base ring %s" % \ (latex(self.group()), latex(self.base_ring())) def element_class(self): r""" The class of elements of self, which is GroupAlgebraElement. EXAMPLES: EXAMPLES:: sage: GroupAlgebra(SU(2, GF(4,'a'))).element_class() """ return GroupAlgebraElement return GroupAlgebraElement_class def GroupAlgebraElement(parent, x, check = True) : r""" Create an element of the group algebra parent. class GroupAlgebraElement(AlgebraElement): - x -- an element of the base ring, the group or a formal sum with summand (a, g), where a is in the base ring and g is in the group - check -- determines whether the summand of a formal sum will be checked for correctness def __init__(self, parent, x, check): r""" Create an element of the parent group algebra. Not intended to be called by the user; see GroupAlgebra.__call__ for examples and doctests.""" EXAMPLES:: sage: G = KleinFourGroup() sage: A = GroupAlgebra(G) sage: GroupAlgebraElement(A, A.group().gen(1)) (1,2) sage: GroupAlgebraElement(A, 1) () sage: GroupAlgebraElement(A, 10) 10*() """ ## Provide this for backward compatibility to the old class layout assert isinstance(parent, GroupAlgebra_class), "%s must be group algebra" if not isinstance(x, Element) : x = IntegerRing()(x) if isinstance(x, FormalSum) and check : for c,g in x : if not c in parent.base_ring() or not g in parent.group() : raise ValueError, "summands of %s must be elements of base " + \ "ring and group respectively" return parent._element_constructor(x) ########################################################################### ## GroupAlgebraElement_class ########################################################################### class GroupAlgebraElement_class ( AlgebraElement ) : def __init__(self, parent, x): r""" Create an element of the parent group algebra. Not intended to be called by the user; see GroupAlgebra._element_constructor_ for examples and doctests. EXAMPLES:: sage: A = GroupAlgebra(KleinFourGroup(), ZZ) sage: GroupAlgebraElement(A, A._formal_sum_module([(1, KleinFourGroup().gen(0))])) (3,4) """ AlgebraElement.__init__(self, parent) self._fs = x if not hasattr(x, 'parent'): x = IntegerRing()(x) # occasionally coercion framework tries to pass a Python int ########################################################################### ## GroupAlgebraElement_class :: Arithmetic ########################################################################### if isinstance(x, FormalSum): if check: for c,d in x._data: if d.parent() != self.parent().group(): raise TypeError, "%s is not an element of group %s" % (d, self.parent().group()) self._fs = x else: self._fs = x def _add_(left, right): r""" Add left to right. elif self.base_ring().has_coerce_map_from(x.parent()): self._fs = self.parent()._formal_sum_module([ (x, self.parent().group()(1)) ]) elif self.parent().group().has_coerce_map_from(x.parent()): self._fs = self.parent()._formal_sum_module([ (1, self.parent().group()(x)) ]) else: raise TypeError, "Don't know how to create an element of %s from %s" % (self.parent(), x) EXAMPLE:: def _repr_(self): return self._fs._repr_() def _add_(self, other): r""" Add self to other. EXAMPLE: sage: G = GL(3, GF(7)) sage: ZG = GroupAlgebra(G) sage: g1 = G([0,0,2,2,5,0,6,6,2]) sage: g1 = ZG.group()([0,0,2,2,5,0,6,6,2]) sage: s = ZG(g1) sage: s + s 2*[0  0  2] [2  5  0] [6  6  2] """ fs_sum = self._fs + other._fs return self.parent()(fs_sum, check=False) """ return left.parent()(left._fs + right._fs) def _mul_(self, right): r""" Calculate self*right, where both self and right are GroupAlgebraElements. def _mul_(left, right): r""" Calculate left*right, where both left and right are GroupAlgebraElements_class. EXAMPLE: EXAMPLE:: sage: G = GL(3, GF(7)) sage: ZG = GroupAlgebra(G) sage: a, b = G.random_element(), G.random_element() sage: za*za == za^2 True """ d1 = self._fs._data d2 = right._fs._data new = [] for (a1, g1) in d1: for a2,g2 in d2: if self.parent().group().is_multiplicative(): new.append( (a1*a2, g1*g2) ) else: new.append( (a1*a2, g1 + g2) ) return self.parent()( self.parent()._formal_sum_module(new), check=False) if left.parent().group().is_multiplicative() : product = [ (a1 * a2, g1 * g2) for (a1,g1) in left._fs for (a2,g2) in right._fs ] else : product = [ (a1 * a2, g1 + g2) for (a1,g1) in left._fs for (a2,g2) in right._fs ] def __eq__(self, other): r""" Test if self is equal to other. return left.parent()( left.parent()._formal_sum_module(product) ) EXAMPLES: ########################################################################### ## GroupAlgebraElement_class :: Comparision ########################################################################### def __cmp__(self, other) : r""" Compare self and other. EXAMPLES:: sage: G = AbelianGroup(1,[4]) sage: a = GroupAlgebra(G)(1) sage: b = GroupAlgebra(G)(2) sage: ZG = GroupAlgebra(G) sage: a = ZG(1) sage: b = ZG(2) sage: a + a == b True sage: a == b sage: a == GroupAlgebra(AbelianGroup(1, [5]))(1) False """ if isinstance(other, GroupAlgebraElement) and self.parent() == other.parent(): return self._fs == other._fs else: return False c = cmp(type(self), type(other)) if c == 0 : c = cmp(self._fs, other._fs) return c ########################################################################### ## GroupAlgebraElement_class :: Representations ########################################################################### def _repr_(self) : r""" EXAMPLES:: sage: G = KleinFourGroup() sage: A = GroupAlgebra(G, ZZ) sage: latex(A(1) + A(A.group().gen(0))) () + (3,4) """ return self._fs._repr_() def _latex_(self) : r""" EXAMPLES:: sage: G = KleinFourGroup() sage: A = GroupAlgebra(G, ZZ) sage: latex(A(1) + A(A.group().gen(0))) () + (3,4) """ return latex(self._fs)