Path: blob/master/src/sage/groups/perm_gps/permgroup.py
8815 views
# -*- coding: utf-8 -*-1r"""2Permutation groups34A permutation group is a finite group `G` whose elements are5permutations of a given finite set `X` (i.e., bijections6`X \longrightarrow X`) and whose group operation is the composition of7permutations. The number of elements of `X` is called the degree of `G`.89In Sage, a permutation is represented as either a string that10defines a permutation using disjoint cycle notation, or a list of11tuples, which represent disjoint cycles. That is::1213(a,...,b)(c,...,d)...(e,...,f) <--> [(a,...,b), (c,...,d),..., (e,...,f)]14() = identity <--> []1516You can make the "named" permutation groups (see17``permgp_named.py``) and use the following18constructions:1920- permutation group generated by elements,21- ``direct_product_permgroups``, which takes a list of permutation22groups and returns their direct product.2324JOKE: Q: What's hot, chunky, and acts on a polygon? A: Dihedral25soup. Renteln, P. and Dundes, A. "Foolproof: A Sampling of26Mathematical Folk Humor." Notices Amer. Math. Soc. 52, 24-34,272005.2829AUTHORS:3031- David Joyner (2005-10-14): first version3233- David Joyner (2005-11-17)3435- William Stein (2005-11-26): rewrite to better wrap Gap3637- David Joyner (2005-12-21)3839- William Stein and David Joyner (2006-01-04): added conjugacy_class_representatives4041- David Joyner (2006-03): reorganization into subdirectory perm_gps;42added __contains__, has_element; fixed _cmp_; added subgroup43class+methods, PGL,PSL,PSp, PSU classes,4445- David Joyner (2006-06): added PGU, functionality to SymmetricGroup,46AlternatingGroup, direct_product_permgroups4748- David Joyner (2006-08): added degree,49ramification_module_decomposition_modular_curve and50ramification_module_decomposition_hurwitz_curve methods to PSL(2,q),51MathieuGroup, is_isomorphic5253- Bobby Moretti (2006)-10): Added KleinFourGroup, fixed bug in DihedralGroup5455- David Joyner (2006-10): added is_subgroup (fixing a bug found by56Kiran Kedlaya), is_solvable, normalizer, is_normal_subgroup, Suzuki5758- David Kohel (2007-02): fixed __contains__ to not enumerate group59elements, following the convention for __call__6061- David Harvey, Mike Hansen, Nick Alexander, William Stein62(2007-02,03,04,05): Various patches6364- Nathan Dunfield (2007-05): added orbits6566- David Joyner (2007-06): added subgroup method (suggested by David67Kohel), composition_series, lower_central_series,68upper_central_series, cayley_table, quotient_group, sylow_subgroup,69is_cyclic, homology, homology_part, cohomology, cohomology_part,70poincare_series, molien_series, is_simple, is_monomial,71is_supersolvable, is_nilpotent, is_perfect, is_polycyclic,72is_elementary_abelian, is_pgroup, gens_small,73isomorphism_type_info_simple_group. moved all the"named"74groups to a new file.7576- Nick Alexander (2007-07): move is_isomorphic to isomorphism_to, add77from_gap_list7879- William Stein (2007-07): put is_isomorphic back (and make it better)8081- David Joyner (2007-08): fixed bugs in composition_series,82upper/lower_central_series, derived_series,8384- David Joyner (2008-06): modified is_normal (reported by85W. J. Palenstijn), and added normalizes8687- David Joyner (2008-08): Added example to docstring of cohomology.8889- Simon King (2009-04): __cmp__ methods for PermutationGroup_generic and PermutationGroup_subgroup9091- Nicolas Borie (2009): Added orbit, transversals, stabiliser and strong_generating_system methods9293- Christopher Swenson (2012): Added a special case to compute the order efficiently.94(This patch Copyright 2012 Google Inc. All Rights Reserved. )9596- Javier Lopez Pena (2013): Added conjugacy classes.9798REFERENCES:99100- Cameron, P., Permutation Groups. New York: Cambridge University101Press, 1999.102103- Wielandt, H., Finite Permutation Groups. New York: Academic Press,1041964.105106- Dixon, J. and Mortimer, B., Permutation Groups, Springer-Verlag,107Berlin/New York, 1996.108109.. note::110111Though Suzuki groups are okay, Ree groups should *not* be wrapped112as permutation groups - the construction is too slow - unless (for113small values or the parameter) they are made using explicit114generators.115116"""117118#*****************************************************************************119# Copyright (C) 2006 William Stein <[email protected]>120# David Joyner <[email protected]>121#122# Distributed under the terms of the GNU General Public License (GPL)123# http://www.gnu.org/licenses/124#*****************************************************************************125from functools import wraps126127from sage.misc.randstate import current_randstate128import sage.groups.old as group129130from sage.rings.all import QQ, Integer131from sage.interfaces.all import is_ExpectElement132from sage.interfaces.gap import gap, GapElement133from sage.groups.perm_gps.permgroup_element import PermutationGroupElement, standardize_generator134from sage.groups.abelian_gps.abelian_group import AbelianGroup135from sage.misc.cachefunc import cached_method136from sage.groups.class_function import ClassFunction137from sage.misc.package import is_package_installed138from sage.sets.finite_enumerated_set import FiniteEnumeratedSet139from sage.categories.all import FiniteEnumeratedSets140from sage.groups.conjugacy_classes import ConjugacyClassGAP141from sage.functions.other import factorial142143144def load_hap():145"""146Load the GAP hap package into the default GAP interpreter147interface. If this fails, try one more time to load it.148149EXAMPLES::150151sage: sage.groups.perm_gps.permgroup.load_hap() # optional - gap_packages152"""153try:154gap.load_package("hap")155except Exception:156gap.load_package("hap")157158def hap_decorator(f):159"""160A decorator for permutation group methods that require HAP. It161checks to see that HAP is installed as well as checks that the162argument ``p`` is either 0 or prime.163164EXAMPLES::165166sage: from sage.groups.perm_gps.permgroup import hap_decorator167sage: def foo(self, n, p=0): print "Done"168sage: foo = hap_decorator(foo)169sage: foo(None, 3) #optional - gap_packages170Done171sage: foo(None, 3, 0) # optional - gap_packages172Done173sage: foo(None, 3, 5) # optional - gap_packages174Done175sage: foo(None, 3, 4) #optional - gap_packages176Traceback (most recent call last):177...178ValueError: p must be 0 or prime179"""180@wraps(f)181def wrapped(self, n, p=0):182if not is_package_installed('gap_packages'):183raise RuntimeError, "You must install the optional gap_packages package."184load_hap()185from sage.rings.arith import is_prime186if not (p == 0 or is_prime(p)):187raise ValueError, "p must be 0 or prime"188189return f(self, n, p=p)190return wrapped191192def direct_product_permgroups(P):193"""194Takes the direct product of the permutation groups listed in ``P``.195196EXAMPLES::197198sage: G1 = AlternatingGroup([1,2,4,5])199sage: G2 = AlternatingGroup([3,4,6,7])200sage: D = direct_product_permgroups([G1,G2,G1])201sage: D.order()2021728203sage: D = direct_product_permgroups([G1])204sage: D==G1205True206sage: direct_product_permgroups([])207Symmetric group of order 0! as a permutation group208"""209n = len(P)210if n == 0:211from sage.groups.perm_gps.permgroup_named import SymmetricGroup212return SymmetricGroup(0)213elif n == 1:214return P[0]215else:216G = gap.DirectProduct(*P)217return PermutationGroup(gap_group=G)218219def from_gap_list(G, src):220r"""221Convert a string giving a list of GAP permutations into a list of222elements of ``G``.223224EXAMPLES::225226sage: from sage.groups.perm_gps.permgroup import from_gap_list227sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])228sage: L = from_gap_list(G, "[(1,2,3)(4,5), (3,4)]"); L229[(1,2,3)(4,5), (3,4)]230sage: L[0].parent() is G231True232sage: L[1].parent() is G233True234"""235# trim away the list constructs236src = src.replace("[", "").replace("]", "")237# cut out the individual elements238srcs = src.split("),")239for i in range(len(srcs[:-1])):240srcs[i] = srcs[i] + ")"241srcs = map(G, srcs)242return srcs243244def PermutationGroup(gens=None, gap_group=None, domain=None, canonicalize=True, category=None):245"""246Return the permutation group associated to `x` (typically a247list of generators).248249INPUT:250251252- ``gens`` - list of generators (default: ``None``)253254- ``gap_group`` - a gap permutation group (default: ``None``)255256- ``canonicalize`` - bool (default: ``True``); if ``True``,257sort generators and remove duplicates258259260OUTPUT:261262- A permutation group.263264EXAMPLES::265266sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])267sage: G268Permutation Group with generators [(3,4), (1,2,3)(4,5)]269270We can also make permutation groups from PARI groups::271272sage: H = pari('x^4 - 2*x^3 - 2*x + 1').polgalois()273sage: G = PariGroup(H, 4); G274PARI group [8, -1, 3, "D(4)"] of degree 4275sage: H = PermutationGroup(G); H # optional - database_gap276Transitive group number 3 of degree 4277sage: H.gens() # optional - database_gap278[(1,2,3,4), (1,3)]279280We can also create permutation groups whose generators are Gap281permutation objects::282283sage: p = gap('(1,2)(3,7)(4,6)(5,8)'); p284(1,2)(3,7)(4,6)(5,8)285sage: PermutationGroup([p])286Permutation Group with generators [(1,2)(3,7)(4,6)(5,8)]287288Permutation groups can work on any domain. In the following289examples, the permutations are specified in list notation,290according to the order of the elements of the domain::291292sage: list(PermutationGroup([['b','c','a']], domain=['a','b','c']))293[(), ('a','b','c'), ('a','c','b')]294sage: list(PermutationGroup([['b','c','a']], domain=['b','c','a']))295[()]296sage: list(PermutationGroup([['b','c','a']], domain=['a','c','b']))297[(), ('a','b')]298299There is an underlying gap object that implements each300permutation group::301302sage: G = PermutationGroup([[(1,2,3,4)]])303sage: G._gap_()304Group( [ (1,2,3,4) ] )305sage: gap(G)306Group( [ (1,2,3,4) ] )307sage: gap(G) is G._gap_()308True309sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])310sage: current_randstate().set_seed_gap()311sage: G._gap_().DerivedSeries()312[ Group( [ (3,4), (1,2,3)(4,5) ] ), Group( [ (1,5)(3,4), (1,5)(2,4), (1,5,3) ] ) ]313314TESTS::315316sage: r = Permutation("(1,7,9,3)(2,4,8,6)")317sage: f = Permutation("(1,3)(4,6)(7,9)")318sage: PermutationGroup([r,f]) #See Trac #12597319Permutation Group with generators [(1,3)(4,6)(7,9), (1,7,9,3)(2,4,8,6)]320321sage: PermutationGroup(SymmetricGroup(5))322Traceback (most recent call last):323...324TypeError: gens must be a tuple, list, or GapElement325"""326if not is_ExpectElement(gens) and hasattr(gens, '_permgroup_'):327return gens._permgroup_()328if gens is not None and not isinstance(gens, (tuple, list, GapElement)):329raise TypeError, "gens must be a tuple, list, or GapElement"330return PermutationGroup_generic(gens=gens, gap_group=gap_group, domain=domain,331canonicalize=canonicalize, category=category)332333334class PermutationGroup_generic(group.Group):335"""336EXAMPLES::337338sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])339sage: G340Permutation Group with generators [(3,4), (1,2,3)(4,5)]341sage: G.center()342Subgroup of (Permutation Group with generators [(3,4), (1,2,3)(4,5)]) generated by [()]343sage: G.group_id() # optional - database_gap344[120, 34]345sage: n = G.order(); n346120347sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])348sage: TestSuite(G).run()349"""350def __init__(self, gens=None, gap_group=None, canonicalize=True, domain=None, category=None):351r"""352INPUT:353354355- ``gens`` - list of generators (default: ``None``)356357- ``gap_group`` - a gap permutation group (default: ``None``)358359- ``canonicalize`` - bool (default: ``True``); if ``True``,360sort generators and remove duplicates361362363OUTPUT:364365- A permutation group.366367EXAMPLES:368369We explicitly construct the alternating group on four370elements::371372sage: A4 = PermutationGroup([[(1,2,3)],[(2,3,4)]]); A4373Permutation Group with generators [(2,3,4), (1,2,3)]374sage: A4.__init__([[(1,2,3)],[(2,3,4)]]); A4375Permutation Group with generators [(2,3,4), (1,2,3)]376sage: A4.center()377Subgroup of (Permutation Group with generators [(2,3,4), (1,2,3)]) generated by [()]378sage: A4.category()379Category of finite permutation groups380sage: TestSuite(A4).run()381382TESTS::383384sage: TestSuite(PermutationGroup([[]])).run()385sage: TestSuite(PermutationGroup([])).run()386sage: TestSuite(PermutationGroup([(0,1)])).run()387"""388from sage.categories.finite_permutation_groups import FinitePermutationGroups389super(PermutationGroup_generic, self).__init__(category = FinitePermutationGroups().or_subcategory(category))390if (gens is None and gap_group is None):391raise ValueError, "you must specify gens or gap_group"392393#Handle the case where only the GAP group is specified.394if gens is None:395if isinstance(gap_group, str):396gap_group = gap(gap_group)397gens = [gen for gen in gap_group.GeneratorsOfGroup()]398399if domain is None:400gens = [standardize_generator(x) for x in gens]401domain = set()402for x in gens:403for cycle in x:404domain = domain.union(cycle)405domain = sorted(domain)406407#Here we need to check if all of the points are integers408#to make the domain contain all integers up to the max.409#This is needed for backward compatibility410if all(isinstance(p, (Integer, int, long)) for p in domain):411domain = range(min([1] + domain), max([1] + domain)+1)412413if domain not in FiniteEnumeratedSets():414domain = FiniteEnumeratedSet(domain)415416self._domain = domain417self._deg = len(self._domain)418self._domain_to_gap = dict((key, i+1) for i, key in enumerate(self._domain))419self._domain_from_gap = dict((i+1, key) for i, key in enumerate(self._domain))420421if not gens: # length 0422gens = [()]423gens = [self._element_class()(x, self, check=False) for x in gens]424if canonicalize:425gens = sorted(set(gens))426self._gens = gens427428def construction(self):429"""430EXAMPLES::431432sage: P1 = PermutationGroup([[(1,2)]])433sage: P1.construction()434(PermutationGroupFunctor[(1,2)], Permutation Group with generators [()])435436sage: PermutationGroup([]).construction() is None437True438439This allows us to perform computations like the following::440441sage: P1 = PermutationGroup([[(1,2)]]); p1 = P1.gen()442sage: P2 = PermutationGroup([[(1,3)]]); p2 = P2.gen()443sage: p = p1*p2; p444(1,2,3)445sage: p.parent()446Permutation Group with generators [(1,2), (1,3)]447sage: p.parent().domain()448{1, 2, 3}449450Note that this will merge permutation groups with different451domains::452453sage: g1 = PermutationGroupElement([(1,2),(3,4,5)])454sage: g2 = PermutationGroup([('a','b')], domain=['a', 'b']).gens()[0]455sage: g2456('a','b')457sage: p = g1*g2; p458(1,2)(3,4,5)('a','b')459"""460gens = self.gens()461if len(gens) == 1 and gens[0].is_one():462return None463else:464from sage.categories.pushout import PermutationGroupFunctor465return (PermutationGroupFunctor(gens, self.domain()),466PermutationGroup([]))467468@cached_method469def _has_natural_domain(self):470"""471Returns True if the underlying domain is of the form (1,...,n)472473EXAMPLES::474475sage: SymmetricGroup(3)._has_natural_domain()476True477sage: SymmetricGroup((1,2,3))._has_natural_domain()478True479sage: SymmetricGroup((1,3))._has_natural_domain()480False481sage: SymmetricGroup((3,2,1))._has_natural_domain()482False483"""484domain = self.domain()485natural_domain = FiniteEnumeratedSet(range(1, len(domain)+1))486return domain == natural_domain487488def _gap_init_(self):489r"""490Returns a string showing how to declare / initialize ``self`` in Gap.491Stored in the ``self._gap_string`` attribute.492493EXAMPLES:494495The ``_gap_init_`` method shows how you496would define the Sage ``PermutationGroup_generic``497object in Gap::498499sage: A4 = PermutationGroup([[(1,2,3)],[(2,3,4)]]); A4500Permutation Group with generators [(2,3,4), (1,2,3)]501sage: A4._gap_init_()502'Group([PermList([1, 3, 4, 2]), PermList([2, 3, 1, 4])])'503"""504return 'Group([%s])'%(', '.join([g._gap_init_() for g in self.gens()]))505506def _magma_init_(self, magma):507r"""508Returns a string showing how to declare / initialize self in Magma.509510EXAMPLES:511512We explicitly construct the alternating group on four513elements. In Magma, one would type the string below to construct514the group::515516sage: A4 = PermutationGroup([[(1,2,3)],[(2,3,4)]]); A4517Permutation Group with generators [(2,3,4), (1,2,3)]518sage: A4._magma_init_(magma)519'PermutationGroup<4 | (2,3,4), (1,2,3)>'520521sage: S = SymmetricGroup(['a', 'b', 'c'])522sage: S._magma_init_(magma)523'PermutationGroup<3 | (1,2,3), (1,2)>'524"""525g = ', '.join([g._gap_cycle_string() for g in self.gens()])526return 'PermutationGroup<%s | %s>'%(self.degree(), g)527528def __cmp__(self, right):529"""530Compare ``self`` and ``right``.531532The comparison extends the subgroup relation. Hence, it is first checked533whether one of the groups is subgroup of the other. If this is not the534case then the ordering is whatever it is in Gap.535536NOTE:537The comparison does not provide a total ordering, as can be seen538in the examples below.539540EXAMPLES::541542sage: G1 = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])543sage: G2 = PermutationGroup([[(1,2,3),(4,5)]])544sage: G1 > G2 # since G2 is a subgroup of G1545True546sage: G1 < G2547False548549The following example shows that the comparison does not yield a total550ordering::551552sage: H1 = PermutationGroup([[(1,2)],[(5,6)]])553sage: H2 = PermutationGroup([[(3,4)]])554sage: H3 = PermutationGroup([[(1,2)]])555sage: H1 < H2 # according to Gap's ordering556True557sage: H2 < H3 # according to Gap's ordering558True559sage: H3 < H1 # since H3 is a subgroup of H1560True561"""562if (not isinstance(right, PermutationGroup_generic) or563self.domain() != right.domain()):564return -1565if self is right:566return 0567gSelf = self._gap_()568gRight = right._gap_()569gapcmp = gSelf.__cmp__(gRight)570if not gapcmp:571return 0572if gSelf.IsSubgroup(gRight):573return 1574if gRight.IsSubgroup(gSelf):575return -1576return gapcmp577578def _element_class(self):579r"""580Return the class to be used for creating elements of this group. By581default this is582``sage.groups.perm_gps.permgroup_element.PermutationGroupElement``, but583it may be overridden in derived subclasses (most importantly584``sage.rings.number_field.galois_group.GaloisGroup_v2``).585586EXAMPLE::587588sage: SymmetricGroup(17)._element_class()589<type 'sage.groups.perm_gps.permgroup_element.PermutationGroupElement'>590"""591return PermutationGroupElement592593def __call__(self, x, check=True):594"""595Coerce ``x`` into this permutation group.596597The input can be either a string that defines a permutation in598cycle notation, a permutation group element, a list of integers599that gives the permutation as a mapping, a list of tuples, or the600integer 1.601602EXAMPLES:603604We illustrate each way to make a permutation in `S_4`::605606sage: G = SymmetricGroup(4)607sage: G((1,2,3,4))608(1,2,3,4)609sage: G([(1,2),(3,4)])610(1,2)(3,4)611sage: G('(1,2)(3,4)')612(1,2)(3,4)613sage: G('(1,2)(3)(4)')614(1,2)615sage: G(((1,2,3),(4,)))616(1,2,3)617sage: G(((1,2,3,4),))618(1,2,3,4)619sage: G([1,2,4,3])620(3,4)621sage: G([2,3,4,1])622(1,2,3,4)623sage: G(G((1,2,3,4)))624(1,2,3,4)625sage: G(1)626()627628Some more examples::629630sage: G = PermutationGroup([(1,2,3,4)])631sage: G([(1,3), (2,4)])632(1,3)(2,4)633sage: G(G.0^3)634(1,4,3,2)635sage: G(1)636()637sage: G((1,4,3,2))638(1,4,3,2)639sage: G([(1,2)])640Traceback (most recent call last):641...642TypeError: permutation [(1, 2)] not in Permutation Group with generators [(1,2,3,4)]643"""644try:645if x.parent() is self:646return x647except AttributeError:648pass649650if isinstance(x, (int, long, Integer)) and x == 1:651return self.identity()652653return self._element_class()(x, self, check=check)654655def _coerce_impl(self, x):656r"""657Implicit coercion of ``x`` into ``self``.658659EXAMPLES:660661We illustrate some arithmetic that involves implicit662coercion of elements in different permutation groups::663664sage: g1 = PermutationGroupElement([(1,2),(3,4,5)])665sage: g1.parent()666Symmetric group of order 5! as a permutation group667sage: g2 = PermutationGroupElement([(1,2)])668sage: g2.parent()669Symmetric group of order 2! as a permutation group670sage: g1*g2671(3,4,5)672sage: g2*g2673()674sage: g2*g1675(3,4,5)676677We try to implicitly coerce in a non-permutation, which raises a678TypeError::679680sage: G = PermutationGroup([[(1,2,3,4)], [(1,2)]])681sage: G._coerce_impl(1)682Traceback (most recent call last):683...684TypeError: no implicit coercion of element into permutation group685"""686from permgroup_named import SymmetricGroup687if isinstance(x, PermutationGroupElement):688x_parent = x.parent()689if x_parent is self:690return x691692compatible_domains = all(point in self._domain_to_gap for point in x_parent.domain())693if compatible_domains and (self.__class__ == SymmetricGroup or x._gap_() in self._gap_()):694return self._element_class()(x.cycle_tuples(), self, check=False)695raise TypeError, "no implicit coercion of element into permutation group"696697@cached_method698def list(self):699"""700Return list of all elements of this group.701702EXAMPLES::703704sage: G = PermutationGroup([[(1,2,3,4)], [(1,2)]])705sage: G.list()706[(), (3,4), (2,3), (2,3,4), (2,4,3), (2,4), (1,2), (1,2)(3,4), (1,2,3), (1,2,3,4), (1,2,4,3), (1,2,4), (1,3,2), (1,3,4,2), (1,3), (1,3,4), (1,3)(2,4), (1,3,2,4), (1,4,3,2), (1,4,2), (1,4,3), (1,4), (1,4,2,3), (1,4)(2,3)]707708sage: G = PermutationGroup([[('a','b')]], domain=('a', 'b')); G709Permutation Group with generators [('a','b')]710sage: G.list()711[(), ('a','b')]712"""713return list(self.__iter__())714715def __contains__(self, item):716"""717Returns boolean value of ``item in self``.718719EXAMPLES::720721sage: G = SymmetricGroup(16)722sage: g = G.gen(0)723sage: h = G.gen(1)724sage: g^7*h*g*h in G725True726sage: G = SymmetricGroup(4)727sage: g = G((1,2,3,4))728sage: h = G((1,2))729sage: H = PermutationGroup([[(1,2,3,4)], [(1,2),(3,4)]])730sage: g in H731True732sage: h in H733False734735sage: G = PermutationGroup([[('a','b')]], domain=('a', 'b'))736sage: [('a', 'b')] in G737True738"""739try:740item = self(item, check=True)741except Exception:742return False743return True744745746def has_element(self, item):747"""748Returns boolean value of ``item in self`` - however *ignores*749parentage.750751EXAMPLES::752753sage: G = CyclicPermutationGroup(4)754sage: gens = G.gens()755sage: H = DihedralGroup(4)756sage: g = G([(1,2,3,4)]); g757(1,2,3,4)758sage: G.has_element(g)759True760sage: h = H([(1,2),(3,4)]); h761(1,2)(3,4)762sage: G.has_element(h)763False764"""765return item in self766767def __iter__(self):768"""769Return an iterator over the elements of this group.770771EXAMPLES::772773sage: G = PermutationGroup([[(1,2,3)], [(1,2)]])774sage: [a for a in G]775[(), (2,3), (1,2), (1,2,3), (1,3,2), (1,3)]776"""777for g in self._gap_().Elements():778yield self._element_class()(g, self, check=False)779780def gens(self):781"""782Return tuple of generators of this group. These need not be783minimal, as they are the generators used in defining this group.784785EXAMPLES::786787sage: G = PermutationGroup([[(1,2,3)], [(1,2)]])788sage: G.gens()789[(1,2), (1,2,3)]790791Note that the generators need not be minimal, though duplicates are792removed::793794sage: G = PermutationGroup([[(1,2)], [(1,3)], [(2,3)], [(1,2)]])795sage: G.gens()796[(2,3), (1,2), (1,3)]797798We can use index notation to access the generators returned by799``self.gens``::800801sage: G = PermutationGroup([[(1,2,3,4), (5,6)], [(1,2)]])802sage: g = G.gens()803sage: g[0]804(1,2)805sage: g[1]806(1,2,3,4)(5,6)807808TESTS:809810We make sure that the trivial group gets handled correctly::811812sage: SymmetricGroup(1).gens()813[()]814"""815return self._gens816817818def gens_small(self):819"""820For this group, returns a generating set which has few elements.821As neither irredundancy nor minimal length is proven, it is fast.822823EXAMPLES::824825sage: R = "(25,27,32,30)(26,29,31,28)( 3,38,43,19)( 5,36,45,21)( 8,33,48,24)" ## R = right826sage: U = "( 1, 3, 8, 6)( 2, 5, 7, 4)( 9,33,25,17)(10,34,26,18)(11,35,27,19)" ## U = top827sage: L = "( 9,11,16,14)(10,13,15,12)( 1,17,41,40)( 4,20,44,37)( 6,22,46,35)" ## L = left828sage: F = "(17,19,24,22)(18,21,23,20)( 6,25,43,16)( 7,28,42,13)( 8,30,41,11)" ## F = front829sage: B = "(33,35,40,38)(34,37,39,36)( 3, 9,46,32)( 2,12,47,29)( 1,14,48,27)" ## B = back or rear830sage: D = "(41,43,48,46)(42,45,47,44)(14,22,30,38)(15,23,31,39)(16,24,32,40)" ## D = down or bottom831sage: G = PermutationGroup([R,L,U,F,B,D])832sage: len(G.gens_small())8332834835The output may be unpredictable, due to the use of randomized836algorithms in GAP. Note that both the following answers are equally valid.837838::839840sage: G = PermutationGroup([[('a','b')], [('b', 'c')], [('a', 'c')]])841sage: G.gens_small() # random842[('b','c'), ('a','c','b')] ## (on 64-bit Linux)843[('a','b'), ('a','c','b')] ## (on Solaris)844sage: len(G.gens_small()) == 2845True846"""847gens = self._gap_().SmallGeneratingSet()848return [self._element_class()(x, self, check=False) for x in gens]849850def gen(self, i=None):851r"""852Returns the i-th generator of ``self``; that is, the i-th element of853the list ``self.gens()``.854855The argument `i` may be omitted if there is only one generator (but856this will raise an error otherwise).857858EXAMPLES:859860We explicitly construct the alternating group on four861elements::862863sage: A4 = PermutationGroup([[(1,2,3)],[(2,3,4)]]); A4864Permutation Group with generators [(2,3,4), (1,2,3)]865sage: A4.gens()866[(2,3,4), (1,2,3)]867sage: A4.gen(0)868(2,3,4)869sage: A4.gen(1)870(1,2,3)871sage: A4.gens()[0]; A4.gens()[1]872(2,3,4)873(1,2,3)874875sage: P1 = PermutationGroup([[(1,2)]]); P1.gen()876(1,2)877"""878gens = self.gens()879if i is None:880if len(gens) == 1:881return gens[0]882else:883raise ValueError, "You must specify which generator you want"884else:885return gens[i]886887def identity(self):888"""889Return the identity element of this group.890891EXAMPLES::892893sage: G = PermutationGroup([[(1,2,3),(4,5)]])894sage: e = G.identity()895sage: e896()897sage: g = G.gen(0)898sage: g*e899(1,2,3)(4,5)900sage: e*g901(1,2,3)(4,5)902903sage: S = SymmetricGroup(['a','b','c'])904sage: S.identity()905()906"""907return self._element_class()([], self, check=True)908909def exponent(self):910"""911Computes the exponent of the group. The exponent `e` of a912group `G` is the LCM of the orders of its elements, that913is, `e` is the smallest integer such that `g^e=1`914for all `g \in G`.915916EXAMPLES::917918sage: G = AlternatingGroup(4)919sage: G.exponent()9206921"""922return Integer(self._gap_().Exponent())923924def largest_moved_point(self):925"""926Return the largest point moved by a permutation in this group.927928EXAMPLES::929930sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4)]])931sage: G.largest_moved_point()9324933sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4,10)]])934sage: G.largest_moved_point()93510936937::938939sage: G = PermutationGroup([[('a','b','c'),('d','e')]])940sage: G.largest_moved_point()941'e'942943.. warning::944945The name of this function is not good; this function946should be deprecated in term of degree::947948sage: P = PermutationGroup([[1,2,3,4]])949sage: P.largest_moved_point()9504951sage: P.cardinality()9521953"""954try:955return self._domain_from_gap[Integer(self._gap_().LargestMovedPoint())]956except KeyError:957return self.degree()958959def degree(self):960"""961Returns the degree of this permutation group.962963EXAMPLES::964965sage: S = SymmetricGroup(['a','b','c'])966sage: S.degree()9673968sage: G = PermutationGroup([(1,3),(4,5)])969sage: G.degree()9705971972Note that you can explicitly specify the domain to get a973permutation group of smaller degree::974975sage: G = PermutationGroup([(1,3),(4,5)], domain=[1,3,4,5])976sage: G.degree()9774978"""979return Integer(len(self._domain))980981def domain(self):982r"""983Returns the underlying set that this permutation group acts984on.985986EXAMPLES::987988sage: P = PermutationGroup([(1,2),(3,5)])989sage: P.domain()990{1, 2, 3, 4, 5}991sage: S = SymmetricGroup(['a', 'b', 'c'])992sage: S.domain()993{'a', 'b', 'c'}994"""995return self._domain996997def _domain_gap(self, domain=None):998"""999Returns a GAP string representation of the underlying set1000that this group acts on. See also :meth:`domain`.10011002EXAMPLES::10031004sage: P = PermutationGroup([(1,2),(3,5)])1005sage: P._domain_gap()1006'[1, 2, 3, 4, 5]'1007"""1008if domain is None:1009return repr(range(1, self.degree()+1))1010else:1011try:1012return repr([self._domain_to_gap[point] for point in domain])1013except KeyError:1014raise ValueError, "domain must be a subdomain of self.domain()"10151016@cached_method1017def smallest_moved_point(self):1018"""1019Return the smallest point moved by a permutation in this group.10201021EXAMPLES::10221023sage: G = PermutationGroup([[(3,4)], [(2,3,4)]])1024sage: G.smallest_moved_point()102521026sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4,10)]])1027sage: G.smallest_moved_point()1028110291030Note that this function uses the ordering from the domain::10311032sage: S = SymmetricGroup(['a','b','c'])1033sage: S.smallest_moved_point()1034'a'1035"""1036return self._domain_from_gap[Integer(self._gap_().SmallestMovedPoint())]10371038@cached_method1039def orbits(self):1040"""1041Returns the orbits of the elements of the domain under the1042default group action.10431044EXAMPLES::10451046sage: G = PermutationGroup([ [(3,4)], [(1,3)] ])1047sage: G.orbits()1048[[1, 3, 4], [2]]1049sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4,10)]])1050sage: G.orbits()1051[[1, 2, 3, 4, 10], [5], [6], [7], [8], [9]]10521053sage: G = PermutationGroup([ [('c','d')], [('a','c')],[('b',)]])1054sage: G.orbits()1055[['a', 'c', 'd'], ['b']]10561057The answer is cached::10581059sage: G.orbits() is G.orbits()1060True10611062AUTHORS:10631064- Nathan Dunfield1065"""1066return [[self._domain_from_gap[x] for x in orbit] for orbit in1067self._gap_().Orbits(self._domain_gap()).sage()]10681069@cached_method1070def orbit(self, point, action="OnPoints"):1071"""1072Return the orbit of a point under a group action.10731074INPUT:10751076- ``point`` -- can be a point or any of the list above, depending on the1077action to be considered.10781079- ``action`` -- string. if ``point`` is an element from the domain, a1080tuple of elements of the domain, a tuple of tuples [...], this1081variable describes how the group is acting.10821083The actions currently available through this method are1084``"OnPoints"``, ``"OnTuples"``, ``"OnSets"``, ``"OnPairs"``,1085``"OnSetsSets"``, ``"OnSetsDisjointSets"``,1086``"OnSetsTuples"``, ``"OnTuplesSets"``,1087``"OnTuplesTuples"``. They are taken from GAP's list of1088group actions, see ``gap.help('Group Actions')``.10891090It is set to ``"OnPoints"`` by default. See below for examples.10911092OUTPUT:10931094The orbit of ``point`` as a tuple. Each entry is an image1095under the action of the permutation group, if necessary1096converted to the corresponding container. That is, if1097``action='OnSets'`` then each entry will be a set even if1098``point`` was given by a list/tuple/iterable.10991100EXAMPLES::11011102sage: G = PermutationGroup([ [(3,4)], [(1,3)] ])1103sage: G.orbit(3)1104(3, 4, 1)1105sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4,10)]])1106sage: G.orbit(3)1107(3, 4, 10, 1, 2)1108sage: G = PermutationGroup([ [('c','d')], [('a','c')] ])1109sage: G.orbit('a')1110('a', 'c', 'd')11111112Action of `S_3` on sets::11131114sage: S3 = groups.permutation.Symmetric(3)1115sage: S3.orbit((1,2), action = "OnSets")1116({1, 2}, {2, 3}, {1, 3})11171118On tuples::11191120sage: S3.orbit((1,2), action = "OnTuples")1121((1, 2), (2, 3), (2, 1), (3, 1), (1, 3), (3, 2))11221123Action of `S_4` on sets of disjoint sets::11241125sage: S4 = groups.permutation.Symmetric(4)1126sage: S4.orbit(((1,2),(3,4)), action = "OnSetsDisjointSets")1127({{1, 2}, {3, 4}}, {{2, 3}, {1, 4}}, {{1, 3}, {2, 4}})11281129Action of `S_4` (on a nonstandard domain) on tuples of sets::11301131sage: S4 = PermutationGroup([ [('c','d')], [('a','c')], [('a','b')] ])1132sage: S4.orbit((('a','c'),('b','d')),"OnTuplesSets")1133(({'a', 'c'}, {'b', 'd'}),1134({'a', 'd'}, {'c', 'b'}),1135({'c', 'b'}, {'a', 'd'}),1136({'b', 'd'}, {'a', 'c'}),1137({'c', 'd'}, {'a', 'b'}),1138({'a', 'b'}, {'c', 'd'}))11391140Action of `S_4` (on a very nonstandard domain) on tuples of sets::11411142sage: S4 = PermutationGroup([ [((11,(12,13)),'d')],1143... [((12,(12,11)),(11,(12,13)))], [((12,(12,11)),'b')] ])1144sage: S4.orbit((( (11,(12,13)), (12,(12,11))),('b','d')),"OnTuplesSets")1145(({(11, (12, 13)), (12, (12, 11))}, {'b', 'd'}),1146({'d', (12, (12, 11))}, {(11, (12, 13)), 'b'}),1147({(11, (12, 13)), 'b'}, {'d', (12, (12, 11))}),1148({(11, (12, 13)), 'd'}, {'b', (12, (12, 11))}),1149({'b', 'd'}, {(11, (12, 13)), (12, (12, 11))}),1150({'b', (12, (12, 11))}, {(11, (12, 13)), 'd'}))1151"""1152from sage.sets.set import Set1153actions = {1154"OnPoints" : [],1155"OnSets" : [Set],1156"OnPairs" : [tuple],1157"OnTuples" : [tuple],1158"OnSetsSets" : [Set, Set],1159"OnSetsDisjointSets" : [Set, Set],1160"OnSetsTuples" : [Set, tuple],1161"OnTuplesSets" : [tuple, Set],1162"OnTuplesTuples" : [tuple, tuple],1163}11641165def input_for_gap(x, depth, container):1166if depth == len(container):1167try:1168return self._domain_to_gap[x]1169except KeyError:1170raise ValueError('{0} is not part of the domain'.format(x))1171x = [input_for_gap(xx, depth+1, container) for xx in x]1172if container[depth] is Set:1173x.sort()1174return x11751176def gap_to_output(x, depth, container):1177if depth == len(container):1178return self._domain_from_gap[x]1179else:1180x = [gap_to_output(xx, depth+1, container) for xx in x]1181return container[depth](x)1182try:1183container = actions[action]1184except KeyError:1185raise NotImplementedError("This action is not implemented (yet?).")1186point = input_for_gap(point, 0, container)1187result = self._gap_().Orbit(point, action).sage()1188result = [gap_to_output(x, 0, container) for x in result]1189return tuple(result)11901191def transversals(self, point):1192"""1193If G is a permutation group acting on the set `X = \{1, 2, ...., n\}`1194and H is the stabilizer subgroup of <integer>, a right1195(respectively left) transversal is a set containing exactly1196one element from each right (respectively left) coset of H. This1197method returns a right transversal of ``self`` by the stabilizer1198of ``self`` on <integer> position.11991200EXAMPLES::12011202sage: G = PermutationGroup([ [(3,4)], [(1,3)] ])1203sage: G.transversals(1)1204[(), (1,3,4), (1,4,3)]1205sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4,10)]])1206sage: G.transversals(1)1207[(), (1,2)(3,4), (1,3,2,10,4), (1,4,2,10,3), (1,10,4,3,2)]12081209sage: G = PermutationGroup([ [('c','d')], [('a','c')] ])1210sage: G.transversals('a')1211[(), ('a','c','d'), ('a','d','c')]1212"""1213G = self._gap_()1214return [self(G.RepresentativeAction(self._domain_to_gap[point], self._domain_to_gap[i]))1215for i in self.orbit(point)]12161217def stabilizer(self, point):1218"""1219Return the subgroup of ``self`` which stabilize the given position.1220``self`` and its stabilizers must have same degree.12211222EXAMPLES::12231224sage: G = PermutationGroup([ [(3,4)], [(1,3)] ])1225sage: G.stabilizer(1)1226Subgroup of (Permutation Group with generators [(3,4), (1,3)]) generated by [(3,4)]1227sage: G.stabilizer(3)1228Subgroup of (Permutation Group with generators [(3,4), (1,3)]) generated by [(1,4)]1229sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4,10)]])1230sage: G.stabilizer(10)1231Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4,10)]) generated by [(2,3,4), (1,2)(3,4)]1232sage: G.stabilizer(1)1233Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4,10)]) generated by [(2,3)(4,10), (2,10,4)]1234sage: G = PermutationGroup([[(2,3,4)],[(6,7)]])1235sage: G.stabilizer(1)1236Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(6,7), (2,3,4)]1237sage: G.stabilizer(2)1238Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(6,7)]1239sage: G.stabilizer(3)1240Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(6,7)]1241sage: G.stabilizer(4)1242Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(6,7)]1243sage: G.stabilizer(5)1244Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(6,7), (2,3,4)]1245sage: G.stabilizer(6)1246Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(2,3,4)]1247sage: G.stabilizer(7)1248Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(2,3,4)]1249sage: G.stabilizer(8)1250Subgroup of (Permutation Group with generators [(6,7), (2,3,4)]) generated by [(6,7), (2,3,4)]12511252::12531254sage: G = PermutationGroup([ [('c','d')], [('a','c')] ], domain='abcd')1255sage: G.stabilizer('a')1256Subgroup of (Permutation Group with generators [('c','d'), ('a','c')]) generated by [('c','d')]1257sage: G.stabilizer('b')1258Subgroup of (Permutation Group with generators [('c','d'), ('a','c')]) generated by [('c','d'), ('a','c')]1259sage: G.stabilizer('c')1260Subgroup of (Permutation Group with generators [('c','d'), ('a','c')]) generated by [('a','d')]1261sage: G.stabilizer('d')1262Subgroup of (Permutation Group with generators [('c','d'), ('a','c')]) generated by [('a','c')]1263"""1264try:1265point = self._domain_to_gap[point]1266except KeyError:1267return self.subgroup(gens=self.gens())1268return self.subgroup(gap_group=gap.Stabilizer(self, point))12691270def base(self, seed=None):1271r"""1272Returns a (minimum) base of this permutation group. A base $B$1273of a permutation group is a subset of the domain of the group1274such that the only group element stabilizing all of $B$ is the1275identity.12761277The argument `seed` is optional and must be a subset of the domain1278of `base`. When used, an attempt to create a base containing all or part1279of `seed` will be made.12801281EXAMPLES::12821283sage: G = PermutationGroup([(1,2,3),(6,7,8)])1284sage: G.base()1285[1, 6]1286sage: G.base([2])1287[2, 6]12881289sage: H = PermutationGroup([('a','b','c'),('a','y')])1290sage: H.base()1291['a', 'b', 'c']12921293sage: S = SymmetricGroup(13)1294sage: S.base()1295[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]12961297sage: S = MathieuGroup(12)1298sage: S.base()1299[1, 2, 3, 4, 5]1300sage: S.base([1,3,5,7,9,11]) # create a base for M12 with only odd integers1301[1, 3, 5, 7, 9]1302"""1303if seed is None:1304seed = self.domain()13051306try:1307seed = [self._domain_to_gap[point] for point in seed]1308except KeyError:1309raise ValueError, "seed must be a subset of self.domain()"13101311return [self._domain_from_gap[x] for x in self._gap_().StabChain(seed).BaseStabChain().sage()]13121313def strong_generating_system(self, base_of_group=None):1314"""1315Return a Strong Generating System of ``self`` according the given1316base for the right action of ``self`` on itself.13171318``base_of_group`` is a list of the positions on which ``self`` acts,1319in any order. The algorithm returns a list of transversals and each1320transversal is a list of permutations. By default, ``base_of_group``1321is ``[1, 2, 3, ..., d]`` where `d` is the degree of the group.13221323For ``base_of_group`` =1324`[ \mathrm{pos}_1, \mathrm{pos}_2, \dots , \mathrm{pos}_d]`1325let `G_i` be the subgroup of `G` = ``self`` which stabilizes1326`\mathrm{pos}_1, \mathrm{pos}_2, \dots , \mathrm{pos}_i`, so13271328.. math::13291330G = G_0 \supset G_1 \supset G_2 \supset \dots \supset G_n = \{e\}13311332Then the algorithm returns `[ G_i.\mathrm{transversals}(\mathrm{pos}_{i+1})]_{1 \leq i \leq n}`13331334INPUT:13351336- ``base_of_group`` (optional) -- default: ``[1, 2, 3, ..., d]``1337-- a list containing the integers1338`1, 2, \dots , d` in any order (`d` is the degree of ``self``)13391340OUTPUT:13411342- A list of lists of permutations from the group, which form a strong1343generating system.13441345TESTS::13461347sage: G = SymmetricGroup(10)1348sage: H = PermutationGroup([G.random_element() for i in range(randrange(1,3,1))])1349sage: prod(map(lambda x : len(x), H.strong_generating_system()),1) == H.cardinality()1350True13511352EXAMPLES::13531354sage: G = PermutationGroup([[(7,8)],[(3,4)],[(4,5)]])1355sage: G.strong_generating_system()1356[[()], [()], [(), (3,4,5), (3,5)], [(), (4,5)], [()], [()], [(), (7,8)], [()]]1357sage: G = PermutationGroup([[(1,2,3,4)],[(1,2)]])1358sage: G.strong_generating_system()1359[[(), (1,2)(3,4), (1,3)(2,4), (1,4)(2,3)], [(), (2,3,4), (2,4,3)], [(), (3,4)], [()]]1360sage: G = PermutationGroup([[(1,2,3)],[(4,5,7)],[(1,4,6)]])1361sage: G.strong_generating_system()1362[[(), (1,2,3), (1,4,6), (1,3,2), (1,5,7,4,6), (1,6,4), (1,7,5,4,6)], [(), (2,6,3), (2,5,7,6,3), (2,3,6), (2,7,5,6,3), (2,4,7,6,3)], [(), (3,6,7), (3,5,6), (3,7,6), (3,4,7,5,6)], [(), (4,5)(6,7), (4,7)(5,6), (4,6)(5,7)], [(), (5,7,6), (5,6,7)], [()], [()]]1363sage: G = PermutationGroup([[(1,2,3)],[(2,3,4)],[(3,4,5)]])1364sage: G.strong_generating_system([5,4,3,2,1])1365[[(), (1,5,3,4,2), (1,5,4,3,2), (1,5)(2,3), (1,5,2)], [(), (1,3)(2,4), (1,2)(3,4), (1,4)(2,3)], [(), (1,3,2), (1,2,3)], [()], [()]]1366sage: G = PermutationGroup([[(3,4)]])1367sage: G.strong_generating_system()1368[[()], [()], [(), (3,4)], [()]]1369sage: G.strong_generating_system(base_of_group=[3,1,2,4])1370[[(), (3,4)], [()], [()], [()]]1371sage: G = TransitiveGroup(12,17) # optional1372sage: G.strong_generating_system() # optional1373[[(), (1,4,11,2)(3,6,5,8)(7,10,9,12), (1,8,3,2)(4,11,10,9)(5,12,7,6), (1,7)(2,8)(3,9)(4,10)(5,11)(6,12), (1,12,7,2)(3,10,9,8)(4,11,6,5), (1,11)(2,8)(3,5)(4,10)(6,12)(7,9), (1,10,11,8)(2,3,12,5)(4,9,6,7), (1,3)(2,8)(4,10)(5,7)(6,12)(9,11), (1,2,3,8)(4,9,10,11)(5,6,7,12), (1,6,7,8)(2,3,4,9)(5,10,11,12), (1,5,9)(3,11,7), (1,9,5)(3,7,11)], [(), (2,6,10)(4,12,8), (2,10,6)(4,8,12)], [()], [()], [()], [()], [()], [()], [()], [()], [()], [()]]1374"""1375sgs = []1376stab = self1377if base_of_group is None:1378base_of_group = self.domain()1379for j in base_of_group:1380sgs.append(stab.transversals(j))1381stab = stab.stabilizer(j)1382return sgs138313841385def _repr_(self):1386r"""1387Returns a string describing ``self``.13881389EXAMPLES:13901391We explicitly construct the alternating group on four1392elements. Note that the ``AlternatingGroup`` class has1393its own representation string::13941395sage: A4 = PermutationGroup([[(1,2,3)],[(2,3,4)]]); A41396Permutation Group with generators [(2,3,4), (1,2,3)]1397sage: A4._repr_()1398'Permutation Group with generators [(2,3,4), (1,2,3)]'1399sage: AlternatingGroup(4)._repr_()1400'Alternating group of order 4!/2 as a permutation group'1401"""1402return "Permutation Group with generators %s"%self.gens()14031404def _latex_(self):1405r"""1406Method for describing ``self`` in LaTeX. Encapsulates1407``self.gens()`` in angle brackets to denote that ``self``1408is generated by these elements. Called by the1409``latex()`` function.14101411EXAMPLES:14121413We explicitly construct the alternating group on four1414elements::14151416sage: A4 = PermutationGroup([[(1,2,3)],[(2,3,4)]]); A41417Permutation Group with generators [(2,3,4), (1,2,3)]1418sage: latex(A4)1419\langle (2,3,4), (1,2,3) \rangle1420sage: A4._latex_()1421'\\langle (2,3,4), (1,2,3) \\rangle'14221423sage: S = SymmetricGroup(['a','b','c'])1424sage: latex(S)1425\langle (\text{\texttt{a}},\text{\texttt{b}},\text{\texttt{c}}), (\text{\texttt{a}},\text{\texttt{b}}) \rangle1426"""1427return '\\langle ' + \1428', '.join([x._latex_() for x in self.gens()]) + ' \\rangle'14291430def _order(self):1431"""1432This handles a few special cases of computing the subgroup order much1433faster than GAP.14341435This currently operates very quickly for stabilizer subgroups of1436permutation groups, for one.14371438Will return None if the we could not easily compute it.14391440Author: Christopher Swenson14411442EXAMPLES::14431444sage: SymmetricGroup(10).stabilizer(4)._order()14453628801446sage: SymmetricGroup(10).stabilizer(4).stabilizer(5)._order()1447403201448sage: SymmetricGroup(200).stabilizer(100)._order() == factorial(199) # this should be very fast1449True14501451TESTS::14521453sage: [SymmetricGroup(n).stabilizer(1)._gap_().Size() for n in [4..10]]1454[6, 24, 120, 720, 5040, 40320, 362880]1455sage: [SymmetricGroup(n).stabilizer(1)._order() for n in [4..10]]1456[6, 24, 120, 720, 5040, 40320, 362880]1457"""1458gens = self.gens()1459# This special case only works with more than 1 generator.1460if not gens or len(gens) < 2:1461return None1462# Special case: certain subgroups of the symmetric group for which Gap reports1463# generators of the form ((1, 2), (1, 3), ...)1464# This means that this group is isomorphic to a smaller symmetric group1465# S_n, where n is the number of generators supported.1466#1467# The code that follows checks that the following assumptions hold:1468# * All generators are transpositions (i.e., permutations1469# that switch two elements and leave everything else fixed),1470# * All generators share a common element.1471#1472# We then know that this group is isomorphic to S_n,1473# and therefore its order is n!.14741475# Check that all generators are order 2 and have length-1 cycle tuples.1476for g in gens:1477if g.order() != 2:1478return None1479if len(g.cycle_tuples()) != 1:1480return None1481# Find the common element.1482g0 = gens[0].cycle_tuples()[0]1483g1 = gens[1].cycle_tuples()[0]1484a, b = g01485if a not in g1 and b not in g1:1486return None1487if a in g1:1488elem = a1489else:1490elem = b1491# Count the number of unique elements in the generators.1492unique = set()1493for g in gens:1494a, b = g.cycle_tuples()[0]1495if a != elem and b != elem:1496return None1497unique.add(a)1498unique.add(b)1499# Compute the order.1500return factorial(len(unique))15011502def order(self):1503"""1504Return the number of elements of this group.15051506EXAMPLES::15071508sage: G = PermutationGroup([[(1,2,3),(4,5)], [(1,2)]])1509sage: G.order()1510121511sage: G = PermutationGroup([()])1512sage: G.order()151311514sage: G = PermutationGroup([])1515sage: G.order()151611517"""1518if not self.gens() or self.gens() == [self(1)]:1519return Integer(1)1520subgroup_order = self._order()1521if subgroup_order is not None:1522return subgroup_order1523return Integer(self._gap_().Size())15241525cardinality = order15261527def random_element(self):1528"""1529Return a random element of this group.15301531EXAMPLES::15321533sage: G = PermutationGroup([[(1,2,3),(4,5)], [(1,2)]])1534sage: a = G.random_element()1535sage: a in G1536True1537sage: a.parent() is G1538True1539sage: a^61540()1541"""1542current_randstate().set_seed_gap()1543return self(self._gap_().Random(), check=False)15441545def group_id(self):1546"""1547Return the ID code of this group, which is a list of two integers.1548Requires "optional" database_gap-4.4.x package.15491550EXAMPLES::15511552sage: G = PermutationGroup([[(1,2,3),(4,5)], [(1,2)]])1553sage: G.group_id() # optional - database_gap1554[12, 4]1555"""1556return [Integer(n) for n in self._gap_().IdGroup()]15571558def id(self):1559"""1560(Same as ``self.group_id()``.) Return the ID code of this group, which1561is a list of two integers. Requires "optional" database_gap-4.4.x1562package.15631564EXAMPLES::15651566sage: G = PermutationGroup([[(1,2,3),(4,5)], [(1,2)]])1567sage: G.group_id() # optional - database_gap1568[12, 4]1569"""1570return self.group_id()15711572def group_primitive_id(self):1573"""1574Return the index of this group in the GAP database of primitive groups.15751576Requires "optional" database_gap-4.4.x package.15771578OUTPUT:15791580A positive integer, following GAP's conventions. A1581``ValueError`` is raised if the group is not primitive.15821583EXAMPLES::15841585sage: G = PermutationGroup([[(1,2,3,4,5)], [(1,5),(2,4)]])1586sage: G.group_primitive_id() # optional - database_gap158721588sage: G.degree()1589515901591From the information of the degree and the identification number,1592you can recover the isomorphism class of your group in the GAP1593database::15941595sage: H = PrimitiveGroup(5,2) # optional - database_gap1596sage: G == H # optional - database_gap1597False1598sage: G.is_isomorphic(H) # optional - database_gap1599True1600"""1601if not self.is_primitive():1602raise ValueError('Group is not primitive')1603return Integer(self._gap_().PrimitiveIdentification())16041605@cached_method1606def structure_description(self, latex=False):1607r"""1608Return a string that tries to describe the structure of ``self``.16091610This methods wraps GAP's ``StructureDescription`` method.16111612Requires the *optional* ``database_gap`` package.16131614For full details, including the form of the returned string and the1615algorithm to build it, see `GAP's documentation1616<http://www.gap-system.org/Manuals/doc/ref/chap39.html>`_.16171618INPUT:16191620- ``latex`` -- a boolean (default: ``False``). If ``True`` return a1621LaTeX formatted string.16221623OUTPUT:16241625- string16261627.. WARNING::16281629From GAP's documentation: The string returned by1630``StructureDescription`` is **not** an isomorphism invariant:1631non-isomorphic groups can have the same string value, and two1632isomorphic groups in different representations can produce different1633strings.16341635EXAMPLES::16361637sage: G = CyclicPermutationGroup(6)1638sage: G.structure_description() # optional - database_gap1639'C6'1640sage: G.structure_description(latex=True) # optional - database_gap1641'C_{6}'1642sage: G2 = G.direct_product(G, maps=False)1643sage: LatexExpr(G2.structure_description(latex=True)) # optional - database_gap1644C_{6} \times C_{6}16451646This method is mainly intended for small groups or groups with few1647normal subgroups. Even then there are some surprises::16481649sage: D3 = DihedralGroup(3)1650sage: D3.structure_description() # optional - database_gap1651'S3'16521653We use the Sage notation for the degree of dihedral groups::16541655sage: D4 = DihedralGroup(4)1656sage: D4.structure_description() # optional - database_gap1657'D4'16581659"""1660import re1661def correct_dihedral_degree(match):1662return "%sD%d" % (match.group(1), int(match.group(2))/2)16631664description = self._gap_().StructureDescription().str()1665description = re.sub(r"(\A|\W)D(\d+)", correct_dihedral_degree, description)1666if not latex:1667return description1668description = description.replace("x", r"\times").replace(":", r"\rtimes")1669description = re.sub(r"([A-Za-z]+)([0-9]+)", r"\g<1>_{\g<2>}", description)1670description = re.sub(r"O([+-])", r"O^{\g<1>}", description)16711672return description16731674def center(self):1675"""1676Return the subgroup of elements that commute with every element1677of this group.16781679EXAMPLES::16801681sage: G = PermutationGroup([[(1,2,3,4)]])1682sage: G.center()1683Subgroup of (Permutation Group with generators [(1,2,3,4)]) generated by [(1,2,3,4)]1684sage: G = PermutationGroup([[(1,2,3,4)], [(1,2)]])1685sage: G.center()1686Subgroup of (Permutation Group with generators [(1,2), (1,2,3,4)]) generated by [()]1687"""1688return self.subgroup(gap_group=self._gap_().Center())16891690def socle(self):1691r"""1692Returns the socle of ``self``. The socle of a group $G$ is1693the subgroup generated by all minimal normal subgroups.16941695EXAMPLES::16961697sage: G=SymmetricGroup(4)1698sage: G.socle()1699Subgroup of (Symmetric group of order 4! as a permutation group) generated by [(1,2)(3,4), (1,4)(2,3)]1700sage: G.socle().socle()1701Subgroup of (Subgroup of (Symmetric group of order 4! as a permutation group) generated by [(1,2)(3,4), (1,4)(2,3)]) generated by [(1,2)(3,4), (1,4)(2,3)]1702"""1703return self.subgroup(gap_group=self._gap_().Socle())17041705def frattini_subgroup(self):1706r"""1707Returns the Frattini subgroup of ``self``. The Frattini1708subgroup of a group $G$ is the intersection of all1709maximal subgroups of $G$.17101711EXAMPLES::17121713sage: G=PermutationGroup([[(1,2,3,4)],[(2,4)]])1714sage: G.frattini_subgroup()1715Subgroup of (Permutation Group with generators [(2,4), (1,2,3,4)]) generated by [(1,3)(2,4)]1716sage: G=SymmetricGroup(4)1717sage: G.frattini_subgroup()1718Subgroup of (Symmetric group of order 4! as a permutation group) generated by [()]17191720"""1721return self.subgroup(gap_group=self._gap_().FrattiniSubgroup())17221723def fitting_subgroup(self):1724r"""1725Returns the Fitting subgroup of ``self``. The Fitting1726subgroup of a group $G$ is the largest nilpotent normal1727subgroup of $G$.17281729EXAMPLES::17301731sage: G=PermutationGroup([[(1,2,3,4)],[(2,4)]])1732sage: G.fitting_subgroup()1733Subgroup of (Permutation Group with generators [(2,4), (1,2,3,4)]) generated by [(2,4), (1,2,3,4), (1,3)]1734sage: G=PermutationGroup([[(1,2,3,4)],[(1,2)]])1735sage: G.fitting_subgroup()1736Subgroup of (Permutation Group with generators [(1,2), (1,2,3,4)]) generated by [(1,2)(3,4), (1,3)(2,4)]17371738"""1739return self.subgroup(gap_group=self._gap_().FittingSubgroup())17401741def solvable_radical(self):1742r"""1743Returns the solvable radical of ``self``. The solvable1744radical (or just radical) of a group $G$ is the largest1745solvable normal subgroup of $G$.17461747EXAMPLES::17481749sage: G=SymmetricGroup(4)1750sage: G.solvable_radical()1751Subgroup of (Symmetric group of order 4! as a permutation group) generated by [(1,2), (1,2,3,4)]1752sage: G=SymmetricGroup(5)1753sage: G.solvable_radical()1754Subgroup of (Symmetric group of order 5! as a permutation group) generated by [()]17551756"""1757return self.subgroup(gap_group=self._gap_().RadicalGroup())17581759def intersection(self, other):1760r"""1761Returns the permutation group that is the intersection of1762``self`` and ``other``.17631764INPUT:17651766- ``other`` - a permutation group.17671768OUTPUT:17691770A permutation group that is the set-theoretic intersection of ``self``1771with ``other``. The groups are viewed as subgroups of a symmetric1772group big enough to contain both group's symbol sets. So there is1773no strict notion of the two groups being subgroups of a common parent.17741775EXAMPLES::17761777sage: H = DihedralGroup(4)17781779sage: K = CyclicPermutationGroup(4)1780sage: H.intersection(K)1781Permutation Group with generators [(1,2,3,4)]17821783sage: L = DihedralGroup(5)1784sage: H.intersection(L)1785Permutation Group with generators [(1,4)(2,3)]17861787sage: M = PermutationGroup(["()"])1788sage: H.intersection(M)1789Permutation Group with generators [()]17901791Some basic properties. ::17921793sage: H = DihedralGroup(4)1794sage: L = DihedralGroup(5)1795sage: H.intersection(L) == L.intersection(H)1796True1797sage: H.intersection(H) == H1798True17991800The group ``other`` is verified as such. ::18011802sage: H = DihedralGroup(4)1803sage: H.intersection('junk')1804Traceback (most recent call last):1805...1806TypeError: junk is not a permutation group1807"""1808from sage.categories.finite_permutation_groups import FinitePermutationGroups1809if other not in FinitePermutationGroups():1810raise TypeError("{0} is not a permutation group".format(other))1811return PermutationGroup(gap_group=gap.Intersection(self, other))18121813def conjugacy_class(self, g):1814r"""1815Return the conjugacy class of ``g`` inside the group ``self``.18161817INPUT:18181819- ``g`` -- an element of the permutation group ``self``18201821OUTPUT:18221823The conjugacy class of ``g`` in the group ``self``. If ``self`` is1824the group denoted by `G`, this method computes the set1825`\{x^{-1}gx\ \vert\ x \in G \}`18261827EXAMPLES::18281829sage: G = SymmetricGroup(4)1830sage: g = G((1,2,3,4))1831sage: G.conjugacy_class(g)1832Conjugacy class of (1,2,3,4) in Symmetric group of order 4! as a permutation group1833"""1834return ConjugacyClassGAP(self, g)18351836def conjugacy_classes(self):1837r"""1838Return a list with all the conjugacy classes of ``self``.18391840EXAMPLES::18411842sage: G = DihedralGroup(3)1843sage: G.conjugacy_classes()1844[Conjugacy class of () in Dihedral group of order 6 as a permutation group,1845Conjugacy class of (1,2) in Dihedral group of order 6 as a permutation group,1846Conjugacy class of (1,2,3) in Dihedral group of order 6 as a permutation group]1847"""1848cl = self._gap_().ConjugacyClasses()1849n = Integer(cl.Length())1850L = gap("List([1..Length(%s)], i->Representative(%s[i]))"%(cl.name(), cl.name()))1851return [ConjugacyClassGAP(self,self._element_class()(L[i], self, check=False)) for i in range(1,n+1)]18521853def conjugate(self, g):1854r"""1855Returns the group formed by conjugating ``self`` with ``g``.18561857INPUT:18581859- ``g`` - a permutation group element, or an object that converts1860to a permutation group element, such as a list of integers or1861a string of cycles.18621863OUTPUT:18641865If ``self`` is the group denoted by `H`, then this method computes1866the group18671868.. math::18691870g^{-1}Hg = \{g^{-1}hg\vert h\in H\}18711872which is the group `H` conjugated by `g`.18731874There are no restrictions on ``self`` and ``g`` belonging to1875a common permutation group, and correspondingly, there is no1876relationship (such as a common parent) between ``self`` and the1877output group.18781879EXAMPLES::18801881sage: G = DihedralGroup(6)1882sage: a = PermutationGroupElement("(1,2,3,4)")1883sage: G.conjugate(a)1884Permutation Group with generators [(1,4)(2,6)(3,5), (1,5,6,2,3,4)]18851886The element performing the conjugation can be specified in1887several ways. ::18881889sage: G = DihedralGroup(6)1890sage: strng = "(1,2,3,4)"1891sage: G.conjugate(strng)1892Permutation Group with generators [(1,4)(2,6)(3,5), (1,5,6,2,3,4)]1893sage: G = DihedralGroup(6)1894sage: lst = [2,3,4,1]1895sage: G.conjugate(lst)1896Permutation Group with generators [(1,4)(2,6)(3,5), (1,5,6,2,3,4)]1897sage: G = DihedralGroup(6)1898sage: cycles = [(1,2,3,4)]1899sage: G.conjugate(cycles)1900Permutation Group with generators [(1,4)(2,6)(3,5), (1,5,6,2,3,4)]19011902Conjugation is a group automorphism, so conjugate groups1903will be isomorphic. ::19041905sage: G = DiCyclicGroup(6)1906sage: G.degree()1907111908sage: cycle = [i+1 for i in range(1,11)] + [1]1909sage: C = G.conjugate(cycle)1910sage: G.is_isomorphic(C)1911True19121913The conjugating element may be from a symmetric group with1914larger degree than the group being conjugated. ::19151916sage: G = AlternatingGroup(5)1917sage: G.degree()191851919sage: g = "(1,3)(5,6,7)"1920sage: H = G.conjugate(g); H1921Permutation Group with generators [(1,4,6,3,2), (1,4,6)]1922sage: H.degree()1923619241925The conjugating element is checked. ::19261927sage: G = SymmetricGroup(3)1928sage: G.conjugate("junk")1929Traceback (most recent call last):1930...1931TypeError: junk does not convert to a permutation group element1932"""1933try:1934g = PermutationGroupElement(g)1935except Exception:1936raise TypeError("{0} does not convert to a permutation group element".format(g))1937return PermutationGroup(gap_group=gap.ConjugateGroup(self, g))19381939def direct_product(self, other, maps=True):1940"""1941Wraps GAP's ``DirectProduct``, ``Embedding``, and ``Projection``.19421943Sage calls GAP's ``DirectProduct``, which chooses an efficient1944representation for the direct product. The direct product of1945permutation groups will be a permutation group again. For a direct1946product ``D``, the GAP operation ``Embedding(D,i)`` returns the1947homomorphism embedding the i-th factor into ``D``. The GAP operation1948``Projection(D,i)`` gives the projection of ``D`` onto the i-th factor.1949This method returns a 5-tuple: a permutation group and 4 morphisms.19501951INPUT:19521953- ``self, other`` - permutation groups19541955OUTPUT:19561957- ``D`` - a direct product of the inputs, returned as1958a permutation group as well19591960- ``iota1`` - an embedding of ``self`` into ``D``19611962- ``iota2`` - an embedding of ``other`` into ``D``19631964- ``pr1`` - the projection of ``D`` onto ``self`` (giving a1965splitting 1 - other - D - self - 1)19661967- ``pr2`` - the projection of ``D`` onto ``other`` (giving a1968splitting 1 - self - D - other - 1)19691970EXAMPLES::19711972sage: G = CyclicPermutationGroup(4)1973sage: D = G.direct_product(G,False)1974sage: D1975Permutation Group with generators [(5,6,7,8), (1,2,3,4)]1976sage: D,iota1,iota2,pr1,pr2 = G.direct_product(G)1977sage: D; iota1; iota2; pr1; pr21978Permutation Group with generators [(5,6,7,8), (1,2,3,4)]1979Permutation group morphism:1980From: Cyclic group of order 4 as a permutation group1981To: Permutation Group with generators [(5,6,7,8), (1,2,3,4)]1982Defn: Embedding( Group( [ (1,2,3,4), (5,6,7,8) ] ), 1 )1983Permutation group morphism:1984From: Cyclic group of order 4 as a permutation group1985To: Permutation Group with generators [(5,6,7,8), (1,2,3,4)]1986Defn: Embedding( Group( [ (1,2,3,4), (5,6,7,8) ] ), 2 )1987Permutation group morphism:1988From: Permutation Group with generators [(5,6,7,8), (1,2,3,4)]1989To: Cyclic group of order 4 as a permutation group1990Defn: Projection( Group( [ (1,2,3,4), (5,6,7,8) ] ), 1 )1991Permutation group morphism:1992From: Permutation Group with generators [(5,6,7,8), (1,2,3,4)]1993To: Cyclic group of order 4 as a permutation group1994Defn: Projection( Group( [ (1,2,3,4), (5,6,7,8) ] ), 2 )1995sage: g=D([(1,3),(2,4)]); g1996(1,3)(2,4)1997sage: d=D([(1,4,3,2),(5,7),(6,8)]); d1998(1,4,3,2)(5,7)(6,8)1999sage: iota1(g); iota2(g); pr1(d); pr2(d)2000(1,3)(2,4)2001(5,7)(6,8)2002(1,4,3,2)2003(1,3)(2,4)2004"""2005G = self._gap_().DirectProduct(other)2006D = PermutationGroup(gap_group=G)2007if not maps:2008return D2009else:2010from sage.groups.perm_gps.permgroup_morphism import PermutationGroupMorphism_from_gap2011iota1 = PermutationGroupMorphism_from_gap(self, D, G.Embedding(1))2012iota2 = PermutationGroupMorphism_from_gap(other, D, G.Embedding(2))2013pr1 = PermutationGroupMorphism_from_gap(D, self, G.Projection(1))2014pr2 = PermutationGroupMorphism_from_gap(D, other, G.Projection(2))2015return D, iota1, iota2, pr1, pr220162017def semidirect_product(self, N, mapping, check=True):2018r"""2019The semidirect product of ``self`` with ``N``.20202021INPUT:20222023- ``N`` - A group which is acted on by ``self`` and2024naturally embeds as a normal subgroup of the returned semidirect2025product.20262027- ``mapping`` - A pair of lists that together define a2028homomorphism, `\phi :` self `\rightarrow` Aut(N), by giving,2029in the second list, the images of the generators of ``self``2030in the order given in the first list.20312032- ``check`` - A boolean that, if set to False, will skip the2033initial tests which are made on ``mapping``. This may be beneficial2034for large ``N``, since in such cases the injectivity test can be2035expensive. Set to True by default.20362037OUTPUT:20382039The semidirect product of ``self`` and ``N`` defined by the2040action of ``self`` on ``N`` given in ``mapping`` (note that a2041homomorphism from A to the automorphism group of B is2042equivalent to an action of A on the B's underlying set). The2043semidirect product of two groups, `H` and `N`, is a construct2044similar to the direct product in so far as the elements are2045the Cartesian product of the elements of `H` and the elements2046of `N`. The operation, however, is built upon an action of `H`2047on `N`, and is defined as such:20482049.. MATH::20502051(h_1,n_1)(h_2,n_2) = (h_{1}h_{2}, n_{1}^{h_2}n_2)20522053This function is a wrapper for GAP's ``SemidirectProduct``2054command. The permutation group returned is built upon a2055permutation representation of the semidirect product of ``self``2056and ``N`` on a set of size `\mid N \mid`. The generators of2057``N`` are given as their right regular representations, while the2058generators of ``self`` are defined by the underlying action of2059``self`` on ``N``. It should be noted that the defining action is2060not always faithful, and in this case the inputted representations2061of the generators of ``self`` are placed on additional letters2062and adjoined to the output's generators of ``self``.206320642065EXAMPLES:20662067Perhaps the most common example of a semidirect product comes2068from the family of dihedral groups. Each dihedral group is the2069semidirect product of $C_2$ with $C_n$, where, by convention,2070$3 \leq n$. In this case, the nontrivial element of $C_2$ acts2071on $C_n$ so as to send each element to its inverse. ::20722073sage: C2 = CyclicPermutationGroup(2)2074sage: C8 = CyclicPermutationGroup(8)2075sage: alpha = PermutationGroupMorphism_im_gens(C8,C8,[(1,8,7,6,5,4,3,2)])2076sage: S = C2.semidirect_product(C8,[[(1,2)],[alpha]])2077sage: S == DihedralGroup(8)2078False2079sage: S.is_isomorphic(DihedralGroup(8))2080True2081sage: S.gens()2082[(3,4,5,6,7,8,9,10), (1,2)(4,10)(5,9)(6,8)]20832084A more complicated example can be drawn from [THOMAS-WOODS]_.2085It is there given that a semidirect product of $D_4$ and $C_3$2086is isomorphic to one of $C_2$ and the dicyclic group of order208712. This nonabelian group of order 24 has very similar2088structure to the dicyclic and dihedral groups of order 24, the2089three being the only groups of order 24 with a two-element2090center and 9 conjugacy classes. ::20912092sage: D4 = DihedralGroup(4)2093sage: C3 = CyclicPermutationGroup(3)2094sage: alpha1 = PermutationGroupMorphism_im_gens(C3,C3,[(1,3,2)])2095sage: alpha2 = PermutationGroupMorphism_im_gens(C3,C3,[(1,2,3)])2096sage: S1 = D4.semidirect_product(C3,[[(1,2,3,4),(1,3)],[alpha1,alpha2]])2097sage: C2 = CyclicPermutationGroup(2)2098sage: Q = DiCyclicGroup(3)2099sage: a = Q.gens()[0]; b=Q.gens()[1].inverse()2100sage: alpha = PermutationGroupMorphism_im_gens(Q,Q,[a,b])2101sage: S2 = C2.semidirect_product(Q,[[(1,2)],[alpha]])2102sage: S1.is_isomorphic(S2)2103True2104sage: S1.is_isomorphic(DihedralGroup(12))2105False2106sage: S1.is_isomorphic(DiCyclicGroup(6))2107False2108sage: S1.center()2109Subgroup of (Permutation Group with generators2110[(5,6,7), (1,2,3,4)(6,7), (1,3)]) generated by [(1,3)(2,4)]2111sage: len(S1.conjugacy_classes_representatives())2112921132114If your normal subgroup is large, and you are confident that2115your inputs will successfully create a semidirect product, then2116it is beneficial, for the sake of time efficiency, to set the2117``check`` parameter to ``False``. ::21182119sage: C2 = CyclicPermutationGroup(2)2120sage: C2000 = CyclicPermutationGroup(500)2121sage: alpha = PermutationGroupMorphism(C2000,C2000,[C2000.gen().inverse()])2122sage: S = C2.semidirect_product(C2000,[[(1,2)],[alpha]],check=False)21232124TESTS::21252126sage: C3 = CyclicPermutationGroup(3)2127sage: D4 = DihedralGroup(4)2128sage: alpha = PermutationGroupMorphism(C3,C3,[C3("(1,3,2)")])2129sage: alpha1 = PermutationGroupMorphism(C3,C3,[C3("(1,2,3)")])21302131sage: s = D4.semidirect_product('junk', [[(1,2,3,4),(1,2)], [alpha, alpha1]])2132Traceback (most recent call last):2133...2134TypeError: junk is not a permutation group21352136sage: s = D4.semidirect_product(C3, [[(1,2,3,4),(1,2)], [alpha, alpha1]])2137Traceback (most recent call last):2138...2139ValueError: the generator list must generate the calling group, [(1, 2, 3, 4), (1, 2)]2140does not generate Dihedral group of order 8 as a permutation group21412142sage: s = D4.semidirect_product(C3, [[(1,2,3,4),(1,3)], [alpha]])2143Traceback (most recent call last):2144...2145ValueError: the list of generators and the list of morphisms must be of equal length21462147sage: alpha2 = PermutationGroupMorphism(C3, D4, [D4("()")])2148sage: s = D4.semidirect_product(C3, [[(1,2,3,4),(1,3)], [alpha, alpha2]])2149Traceback (most recent call last):2150...2151ValueError: an element of the automorphism list is not an endomorphism (and is therefore not an automorphism)21522153sage: alpha3 = PermutationGroupMorphism(C3,C3,[C3("()")])2154sage: s = D4.semidirect_product(C3, [[(1,2,3,4),(1,3)], [alpha, alpha3]])2155Traceback (most recent call last):2156...2157ValueError: an element of the automorphism list is not an injection (and is therefore not an automorphism)21582159REFERENCES:21602161.. [THOMAS-WOODS] A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980)21622163AUTHOR:21642165- Kevin Halasz (2012-8-12)21662167"""2168if check == True:2169from sage.categories.finite_permutation_groups import FinitePermutationGroups2170if N not in FinitePermutationGroups():2171raise TypeError("{0} is not a permutation group".format(N))2172if not PermutationGroup(gens = mapping[0]) == self:2173msg = 'the generator list must generate the calling group, {0} does not generate {1}'2174raise ValueError(msg.format(mapping[0], self._repr_()))2175if len(mapping[0]) != len(mapping[1]):2176msg = 'the list of generators and the list of morphisms must be of equal length'2177raise ValueError(msg)2178if not all([a.is_endomorphism() for a in mapping[1]]):2179msg = 'an element of the automorphism list is not an endomorphism (and is therefore not an automorphism)'2180raise ValueError(msg)2181if not all([a.kernel().order() == 1 for a in mapping[1]]):2182msg = 'an element of the automorphism list is not an injection (and is therefore not an automorphism)'2183raise ValueError(msg)21842185# create a parallel list of the automorphisms of N in GAP2186gap.eval('N := Group(' + str(N.gens()) + ')')2187gens_string = ",".join([str(x) for x in N.gens()])2188homomorphism_cmd = 'alpha := GroupHomomorphismByImages(N, N, [{0}],[{1}])'2189gap.eval('morphisms := []')2190for alpha in mapping[1]:2191images_string = ",".join([str(alpha(n)) for n in N.gens()])2192gap.eval(homomorphism_cmd.format(gens_string, images_string))2193gap.eval('Add(morphisms, alpha)')2194# create the necessary homomorphism from self into the2195# automorphism group of N in GAP2196gap.eval('H := Group({0})'.format(mapping[0]))2197gap.eval('phi := GroupHomomorphismByImages(H, AutomorphismGroup(N),{},morphisms)'.format(mapping[0]))2198gap.eval('sdp := SemidirectProduct(H, phi, N)')2199return PermutationGroup(gap_group = 'sdp')22002201def holomorph(self):2202r"""2203The holomorph of a group as a permutation group.22042205The holomorph of a group `G` is the semidirect product2206`G \rtimes_{id} Aut(G)`, where `id` is the identity function2207on `Aut(G)`, the automorphism group of `G`.22082209See :wikipedia:`Holomorph (mathematics)`22102211OUTPUT:22122213Returns the holomorph of a given group as permutation group2214via a wrapping of GAP's semidirect product function.22152216EXAMPLES:22172218Thomas and Wood's 'Group Tables' (Shiva Publishing, 1980) tells2219us that the holomorph of `C_5` is the unique group of order 202220with a trivial center. ::22212222sage: C5 = CyclicPermutationGroup(5)2223sage: A = C5.holomorph()2224sage: A.order()2225202226sage: A.is_abelian()2227False2228sage: A.center()2229Subgroup of (Permutation Group with generators2230[(5,6,7,8,9), (1,2,4,3)(6,7,9,8)]) generated by [()]2231sage: A2232Permutation Group with generators [(5,6,7,8,9), (1,2,4,3)(6,7,9,8)]22332234Noting that the automorphism group of `D_4` is itself `D_4`, it2235can easily be shown that the holomorph is indeed an internal2236semidirect product of these two groups. ::22372238sage: D4 = DihedralGroup(4)2239sage: H = D4.holomorph()2240sage: H.gens()2241[(3,8)(4,7), (2,3,5,8), (2,5)(3,8), (1,4,6,7)(2,3,5,8), (1,8)(2,7)(3,6)(4,5)]2242sage: G = H.subgroup([H.gens()[0],H.gens()[1],H.gens()[2]])2243sage: N = H.subgroup([H.gens()[3],H.gens()[4]])2244sage: N.is_normal(H)2245True2246sage: G.is_isomorphic(D4)2247True2248sage: N.is_isomorphic(D4)2249True2250sage: G.intersection(N)2251Permutation Group with generators [()]2252sage: L = [H(x)*H(y) for x in G for y in N]; L.sort()2253sage: L1 = H.list(); L1.sort()2254sage: L == L12255True22562257Author:22582259- Kevin Halasz (2012-08-14)2260"""22612262gap.eval('G := Group(' + str(self.gens()) + ')')2263gap.eval('aut := AutomorphismGroup(G)')2264gap.eval('alpha := InverseGeneralMapping(NiceMonomorphism(aut))')2265gap.eval('product := SemidirectProduct(NiceObject(aut),alpha,G)')2266return PermutationGroup(gap_group = 'product')22672268def subgroup(self, gens=None, gap_group=None, domain=None, category=None, canonicalize=True, check=True):2269"""2270Wraps the ``PermutationGroup_subgroup`` constructor. The argument2271``gens`` is a list of elements of ``self``.22722273EXAMPLES::22742275sage: G = PermutationGroup([(1,2,3),(3,4,5)])2276sage: g = G((1,2,3))2277sage: G.subgroup([g])2278Subgroup of (Permutation Group with generators [(3,4,5), (1,2,3)]) generated by [(1,2,3)]2279"""2280return PermutationGroup_subgroup(self, gens=gens, gap_group=gap_group, domain=None,2281category=category, canonicalize=canonicalize, check=check)22822283def as_finitely_presented_group(self, reduced=False):2284"""2285Return a finitely presented group isomorphic to ``self``.22862287This method acts as wrapper for the GAP function ``IsomorphismFpGroupByGenerators``,2288which yields an isomorphism from a given group to a finitely presented group.22892290INPUT:22912292- ``reduced`` -- Default ``False``, if ``True`` :meth:`FinitelyPresentedGroup.simplified2293<sage.groups.finitely_presented.FinitelyPresentedGroup.simplified>`2294is called, attempting to simplify the presentation of the finitely presented group2295to be returned.22962297OUTPUT:22982299Finite presentation of self, obtained by taking the image2300of the isomorphism returned by the GAP function, ``IsomorphismFpGroupByGenerators``.23012302ALGORITHM:23032304Uses GAP.23052306EXAMPLES::23072308sage: CyclicPermutationGroup(50).as_finitely_presented_group()2309Finitely presented group < a | a^50 >2310sage: DihedralGroup(4).as_finitely_presented_group()2311Finitely presented group < a, b | b^2, a^4, (b*a)^2 >2312sage: GeneralDihedralGroup([2,2]).as_finitely_presented_group()2313Finitely presented group < a, b, c | a^2, b^2, c^2, (c*b)^2, (c*a)^2, (b*a)^2 >23142315GAP algorithm is not guaranteed to produce minimal or canonical presentation::23162317sage: G = PermutationGroup(['(1,2,3,4,5)', '(1,5)(2,4)'])2318sage: G.is_isomorphic(DihedralGroup(5))2319True2320sage: K = G.as_finitely_presented_group(); K2321Finitely presented group < a, b | b^2, (b*a)^2, b*a^-3*b*a^2 >2322sage: K.as_permutation_group().is_isomorphic(DihedralGroup(5))2323True23242325We can attempt to reduce the output presentation::23262327sage: PermutationGroup(['(1,2,3,4,5)','(1,3,5,2,4)']).as_finitely_presented_group()2328Finitely presented group < a, b | b^-2*a^-1, b*a^-2 >2329sage: PermutationGroup(['(1,2,3,4,5)','(1,3,5,2,4)']).as_finitely_presented_group(reduced=True)2330Finitely presented group < a | a^5 >23312332TESTS::23332334sage: PermutationGroup([]).as_finitely_presented_group()2335Finitely presented group < a | a >2336sage: S = SymmetricGroup(6)2337sage: perm_ls = [S.random_element() for i in range(3)]2338sage: G = PermutationGroup(perm_ls)2339sage: G.as_finitely_presented_group().as_permutation_group().is_isomorphic(G)2340True23412342`D_9` is the only non-Abelian group of order 182343with an automorphism group of order 54 [THOMAS-WOODS]_::23442345sage: D = DihedralGroup(9).as_finitely_presented_group().gap()2346sage: D.Order(), D.IsAbelian(), D.AutomorphismGroup().Order()2347(18, false, 54)23482349`S_3` is the only non-Abelian group of order 6 [THOMAS-WOODS]_::23502351sage: S = SymmetricGroup(3).as_finitely_presented_group().gap()2352sage: S.Order(), S.IsAbelian()2353(6, false)23542355We can manually construct a permutation representation using GAP2356coset enumeration methods::23572358sage: D = GeneralDihedralGroup([3,3,4]).as_finitely_presented_group().gap()2359sage: ctab = D.CosetTable(D.Subgroup([]))2360sage: gen_ls = gap.List(ctab, gap.PermList)2361sage: PermutationGroup(gen_ls).is_isomorphic(GeneralDihedralGroup([3,3,4]))2362True2363sage: A = AlternatingGroup(5).as_finitely_presented_group().gap()2364sage: ctab = A.CosetTable(A.Subgroup([]))2365sage: gen_ls = gap.List(ctab, gap.PermList)2366sage: PermutationGroup(gen_ls).is_isomorphic(AlternatingGroup(5))2367True23682369AUTHORS:23702371- Davis Shurbert (2013-06-21): initial version2372"""2373from sage.groups.free_group import FreeGroup, _lexi_gen2374from sage.groups.finitely_presented import FinitelyPresentedGroup2375from sage.libs.gap.libgap import libgap23762377image_fp = libgap.Image( libgap.IsomorphismFpGroupByGenerators(self, self.gens()))2378image_gens = image_fp.FreeGeneratorsOfFpGroup()2379name_itr = _lexi_gen() # Python generator object for variable names2380F = FreeGroup([name_itr.next() for x in image_gens])23812382# Convert GAP relators to Sage relators using the Tietze word of each defining relation.2383ret_rls = tuple([F(rel_word.TietzeWordAbstractWord(image_gens).sage())2384for rel_word in image_fp.RelatorsOfFpGroup()])2385ret_fp = FinitelyPresentedGroup(F,ret_rls)2386if reduced:2387ret_fp = ret_fp.simplified()2388return ret_fp23892390def quotient(self, N):2391"""2392Returns the quotient of this permutation group by the normal2393subgroup `N`, as a permutation group.23942395Wraps the GAP operator "/".23962397EXAMPLES::23982399sage: G = PermutationGroup([(1,2,3), (2,3)])2400sage: N = PermutationGroup([(1,2,3)])2401sage: G.quotient(N)2402Permutation Group with generators [(1,2)]2403sage: G.quotient(G)2404Permutation Group with generators [()]2405"""2406Q = self._gap_() / N._gap_()2407# Return Q as a permutation group2408# This is currently done using the right regular representation2409# FIXME: GAP certainly knows of a better way!2410phi = Q.RegularActionHomomorphism()2411return PermutationGroup(gap_group=phi.Image())24122413def quotient_group(self, N):2414"""2415This function has been deprecated and will be removed in a2416future version of Sage; use ``quotient`` instead.24172418Original docstring follows.24192420Returns the quotient of this permutation group by the normal2421subgroup `N`.24222423Wraps the GAP operator "/".24242425TESTS::24262427sage: G = PermutationGroup([(1,2,3), (2,3)])2428sage: N = PermutationGroup([(1,2,3)])2429sage: G.quotient_group(N)2430doctest:...: DeprecationWarning: quotient_group() is deprecated; use quotient() instead.2431See http://trac.sagemath.org/7371 for details.2432Permutation Group with generators [(1,2)]2433"""2434from sage.misc.superseded import deprecation2435deprecation(7371, 'quotient_group() is deprecated; use quotient() instead.')2436return self.quotient(N)24372438def commutator(self, other=None):2439r"""2440Returns the commutator subgroup of a group, or of a pair of groups.24412442INPUT:24432444- ``other`` - default: ``None`` - a permutation group.24452446OUTPUT:24472448Let $G$ denote ``self``. If ``other`` is ``None`` then this method2449returns the subgroup of $G$ generated by the set of commutators,24502451.. math::24522453\{[g_1,g_2]\vert g_1, g_2\in G\} = \{g_1^{-1}g_2^{-1}g_1g_2\vert g_1, g_2\in G\}24542455Let $H$ denote ``other``, in the case that it is not ``None``. Then2456this method returns the group generated by the set of commutators,24572458.. math::24592460\{[g,h]\vert g\in G\, h\in H\} = \{g^{-1}h^{-1}gh\vert g\in G\, h\in H\}24612462The two groups need only be permutation groups, there is no notion2463of requiring them to explicitly be subgroups of some other group.24642465.. note::24662467For the identical statement, the generators of the returned2468group can vary from one execution to the next.24692470EXAMPLES::24712472sage: G = DiCyclicGroup(4)2473sage: G.commutator()2474Permutation Group with generators [(1,3,5,7)(2,4,6,8)(9,11,13,15)(10,12,14,16)]24752476sage: G = SymmetricGroup(5)2477sage: H = CyclicPermutationGroup(5)2478sage: C = G.commutator(H)2479sage: C.is_isomorphic(AlternatingGroup(5))2480True24812482An abelian group will have a trivial commutator. ::24832484sage: G = CyclicPermutationGroup(10)2485sage: G.commutator()2486Permutation Group with generators [()]24872488The quotient of a group by its commutator is always abelian. ::24892490sage: G = DihedralGroup(20)2491sage: C = G.commutator()2492sage: Q = G.quotient(C)2493sage: Q.is_abelian()2494True24952496When forming commutators from two groups, the order of the2497groups does not matter. ::24982499sage: D = DihedralGroup(3)2500sage: S = SymmetricGroup(2)2501sage: C1 = D.commutator(S); C12502Permutation Group with generators [(1,2,3)]2503sage: C2 = S.commutator(D); C22504Permutation Group with generators [(1,3,2)]2505sage: C1 == C22506True25072508This method calls two different functions in GAP, so2509this tests that their results are consistent. The2510commutator groups may have different generators, but the2511groups are equal. ::25122513sage: G = DiCyclicGroup(3)2514sage: C = G.commutator(); C2515Permutation Group with generators [(5,7,6)]2516sage: CC = G.commutator(G); CC2517Permutation Group with generators [(5,6,7)]2518sage: C == CC2519True25202521The second group is checked. ::25222523sage: G = SymmetricGroup(2)2524sage: G.commutator('junk')2525Traceback (most recent call last):2526...2527TypeError: junk is not a permutation group2528"""2529if other is None:2530return PermutationGroup(gap_group=gap.DerivedSubgroup(self))2531else:2532from sage.categories.finite_permutation_groups import FinitePermutationGroups2533if other not in FinitePermutationGroups():2534raise TypeError("{0} is not a permutation group".format(other))2535return PermutationGroup(gap_group=gap.CommutatorSubgroup(self, other))25362537@hap_decorator2538def cohomology(self, n, p = 0):2539r"""2540Computes the group cohomology `H^n(G, F)`, where `F = \ZZ`2541if `p=0` and `F = \ZZ / p \ZZ` if `p > 0` is a prime.2542Wraps HAP's ``GroupHomology`` function, written by Graham Ellis.25432544REQUIRES: GAP package HAP (in gap_packages-\*.spkg).25452546EXAMPLES::25472548sage: G = SymmetricGroup(4)2549sage: G.cohomology(1,2) # optional - gap_packages2550Multiplicative Abelian group isomorphic to C22551sage: G = SymmetricGroup(3)2552sage: G.cohomology(5) # optional - gap_packages2553Trivial Abelian group2554sage: G.cohomology(5,2) # optional - gap_packages2555Multiplicative Abelian group isomorphic to C22556sage: G.homology(5,3) # optional - gap_packages2557Trivial Abelian group2558sage: G.homology(5,4) # optional - gap_packages2559Traceback (most recent call last):2560...2561ValueError: p must be 0 or prime25622563This computes `H^4(S_3, \ZZ)` and2564`H^4(S_3, \ZZ / 2 \ZZ)`, respectively.25652566AUTHORS:25672568- David Joyner and Graham Ellis25692570REFERENCES:25712572- G. Ellis, 'Computing group resolutions', J. Symbolic2573Computation. Vol.38, (2004)1077-1118 (Available at2574http://hamilton.nuigalway.ie/).25752576- D. Joyner, 'A primer on computational group homology and2577cohomology', http://front.math.ucdavis.edu/0706.0549.2578"""2579if p == 0:2580L = self._gap_().GroupCohomology(n).sage()2581else:2582L = self._gap_().GroupCohomology(n, p).sage()2583return AbelianGroup(len(L), L)25842585@hap_decorator2586def cohomology_part(self, n, p = 0):2587"""2588Computes the p-part of the group cohomology `H^n(G, F)`,2589where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if2590`p > 0` is a prime. Wraps HAP's Homology function, written2591by Graham Ellis, applied to the `p`-Sylow subgroup of2592`G`.25932594REQUIRES: GAP package HAP (in gap_packages-\*.spkg).25952596EXAMPLES::25972598sage: G = SymmetricGroup(5)2599sage: G.cohomology_part(7,2) # optional - gap_packages2600Multiplicative Abelian group isomorphic to C2 x C2 x C22601sage: G = SymmetricGroup(3)2602sage: G.cohomology_part(2,3) # optional - gap_packages2603Multiplicative Abelian group isomorphic to C326042605AUTHORS:26062607- David Joyner and Graham Ellis2608"""2609if p == 0:2610return AbelianGroup(1, [1])2611else:2612G = self._gap_()2613S = G.SylowSubgroup(p)2614R = S.ResolutionFiniteGroup(n+1)2615HR = R.HomToIntegers()2616L = HR.Cohomology(n).sage()2617return AbelianGroup(len(L), L)26182619@hap_decorator2620def homology(self, n, p = 0):2621r"""2622Computes the group homology `H_n(G, F)`, where2623`F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if2624`p > 0` is a prime. Wraps HAP's ``GroupHomology`` function,2625written by Graham Ellis.26262627REQUIRES: GAP package HAP (in gap_packages-\*.spkg).26282629AUTHORS:26302631- David Joyner and Graham Ellis26322633The example below computes `H_7(S_5, \ZZ)`,2634`H_7(S_5, \ZZ / 2 \ZZ)`,2635`H_7(S_5, \ZZ / 3 \ZZ)`, and2636`H_7(S_5, \ZZ / 5 \ZZ)`, respectively. To compute the2637`2`-part of `H_7(S_5, \ZZ)`, use the ``homology_part``2638function.26392640EXAMPLES::26412642sage: G = SymmetricGroup(5)2643sage: G.homology(7) # optional - gap_packages2644Multiplicative Abelian group isomorphic to C2 x C2 x C4 x C3 x C52645sage: G.homology(7,2) # optional - gap_packages2646Multiplicative Abelian group isomorphic to C2 x C2 x C2 x C2 x C22647sage: G.homology(7,3) # optional - gap_packages2648Multiplicative Abelian group isomorphic to C32649sage: G.homology(7,5) # optional - gap_packages2650Multiplicative Abelian group isomorphic to C526512652REFERENCES:26532654- G. Ellis, "Computing group resolutions", J. Symbolic2655Computation. Vol.38, (2004)1077-1118 (Available at2656http://hamilton.nuigalway.ie/.26572658- D. Joyner, "A primer on computational group homology and cohomology",2659http://front.math.ucdavis.edu/0706.05492660"""2661if p == 0:2662L = self._gap_().GroupHomology(n).sage()2663else:2664L = self._gap_().GroupHomology(n, p).sage()2665return AbelianGroup(len(L), L)26662667@hap_decorator2668def homology_part(self, n, p = 0):2669r"""2670Computes the `p`-part of the group homology2671`H_n(G, F)`, where `F = \ZZ` if `p=0` and2672`F = \ZZ / p \ZZ` if `p > 0` is a prime. Wraps HAP's2673``Homology`` function, written by Graham Ellis, applied to the2674`p`-Sylow subgroup of `G`.26752676REQUIRES: GAP package HAP (in gap_packages-\*.spkg).26772678EXAMPLES::26792680sage: G = SymmetricGroup(5)2681sage: G.homology_part(7,2) # optional - gap_packages2682Multiplicative Abelian group isomorphic to C2 x C2 x C2 x C2 x C426832684AUTHORS:26852686- David Joyner and Graham Ellis2687"""2688if p == 0:2689return AbelianGroup(1, [1])2690else:2691S = self._gap_().SylowSubgroup(p)2692R = S.ResolutionFiniteGroup(n+1)2693TR = R.TensorWithIntegers()2694L = TR.Homology(n).sage()2695return AbelianGroup(len(L), L)26962697def character_table(self):2698r"""2699Returns the matrix of values of the irreducible characters of a2700permutation group `G` at the conjugacy classes of2701`G`. The columns represent the conjugacy classes of2702`G` and the rows represent the different irreducible2703characters in the ordering given by GAP.27042705EXAMPLES::27062707sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3)]])2708sage: G.order()2709122710sage: G.character_table()2711[ 1 1 1 1]2712[ 1 1 -zeta3 - 1 zeta3]2713[ 1 1 zeta3 -zeta3 - 1]2714[ 3 -1 0 0]2715sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3)]])2716sage: CT = gap(G).CharacterTable()27172718Type ``print gap.eval("Display(%s)"%CT.name())`` to display this2719nicely.27202721::27222723sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4)]])2724sage: G.order()272582726sage: G.character_table()2727[ 1 1 1 1 1]2728[ 1 -1 -1 1 1]2729[ 1 -1 1 -1 1]2730[ 1 1 -1 -1 1]2731[ 2 0 0 0 -2]2732sage: CT = gap(G).CharacterTable()27332734Again, type ``print gap.eval("Display(%s)"%CT.name())`` to display this2735nicely.27362737::27382739sage: SymmetricGroup(2).character_table()2740[ 1 -1]2741[ 1 1]2742sage: SymmetricGroup(3).character_table()2743[ 1 -1 1]2744[ 2 0 -1]2745[ 1 1 1]2746sage: SymmetricGroup(5).character_table()2747[ 1 -1 1 1 -1 -1 1]2748[ 4 -2 0 1 1 0 -1]2749[ 5 -1 1 -1 -1 1 0]2750[ 6 0 -2 0 0 0 1]2751[ 5 1 1 -1 1 -1 0]2752[ 4 2 0 1 -1 0 -1]2753[ 1 1 1 1 1 1 1]2754sage: list(AlternatingGroup(6).character_table())2755[(1, 1, 1, 1, 1, 1, 1), (5, 1, 2, -1, -1, 0, 0), (5, 1, -1, 2, -1, 0, 0), (8, 0, -1, -1, 0, zeta5^3 + zeta5^2 + 1, -zeta5^3 - zeta5^2), (8, 0, -1, -1, 0, -zeta5^3 - zeta5^2, zeta5^3 + zeta5^2 + 1), (9, 1, 0, 0, 1, -1, -1), (10, -2, 1, 1, 0, 0, 0)]27562757Suppose that you have a class function `f(g)` on2758`G` and you know the values `v_1, \ldots, v_n` on2759the conjugacy class elements in2760``conjugacy_classes_representatives(G)`` =2761`[g_1, \ldots, g_n]`. Since the irreducible characters2762`\rho_1, \ldots, \rho_n` of `G` form an2763`E`-basis of the space of all class functions (`E`2764a "sufficiently large" cyclotomic field), such a class function is2765a linear combination of these basis elements,2766`f = c_1 \rho_1 + \cdots + c_n \rho_n`. To find2767the coefficients `c_i`, you simply solve the linear system2768``character_table_values(G)`` `[v_1, ..., v_n] = [c_1, ..., c_n]`,2769where `[v_1, \ldots, v_n]` = ``character_table_values(G)`` `^{(-1)}[c_1, ..., c_n]`.27702771AUTHORS:27722773- David Joyner and William Stein (2006-01-04)27742775"""2776G = self._gap_()2777cl = G.ConjugacyClasses()2778n = Integer(cl.Length())2779irrG = G.Irr()2780ct = [[irrG[i+1, j+1] for j in range(n)] for i in range(n)]27812782from sage.rings.all import CyclotomicField2783e = irrG.Flat().Conductor()2784K = CyclotomicField(e)2785ct = [[K(x) for x in v] for v in ct]27862787# Finally return the result as a matrix.2788from sage.matrix.all import MatrixSpace2789MS = MatrixSpace(K, n)2790return MS(ct)27912792def irreducible_characters(self):2793r"""2794Returns a list of the irreducible characters of ``self``.27952796EXAMPLES::27972798sage: irr = SymmetricGroup(3).irreducible_characters()2799sage: [x.values() for x in irr]2800[[1, -1, 1], [2, 0, -1], [1, 1, 1]]2801"""2802return [ClassFunction(self, irr) for irr in self._gap_().Irr()]28032804def trivial_character(self):2805r"""2806Returns the trivial character of ``self``.28072808EXAMPLES::28092810sage: SymmetricGroup(3).trivial_character()2811Character of Symmetric group of order 3! as a permutation group2812"""2813values = [1]*self._gap_().NrConjugacyClasses().sage()2814return self.character(values)28152816def character(self, values):2817r"""2818Returns a group character from ``values``, where ``values`` is2819a list of the values of the character evaluated on the conjugacy2820classes.28212822EXAMPLES::28232824sage: G = AlternatingGroup(4)2825sage: n = len(G.conjugacy_classes_representatives())2826sage: G.character([1]*n)2827Character of Alternating group of order 4!/2 as a permutation group2828"""2829return ClassFunction(self, values)28302831def conjugacy_classes_representatives(self):2832"""2833Returns a complete list of representatives of conjugacy classes in2834a permutation group `G`. The ordering is that given by GAP.28352836EXAMPLES::28372838sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4)]])2839sage: cl = G.conjugacy_classes_representatives(); cl2840[(), (2,4), (1,2)(3,4), (1,2,3,4), (1,3)(2,4)]2841sage: cl[3] in G2842True28432844::28452846sage: G = SymmetricGroup(5)2847sage: G.conjugacy_classes_representatives()2848[(), (1,2), (1,2)(3,4), (1,2,3), (1,2,3)(4,5), (1,2,3,4), (1,2,3,4,5)]28492850::28512852sage: S = SymmetricGroup(['a','b','c'])2853sage: S.conjugacy_classes_representatives()2854[(), ('a','b'), ('a','b','c')]28552856AUTHORS:28572858- David Joyner and William Stein (2006-01-04)2859"""2860cl = self._gap_().ConjugacyClasses()2861return [self(rep.Representative(), check=False) for rep in cl]28622863def conjugacy_classes_subgroups(self):2864"""2865Returns a complete list of representatives of conjugacy classes of2866subgroups in a permutation group `G`. The ordering is that given by2867GAP.28682869EXAMPLES::28702871sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4)]])2872sage: cl = G.conjugacy_classes_subgroups()2873sage: cl2874[Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [()], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(1,2)(3,4)], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(1,3)(2,4)], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4)], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(1,2)(3,4), (1,4)(2,3)], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4), (1,3)(2,4)], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(1,2,3,4), (1,3)(2,4)], Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4), (1,2)(3,4), (1,4)(2,3)]]28752876::28772878sage: G = SymmetricGroup(3)2879sage: G.conjugacy_classes_subgroups()2880[Subgroup of (Symmetric group of order 3! as a permutation group) generated by [()], Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(2,3)], Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(1,2,3)], Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(2,3), (1,2,3)]]28812882AUTHORS:28832884- David Joyner (2006-10)2885"""2886cl = self._gap_().ConjugacyClassesSubgroups()2887return [self.subgroup(gap_group=sub.Representative()) for sub in cl]28882889def subgroups(self):2890r"""2891Returns a list of all the subgroups of ``self``.28922893OUTPUT:28942895Each possible subgroup of ``self`` is contained once2896in the returned list. The list is in order, according2897to the size of the subgroups, from the trivial subgroup2898with one element on through up to the whole group.2899Conjugacy classes of subgroups are contiguous in the list.29002901.. warning::29022903For even relatively small groups this method can2904take a very long time to execute, or create vast2905amounts of output. Likely both. Its purpose is2906instructional, as it can be useful for studying2907small groups. The 156 subgroups of the full2908symmetric group on 5 symbols of order 120, `S_5`,2909can be computed in about a minute on commodity hardware2910in 2011. The 64 subgroups of the cyclic group of order2911`30030 = 2\cdot 3\cdot 5\cdot 7\cdot 11\cdot 13` takes2912about twice as long.29132914For faster results, which still exhibit the structure of2915the possible subgroups, use2916:meth:`conjugacy_classes_subgroups`.29172918EXAMPLES::29192920sage: G = SymmetricGroup(3)2921sage: G.subgroups()2922[Permutation Group with generators [()],2923Permutation Group with generators [(2,3)],2924Permutation Group with generators [(1,2)],2925Permutation Group with generators [(1,3)],2926Permutation Group with generators [(1,2,3)],2927Permutation Group with generators [(2,3), (1,2,3)]]29282929sage: G = CyclicPermutationGroup(14)2930sage: G.subgroups()2931[Permutation Group with generators [()],2932Permutation Group with generators [(1,8)(2,9)(3,10)(4,11)(5,12)(6,13)(7,14)],2933Permutation Group with generators [(1,3,5,7,9,11,13)(2,4,6,8,10,12,14)],2934Permutation Group with generators [(1,2,3,4,5,6,7,8,9,10,11,12,13,14)]]29352936AUTHOR:29372938- Rob Beezer (2011-01-24)2939"""2940all_sg = []2941ccs = self._gap_().ConjugacyClassesSubgroups()2942for cc in ccs:2943for h in cc.Elements():2944all_sg.append(PermutationGroup(gap_group=h))2945return all_sg29462947def blocks_all(self, representatives = True):2948r"""2949Returns the list of block systems of imprimitivity.29502951For more information on primitivity, see the :wikipedia:`Wikipedia2952article on primitive group actions <Primitive_permutation_group>`.29532954INPUT:29552956- ``representative`` (boolean) -- whether to return all possible block2957systems of imprimitivity or only one of their representatives (the2958block can be obtained from its representative set `S` by computing the2959orbit of `S` under ``self``).29602961This parameter is set to ``True`` by default (as it is GAP's default2962behaviour).29632964OUTPUT:29652966This method returns a description of *all* block systems. Hence, the2967output is a "list of lists of lists" or a "list of lists" depending on2968the value of ``representatives``. A bit more clearly, output is :29692970* A list of length (#number of different block systems) of29712972* block systems, each of them being defined as29732974* If ``representative = True`` : a list of representatives of2975each set of the block system29762977* If ``representative = False`` : a partition of the elements2978defining an imprimitivity block.29792980.. SEEALSO::29812982- :meth:`~PermutationGroup_generic.is_primitive`29832984EXAMPLE:29852986Picking an interesting group::29872988sage: g = graphs.DodecahedralGraph()2989sage: g.is_vertex_transitive()2990True2991sage: ag = g.automorphism_group()2992sage: ag.is_primitive()2993False29942995Computing its blocks representatives::29962997sage: ag.blocks_all()2998[[1, 16]]29993000Now the full blocks::30013002sage: ag.blocks_all(representatives = False)3003[[[1, 16], [2, 17], [15, 20], [9, 18], [6, 11], [3, 13], [8, 19], [4, 14], [5, 10], [7, 12]]]30043005"""3006ag = self._gap_()3007if representatives:3008return ag.AllBlocks().sage()3009else:3010return [ag.Orbit(rep,"OnSets").sage() for rep in ag.AllBlocks()]30113012def cosets(self, S, side='right'):3013r"""3014Returns a list of the cosets of ``S`` in ``self``.30153016INPUT:30173018- ``S`` - a subgroup of ``self``. An error is raised3019if ``S`` is not a subgroup.30203021- ``side`` - default: 'right' - determines if right cosets or3022left cosets are returned. ``side`` refers to where the3023representative is placed in the products forming the cosets3024and thus allowable values are only 'right' and 'left'.30253026OUTPUT:30273028A list of lists. Each inner list is a coset of the subgroup3029in the group. The first element of each coset is the smallest3030element (based on the ordering of the elements of ``self``)3031of all the group elements that have not yet appeared in a3032previous coset. The elements of each coset are in the same3033order as the subgroup elements used to build the coset's3034elements.30353036As a consequence, the subgroup itself is the first coset,3037and its first element is the identity element. For each coset,3038the first element listed is the element used as a representative3039to build the coset. These representatives form an increasing3040sequence across the list of cosets, and within a coset the3041representative is the smallest element of its coset (both3042orderings are based on of the ordering of elements of ``self``).30433044In the case of a normal subgroup, left and right cosets should3045appear in the same order as part of the outer list. However,3046the list of the elements of a particular coset may be in a3047different order for the right coset versus the order in the3048left coset. So, if you check to see if a subgroup is normal,3049it is necessary to sort each individual coset first (but not3050the list of cosets, due to the ordering of the representatives).3051See below for examples of this.30523053.. note::30543055This is a naive implementation intended for instructional3056purposes, and hence is slow for larger groups. Sage and GAP3057provide more sophisticated functions for working quickly with3058cosets of larger groups.30593060EXAMPLES:30613062The default is to build right cosets. This example works with3063the symmetry group of an 8-gon and a normal subgroup.3064Notice that a straight check on the equality of the output3065is not sufficient to check normality, while sorting the3066individual cosets is sufficient to then simply test equality of3067the list of lists. Study the second coset in each list to understand the3068need for sorting the elements of the cosets. ::30693070sage: G = DihedralGroup(8)3071sage: quarter_turn = G.list()[5]; quarter_turn3072(1,3,5,7)(2,4,6,8)3073sage: S = G.subgroup([quarter_turn])3074sage: rc = G.cosets(S); rc3075[[(), (1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8), (1,7,5,3)(2,8,6,4)],3076[(2,8)(3,7)(4,6), (1,7)(2,6)(3,5), (1,5)(2,4)(6,8), (1,3)(4,8)(5,7)],3077[(1,2)(3,8)(4,7)(5,6), (1,8)(2,7)(3,6)(4,5), (1,6)(2,5)(3,4)(7,8), (1,4)(2,3)(5,8)(6,7)],3078[(1,2,3,4,5,6,7,8), (1,4,7,2,5,8,3,6), (1,6,3,8,5,2,7,4), (1,8,7,6,5,4,3,2)]]3079sage: lc = G.cosets(S, side='left'); lc3080[[(), (1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8), (1,7,5,3)(2,8,6,4)],3081[(2,8)(3,7)(4,6), (1,3)(4,8)(5,7), (1,5)(2,4)(6,8), (1,7)(2,6)(3,5)],3082[(1,2)(3,8)(4,7)(5,6), (1,4)(2,3)(5,8)(6,7), (1,6)(2,5)(3,4)(7,8), (1,8)(2,7)(3,6)(4,5)],3083[(1,2,3,4,5,6,7,8), (1,4,7,2,5,8,3,6), (1,6,3,8,5,2,7,4), (1,8,7,6,5,4,3,2)]]30843085sage: S.is_normal(G)3086True3087sage: rc == lc3088False3089sage: rc_sorted = [sorted(c) for c in rc]3090sage: lc_sorted = [sorted(c) for c in lc]3091sage: rc_sorted == lc_sorted3092True30933094An example with the symmetry group of a regular3095tetrahedron and a subgroup that is not normal.3096Thus, the right and left cosets are different3097(and so are the representatives). With each3098individual coset sorted, a naive test of normality3099is possible. ::31003101sage: A = AlternatingGroup(4)3102sage: face_turn = A.list()[4]; face_turn3103(1,2,3)3104sage: stabilizer = A.subgroup([face_turn])3105sage: rc = A.cosets(stabilizer, side='right'); rc3106[[(), (1,2,3), (1,3,2)],3107[(2,3,4), (1,3)(2,4), (1,4,2)],3108[(2,4,3), (1,4,3), (1,2)(3,4)],3109[(1,2,4), (1,4)(2,3), (1,3,4)]]3110sage: lc = A.cosets(stabilizer, side='left'); lc3111[[(), (1,2,3), (1,3,2)],3112[(2,3,4), (1,2)(3,4), (1,3,4)],3113[(2,4,3), (1,2,4), (1,3)(2,4)],3114[(1,4,2), (1,4,3), (1,4)(2,3)]]31153116sage: stabilizer.is_normal(A)3117False3118sage: rc_sorted = [sorted(c) for c in rc]3119sage: lc_sorted = [sorted(c) for c in lc]3120sage: rc_sorted == lc_sorted3121False31223123TESTS:31243125The keyword ``side`` is checked for the two possible values. ::31263127sage: G = SymmetricGroup(3)3128sage: S = G.subgroup([G("(1,2)")])3129sage: G.cosets(S, side='junk')3130Traceback (most recent call last):3131...3132ValueError: side should be 'right' or 'left', not junk31333134The subgroup argument is checked to see if it is a permutation group.3135Even a legitimate GAP object can be rejected. ::31363137sage: G=SymmetricGroup(3)3138sage: G.cosets(gap(3))3139Traceback (most recent call last):3140...3141TypeError: 3 is not a permutation group31423143The subgroup is verified as a subgroup of ``self``. ::31443145sage: A = AlternatingGroup(3)3146sage: G = SymmetricGroup(3)3147sage: S = G.subgroup([G("(1,2)")])3148sage: A.cosets(S)3149Traceback (most recent call last):3150...3151ValueError: Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(1,2)] is not a subgroup of Alternating group of order 3!/2 as a permutation group31523153AUTHOR:31543155- Rob Beezer (2011-01-31)3156"""3157from copy import copy3158from sage.categories.finite_permutation_groups import FinitePermutationGroups31593160if not side in ['right','left']:3161raise ValueError("side should be 'right' or 'left', not %s" % side)3162if not S in FinitePermutationGroups():3163raise TypeError("%s is not a permutation group" % S)3164if not S.is_subgroup(self):3165raise ValueError("%s is not a subgroup of %s" % (S, self))31663167group = copy(self.list())3168group.sort()3169subgroup = [self(s) for s in S.list()]3170subgroup.sort()3171decomposition = []3172while group:3173rep = group[0]3174if side == 'right':3175coset = [e*rep for e in subgroup]3176if side == 'left':3177coset = [rep*e for e in subgroup]3178for e in coset:3179group.remove(e)3180decomposition.append(coset)3181return decomposition31823183def normalizer(self, g):3184"""3185Returns the normalizer of ``g`` in ``self``.31863187EXAMPLES::31883189sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4)]])3190sage: g = G([(1,3)])3191sage: G.normalizer(g)3192Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4), (1,3)]3193sage: g = G([(1,2,3,4)])3194sage: G.normalizer(g)3195Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4), (1,2,3,4), (1,3)(2,4)]3196sage: H = G.subgroup([G([(1,2,3,4)])])3197sage: G.normalizer(H)3198Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4), (1,2,3,4), (1,3)(2,4)]3199"""3200return self.subgroup(gap_group=self._gap_().Normalizer(g))32013202def centralizer(self, g):3203"""3204Returns the centralizer of ``g`` in ``self``.32053206EXAMPLES::32073208sage: G = PermutationGroup([[(1,2),(3,4)], [(1,2,3,4)]])3209sage: g = G([(1,3)])3210sage: G.centralizer(g)3211Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(2,4), (1,3)]3212sage: g = G([(1,2,3,4)])3213sage: G.centralizer(g)3214Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(1,2,3,4)]3215sage: H = G.subgroup([G([(1,2,3,4)])])3216sage: G.centralizer(H)3217Subgroup of (Permutation Group with generators [(1,2)(3,4), (1,2,3,4)]) generated by [(1,2,3,4)]3218"""3219return self.subgroup(gap_group=self._gap_().Centralizer(g))32203221def isomorphism_type_info_simple_group(self):3222"""3223If the group is simple, then this returns the name of the group.32243225EXAMPLES::32263227sage: G = CyclicPermutationGroup(5)3228sage: G.isomorphism_type_info_simple_group()3229rec(3230name := "Z(5)",3231parameter := 5,3232series := "Z" )32333234TESTS: This shows that the issue at trac ticket 7360 is fixed::32353236sage: G = KleinFourGroup()3237sage: G.is_simple()3238False3239sage: G.isomorphism_type_info_simple_group()3240Traceback (most recent call last):3241...3242TypeError: Group must be simple.3243"""3244if self.is_simple():3245info = self._gap_().IsomorphismTypeInfoFiniteSimpleGroup()3246return info3247else:3248raise TypeError, "Group must be simple."32493250###################### Boolean tests #####################32513252def is_abelian(self):3253"""3254Return ``True`` if this group is abelian.32553256EXAMPLES::32573258sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3259sage: G.is_abelian()3260False3261sage: G = PermutationGroup(['(1,2,3)(4,5)'])3262sage: G.is_abelian()3263True3264"""3265return self._gap_().IsAbelian().bool()32663267def is_commutative(self):3268"""3269Return ``True`` if this group is commutative.32703271EXAMPLES::32723273sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3274sage: G.is_commutative()3275False3276sage: G = PermutationGroup(['(1,2,3)(4,5)'])3277sage: G.is_commutative()3278True3279"""3280return self.is_abelian()32813282def is_cyclic(self):3283"""3284Return ``True`` if this group is cyclic.32853286EXAMPLES::32873288sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3289sage: G.is_cyclic()3290False3291sage: G = PermutationGroup(['(1,2,3)(4,5)'])3292sage: G.is_cyclic()3293True3294"""3295return self._gap_().IsCyclic().bool()32963297def is_elementary_abelian(self):3298"""3299Return ``True`` if this group is elementary abelian. An elementary3300abelian group is a finite abelian group, where every nontrivial3301element has order `p`, where `p` is a prime.33023303EXAMPLES::33043305sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3306sage: G.is_elementary_abelian()3307False3308sage: G = PermutationGroup(['(1,2,3)','(4,5,6)'])3309sage: G.is_elementary_abelian()3310True3311"""3312return self._gap_().IsElementaryAbelian().bool()33133314def isomorphism_to(self, right):3315"""3316Return an isomorphism from ``self`` to ``right`` if the groups3317are isomorphic, otherwise ``None``.33183319INPUT:332033213322- ``self`` - this group33233324- ``right`` - a permutation group332533263327OUTPUT:33283329- ``None`` or a morphism of permutation groups.33303331EXAMPLES::33323333sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3334sage: H = PermutationGroup(['(1,2,3)(4,5)'])3335sage: G.isomorphism_to(H) is None3336True3337sage: G = PermutationGroup([(1,2,3), (2,3)])3338sage: H = PermutationGroup([(1,2,4), (1,4)])3339sage: G.isomorphism_to(H) # not tested, see below3340Permutation group morphism:3341From: Permutation Group with generators [(2,3), (1,2,3)]3342To: Permutation Group with generators [(1,2,4), (1,4)]3343Defn: [(2,3), (1,2,3)] -> [(2,4), (1,2,4)]33443345TESTS:33463347Partial check that the output makes some sense::33483349sage: G.isomorphism_to(H)3350Permutation group morphism:3351From: Permutation Group with generators [(2,3), (1,2,3)]3352To: Permutation Group with generators [(1,2,4), (1,4)]3353Defn: [(2,3), (1,2,3)] -> [...]3354"""3355current_randstate().set_seed_gap()3356if not isinstance(right, PermutationGroup_generic):3357raise TypeError, "right must be a permutation group"33583359iso = self._gap_().IsomorphismGroups(right)3360if str(iso) == "fail":3361return None33623363dsts = [right(iso.Image(x), check=False) for x in self.gens()]33643365from permgroup_morphism import PermutationGroupMorphism_im_gens3366return PermutationGroupMorphism_im_gens(self, right, dsts)33673368def is_isomorphic(self, right):3369"""3370Return ``True`` if the groups are isomorphic.33713372INPUT:337333743375- ``self`` - this group33763377- ``right`` - a permutation group337833793380OUTPUT:33813382- boolean; ``True`` if ``self`` and ``right`` are isomorphic groups;3383``False`` otherwise.33843385EXAMPLES::33863387sage: v = ['(1,2,3)(4,5)', '(1,2,3,4,5)']3388sage: G = PermutationGroup(v)3389sage: H = PermutationGroup(['(1,2,3)(4,5)'])3390sage: G.is_isomorphic(H)3391False3392sage: G.is_isomorphic(G)3393True3394sage: G.is_isomorphic(PermutationGroup(list(reversed(v))))3395True3396"""3397if not isinstance(right, PermutationGroup_generic):3398raise TypeError, "right must be a permutation group"3399iso = self._gap_().IsomorphismGroups(right)3400return str(iso) != 'fail'34013402def is_monomial(self):3403"""3404Returns ``True`` if the group is monomial. A finite group is monomial3405if every irreducible complex character is induced from a linear3406character of a subgroup.34073408EXAMPLES::34093410sage: G = PermutationGroup(['(1,2,3)(4,5)'])3411sage: G.is_monomial()3412True3413"""3414return self._gap_().IsMonomialGroup().bool()34153416def is_nilpotent(self):3417"""3418Return ``True`` if this group is nilpotent.34193420EXAMPLES::34213422sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3423sage: G.is_nilpotent()3424False3425sage: G = PermutationGroup(['(1,2,3)(4,5)'])3426sage: G.is_nilpotent()3427True3428"""3429return self._gap_().IsNilpotent().bool()34303431def is_normal(self, other):3432"""3433Return ``True`` if this group is a normal subgroup of ``other``.34343435EXAMPLES::34363437sage: AlternatingGroup(4).is_normal(SymmetricGroup(4))3438True3439sage: H = PermutationGroup(['(1,2,3)(4,5)'])3440sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3441sage: H.is_normal(G)3442False3443"""3444if not(self.is_subgroup(other)):3445raise TypeError("%s must be a subgroup of %s"%(self, other))3446return other._gap_().IsNormal(self).bool()34473448def is_perfect(self):3449"""3450Return ``True`` if this group is perfect. A group is perfect if it3451equals its derived subgroup.34523453EXAMPLES::34543455sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3456sage: G.is_perfect()3457False3458sage: G = PermutationGroup(['(1,2,3)(4,5)'])3459sage: G.is_perfect()3460False3461"""3462return self._gap_().IsPerfectGroup().bool()34633464def is_pgroup(self):3465"""3466Returns ``True`` if this group is a `p`-group. A finite group is3467a `p`-group if its order is of the form `p^n` for a prime integer3468`p` and a nonnegative integer `n`.34693470EXAMPLES::34713472sage: G = PermutationGroup(['(1,2,3,4,5)'])3473sage: G.is_pgroup()3474True3475"""3476return self._gap_().IsPGroup().bool()34773478def is_polycyclic(self):3479r"""3480Return ``True`` if this group is polycyclic. A group is polycyclic if3481it has a subnormal series with cyclic factors. (For finite groups,3482this is the same as if the group is solvable - see3483``is_solvable``.)34843485EXAMPLES::34863487sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3488sage: G.is_polycyclic()3489False3490sage: G = PermutationGroup(['(1,2,3)(4,5)'])3491sage: G.is_polycyclic()3492True3493"""3494return self._gap_().IsPolycyclicGroup().bool()34953496def is_simple(self):3497"""3498Returns ``True`` if the group is simple. A group is simple if it has no3499proper normal subgroups.35003501EXAMPLES::35023503sage: G = PermutationGroup(['(1,2,3)(4,5)'])3504sage: G.is_simple()3505False3506"""3507return self._gap_().IsSimpleGroup().bool()35083509def is_solvable(self):3510"""3511Returns ``True`` if the group is solvable.35123513EXAMPLES::35143515sage: G = PermutationGroup(['(1,2,3)(4,5)'])3516sage: G.is_solvable()3517True3518"""3519return self._gap_().IsSolvableGroup().bool()35203521def is_subgroup(self, other):3522"""3523Returns ``True`` if ``self`` is a subgroup of ``other``.35243525EXAMPLES::35263527sage: G = AlternatingGroup(5)3528sage: H = SymmetricGroup(5)3529sage: G.is_subgroup(H)3530True3531"""3532return all((x in other) for x in self.gens())35333534def is_supersolvable(self):3535"""3536Returns ``True`` if the group is supersolvable. A finite group is3537supersolvable if it has a normal series with cyclic factors.35383539EXAMPLES::35403541sage: G = PermutationGroup(['(1,2,3)(4,5)'])3542sage: G.is_supersolvable()3543True3544"""3545return self._gap_().IsSupersolvableGroup().bool()35463547def non_fixed_points(self):3548r"""3549Returns the list of points not fixed by ``self``, i.e., the subset3550of ``self.domain()`` moved by some element of ``self``.35513552EXAMPLES::35533554sage: G = PermutationGroup([[(3,4,5)],[(7,10)]])3555sage: G.non_fixed_points()3556[3, 4, 5, 7, 10]3557sage: G = PermutationGroup([[(2,3,6)],[(9,)]]) # note: 9 is fixed3558sage: G.non_fixed_points()3559[2, 3, 6]3560"""3561pnts = set()3562for gens in self.gens():3563for cycles in gens.cycle_tuples():3564for thispnt in cycles:3565if thispnt not in pnts:3566pnts.add(thispnt)3567return sorted(pnts)35683569def fixed_points(self):3570r"""3571Returns the list of points fixed by ``self``, i.e., the subset3572of ``.domain()`` not moved by any element of ``self``.35733574EXAMPLES::35753576sage: G=PermutationGroup([(1,2,3)])3577sage: G.fixed_points()3578[]3579sage: G=PermutationGroup([(1,2,3),(5,6)])3580sage: G.fixed_points()3581[4]3582sage: G=PermutationGroup([[(1,4,7)],[(4,3),(6,7)]])3583sage: G.fixed_points()3584[2, 5]3585"""3586non_fixed_points = self.non_fixed_points()3587return [i for i in self.domain() if i not in non_fixed_points]35883589def is_transitive(self, domain=None):3590"""3591Returns ``True`` if ``self`` acts transitively on ``domain``.3592A group $G$ acts transitively on set $S$ if for all $x,y\in S$3593there is some $g\in G$ such that $x^g=y$.35943595EXAMPLES::35963597sage: G = SymmetricGroup(5)3598sage: G.is_transitive()3599True3600sage: G = PermutationGroup(['(1,2)(3,4)(5,6)'])3601sage: G.is_transitive()3602False36033604::36053606sage: G = PermutationGroup([[(1,2,3,4,5)],[(1,2)]]) #S_5 on [1..5]3607sage: G.is_transitive([1,4,5])3608True3609sage: G.is_transitive([2..6])3610False3611sage: G.is_transitive(G.non_fixed_points())3612True3613sage: H = PermutationGroup([[(1,2,3)],[(4,5,6)]])3614sage: H.is_transitive(H.non_fixed_points())3615False36163617Note that this differs from the definition in GAP, where3618``IsTransitive`` returns whether the group is transitive on the3619set of points moved by the group.36203621::36223623sage: G = PermutationGroup([(2,3)])3624sage: G.is_transitive()3625False3626sage: gap(G).IsTransitive()3627true3628"""3629#If the domain is not a subset of self.domain(), then the3630#action isn't transitive.3631try:3632domain = self._domain_gap(domain)3633except ValueError:3634return False36353636return self._gap_().IsTransitive(domain).bool()36373638def is_primitive(self, domain=None):3639r"""3640Returns ``True`` if ``self`` acts primitively on ``domain``.3641A group $G$ acts primitively on a set $S$ if364236431. $G$ acts transitively on $S$ and364436452. the action induces no non-trivial block system on $S$.36463647INPUT:36483649- ``domain`` (optional)36503651.. SEEALSO::36523653- :meth:`~PermutationGroup_generic.blocks_all`36543655EXAMPLES:36563657By default, test for primitivity of ``self`` on its domain::36583659sage: G = PermutationGroup([[(1,2,3,4)],[(1,2)]])3660sage: G.is_primitive()3661True3662sage: G = PermutationGroup([[(1,2,3,4)],[(2,4)]])3663sage: G.is_primitive()3664False36653666You can specify a domain on which to test primitivity::36673668sage: G = PermutationGroup([[(1,2,3,4)],[(2,4)]])3669sage: G.is_primitive([1..4])3670False3671sage: G.is_primitive([1,2,3])3672True3673sage: G = PermutationGroup([[(3,4,5,6)],[(3,4)]]) #S_4 on [3..6]3674sage: G.is_primitive(G.non_fixed_points())3675True36763677"""3678#If the domain is not a subset of self.domain(), then the3679#action isn't primitive.3680try:3681domain = self._domain_gap(domain)3682except ValueError:3683return False36843685return self._gap_().IsPrimitive(domain).bool()36863687def is_semi_regular(self, domain=None):3688r"""3689Returns ``True`` if ``self`` acts semi-regularly on ``domain``.3690A group $G$ acts semi-regularly on a set $S$ if the point3691stabilizers of $S$ in $G$ are trivial.36923693``domain`` is optional and may take several forms. See examples.36943695EXAMPLES::36963697sage: G = PermutationGroup([[(1,2,3,4)]])3698sage: G.is_semi_regular()3699True3700sage: G = PermutationGroup([[(1,2,3,4)],[(5,6)]])3701sage: G.is_semi_regular()3702False37033704You can pass in a domain to test semi-regularity::37053706sage: G = PermutationGroup([[(1,2,3,4)],[(5,6)]])3707sage: G.is_semi_regular([1..4])3708True3709sage: G.is_semi_regular(G.non_fixed_points())3710False37113712"""3713try:3714domain = self._domain_gap(domain)3715except ValueError:3716return False3717return self._gap_().IsSemiRegular(domain).bool()37183719def is_regular(self, domain=None):3720r"""3721Returns ``True`` if ``self`` acts regularly on ``domain``.3722A group $G$ acts regularly on a set $S$ if372337241. $G$ acts transitively on $S$ and37252. $G$ acts semi-regularly on $S$.37263727EXAMPLES::37283729sage: G = PermutationGroup([[(1,2,3,4)]])3730sage: G.is_regular()3731True3732sage: G = PermutationGroup([[(1,2,3,4)],[(5,6)]])3733sage: G.is_regular()3734False37353736You can pass in a domain on which to test regularity::37373738sage: G = PermutationGroup([[(1,2,3,4)],[(5,6)]])3739sage: G.is_regular([1..4])3740True3741sage: G.is_regular(G.non_fixed_points())3742False37433744"""3745try:3746domain = self._domain_gap(domain)3747except ValueError:3748return False3749return self._gap_().IsRegular(domain).bool()375037513752def normalizes(self, other):3753r"""3754Returns ``True`` if the group ``other`` is normalized by ``self``.3755Wraps GAP's ``IsNormal`` function.37563757A group `G` normalizes a group `U` if and only if for every3758`g \in G` and `u \in U` the element `u^g`3759is a member of `U`. Note that `U` need not be a subgroup of `G`.37603761EXAMPLES::37623763sage: G = PermutationGroup(['(1,2,3)(4,5)'])3764sage: H = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])3765sage: H.normalizes(G)3766False3767sage: G = SymmetricGroup(3)3768sage: H = PermutationGroup( [ (4,5,6) ] )3769sage: G.normalizes(H)3770True3771sage: H.normalizes(G)3772True37733774In the last example, `G` and `H` are disjoint, so each normalizes the3775other.3776"""3777return self._gap_().IsNormal(other).bool()37783779############## Series ######################37803781def composition_series(self):3782"""3783Return the composition series of this group as a list of3784permutation groups.37853786EXAMPLES:37873788These computations use pseudo-random numbers, so we set3789the seed for reproducible testing.37903791::37923793sage: set_random_seed(0)3794sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])3795sage: G.composition_series() # random output3796[Permutation Group with generators [(1,2,3)(4,5), (3,4)], Permutation Group with generators [(1,5)(3,4), (1,5)(2,3), (1,5,4)], Permutation Group with generators [()]]3797sage: G = PermutationGroup([[(1,2,3),(4,5)], [(1,2)]])3798sage: CS = G.composition_series()3799sage: CS[3]3800Subgroup of (Permutation Group with generators [(1,2), (1,2,3)(4,5)]) generated by [()]3801"""3802current_randstate().set_seed_gap()3803CS = self._gap_().CompositionSeries()3804return [self.subgroup(gap_group=group) for group in CS]38053806def derived_series(self):3807"""3808Return the derived series of this group as a list of permutation3809groups.38103811EXAMPLES:38123813These computations use pseudo-random numbers, so we set3814the seed for reproducible testing.38153816::38173818sage: set_random_seed(0)3819sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])3820sage: G.derived_series() # random output3821[Permutation Group with generators [(1,2,3)(4,5), (3,4)], Permutation Group with generators [(1,5)(3,4), (1,5)(2,4), (2,4)(3,5)]]3822"""3823current_randstate().set_seed_gap()3824DS = self._gap_().DerivedSeries()3825return [self.subgroup(gap_group=group) for group in DS]38263827def lower_central_series(self):3828"""3829Return the lower central series of this group as a list of3830permutation groups.38313832EXAMPLES:38333834These computations use pseudo-random numbers, so we set3835the seed for reproducible testing.38363837::38383839sage: set_random_seed(0)3840sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])3841sage: G.lower_central_series() # random output3842[Permutation Group with generators [(1,2,3)(4,5), (3,4)], Permutation Group with generators [(1,5)(3,4), (1,5)(2,3), (1,3)(2,4)]]3843"""3844current_randstate().set_seed_gap()3845LCS = self._gap_().LowerCentralSeriesOfGroup()3846return [self.subgroup(gap_group=group) for group in LCS]38473848def molien_series(self):3849r"""3850Returns the Molien series of a transitive permutation group. The3851function38523853.. math::38543855M(x) = (1/|G|)\sum_{g\in G} \det(1-x*g)^{-1}385638573858is sometimes called the "Molien series" of `G`. GAP's3859``MolienSeries`` is associated to a character of a3860group `G`. How are these related? A group `G`, given as a permutation3861group on `n` points, has a "natural" representation of dimension `n`,3862given by permutation matrices. The Molien series of `G` is the one3863associated to that permutation representation of `G` using the above3864formula. Character values then count fixed points of the3865corresponding permutations.38663867EXAMPLES::38683869sage: G = SymmetricGroup(5)3870sage: G.molien_series()38711/(-x^15 + x^14 + x^13 - x^10 - x^9 - x^8 + x^7 + x^6 + x^5 - x^2 - x + 1)3872sage: G = SymmetricGroup(3)3873sage: G.molien_series()38741/(-x^6 + x^5 + x^4 - x^2 - x + 1)3875"""3876pi = self._gap_().NaturalCharacter()3877cc = pi.ConstituentsOfCharacter()3878M = cc.Sum().MolienSeries()38793880R = QQ['x']3881nn = M.NumeratorOfRationalFunction()3882dd = M.DenominatorOfRationalFunction()3883return (R(str(nn).replace("_1","")) /3884R(str(dd).replace("_1","")))38853886def normal_subgroups(self):3887"""3888Return the normal subgroups of this group as a (sorted in3889increasing order) list of permutation groups.38903891The normal subgroups of `H = PSL(2,7) \\times PSL(2,7)` are3892`1`, two copies of `PSL(2,7)` and `H`3893itself, as the following example shows.38943895EXAMPLES::38963897sage: G = PSL(2,7)3898sage: D = G.direct_product(G)3899sage: H = D[0]3900sage: NH = H.normal_subgroups()3901sage: len(NH)390243903sage: NH[1].is_isomorphic(G)3904True3905sage: NH[2].is_isomorphic(G)3906True3907"""3908NS = self._gap_().NormalSubgroups()3909return [self.subgroup(gap_group=group) for group in NS]39103911def poincare_series(self, p=2, n=10):3912"""3913Returns the Poincare series of `G \mod p` (`p \geq 2` must be a3914prime), for `n` large. In other words, if you input a finite3915group `G`, a prime `p`, and a positive integer `n`, it returns a3916quotient of polynomials `f(x) = P(x) / Q(x)` whose coefficient of3917`x^k` equals the rank of the vector space3918`H_k(G, \ZZ / p \ZZ)`, for all `k` in the3919range `1 \leq k \leq n`.39203921REQUIRES: GAP package HAP (in gap_packages-\*.spkg).39223923EXAMPLES::39243925sage: G = SymmetricGroup(5)3926sage: G.poincare_series(2,10) # optional - gap_packages3927(x^2 + 1)/(x^4 - x^3 - x + 1)3928sage: G = SymmetricGroup(3)3929sage: G.poincare_series(2,10) # optional - gap_packages39301/(-x + 1)39313932AUTHORS:39333934- David Joyner and Graham Ellis3935"""3936if not is_package_installed('gap_packages'):3937raise RuntimeError, "You must install the optional gap_packages package."3938load_hap()3939from sage.rings.arith import is_prime3940if not (p == 0 or is_prime(p)):3941raise ValueError, "p must be 0 or prime"39423943ff = self._gap_().PoincareSeriesPrimePart(p, n)3944R = QQ['x']3945nn = ff.NumeratorOfRationalFunction()3946dd = ff.DenominatorOfRationalFunction()3947return (R(str(nn).replace('x_1', 'x')) /3948R(str(dd).replace('x_1', 'x')))394939503951def sylow_subgroup(self, p):3952"""3953Returns a Sylow `p`-subgroup of the finite group `G`, where `p` is a3954prime. This is a `p`-subgroup of `G` whose index in `G` is coprime to3955`p`. Wraps the GAP function ``SylowSubgroup``.39563957EXAMPLES::39583959sage: G = PermutationGroup(['(1,2,3)', '(2,3)'])3960sage: G.sylow_subgroup(2)3961Subgroup of (Permutation Group with generators [(2,3), (1,2,3)]) generated by [(2,3)]3962sage: G.sylow_subgroup(5)3963Subgroup of (Permutation Group with generators [(2,3), (1,2,3)]) generated by [()]39643965TESTS:39663967Implementation details should not prevent us from computing3968large subgroups (trac #5491)::39693970sage: PSL(10,2).sylow_subgroup(7)3971Subgroup of...39723973"""3974return self.subgroup(gap_group=self._gap_().SylowSubgroup(p))39753976def upper_central_series(self):3977"""3978Return the upper central series of this group as a list of3979permutation groups.39803981EXAMPLES:39823983These computations use pseudo-random numbers, so we set3984the seed for reproducible testing::39853986sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])3987sage: G.upper_central_series()3988[Subgroup of (Permutation Group with generators [(3,4), (1,2,3)(4,5)]) generated by [()]]3989"""3990current_randstate().set_seed_gap()3991UCS = self._gap_().UpperCentralSeriesOfGroup()3992return [self.subgroup(gap_group=group) for group in UCS]39933994class PermutationGroup_subgroup(PermutationGroup_generic):3995"""3996Subgroup subclass of ``PermutationGroup_generic``, so instance methods3997are inherited.39983999EXAMPLES::40004001sage: G = CyclicPermutationGroup(4)4002sage: gens = G.gens()4003sage: H = DihedralGroup(4)4004sage: H.subgroup(gens)4005Subgroup of (Dihedral group of order 8 as a permutation group) generated by [(1,2,3,4)]4006sage: K = H.subgroup(gens)4007sage: K.list()4008[(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)]4009sage: K.ambient_group()4010Dihedral group of order 8 as a permutation group4011sage: K.gens()4012[(1,2,3,4)]4013"""4014def __init__(self, ambient, gens=None, gap_group=None, domain=None,4015category=None, canonicalize=True, check=True):4016r"""4017Initialization method for the4018``PermutationGroup_subgroup`` class.40194020INPUTS:40214022- ``ambient`` - the ambient group from which to construct this4023subgroup40244025- ``gens`` - the generators of the subgroup40264027- ``from_group`` - ``True``: subgroup is generated from a Gap4028string representation of the generators (default: ``False``)40294030- ``check`` - ``True``: checks if ``gens`` are indeed elements of the4031ambient group40324033- ``canonicalize`` - boolean (default: ``True``); if ``True``, sort4034generators and remove duplicates40354036EXAMPLES:40374038An example involving the dihedral group on four elements.4039`D_8` contains a cyclic subgroup or order four::40404041sage: G = DihedralGroup(4)4042sage: H = CyclicPermutationGroup(4)4043sage: gens = H.gens(); gens4044[(1,2,3,4)]4045sage: S = G.subgroup(gens)4046sage: S4047Subgroup of (Dihedral group of order 8 as a permutation group) generated by [(1,2,3,4)]4048sage: S.list()4049[(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)]4050sage: S.ambient_group()4051Dihedral group of order 8 as a permutation group40524053However, `D_8` does not contain a cyclic subgroup of order4054three::40554056sage: G = DihedralGroup(4)4057sage: H = CyclicPermutationGroup(3)4058sage: gens = H.gens()4059sage: S = PermutationGroup_subgroup(G,list(gens))4060Traceback (most recent call last):4061...4062TypeError: each generator must be in the ambient group4063"""4064if not isinstance(ambient, PermutationGroup_generic):4065raise TypeError, "ambient (=%s) must be perm group."%ambient4066if domain is None:4067domain = ambient.domain()4068if category is None:4069category = ambient.category()4070PermutationGroup_generic.__init__(self, gens=gens, gap_group=gap_group, domain=domain,4071category=category, canonicalize=canonicalize)40724073self._ambient_group = ambient4074if check:4075for g in self.gens():4076if g not in ambient:4077raise TypeError, "each generator must be in the ambient group"40784079def __cmp__(self, other):4080r"""4081Compare ``self`` and ``other``.40824083First, ``self`` and ``other`` are compared as permutation4084groups, see :method:`PermutationGroup_generic.__cmp__`.4085Second, if both are equal, the ambient groups are compared,4086where (if necessary) ``other`` is considered a subgroup of4087itself.40884089EXAMPLES::40904091sage: G=SymmetricGroup(6)4092sage: G1=G.subgroup([G((1,2,3,4,5)),G((1,2))])4093sage: G2=G.subgroup([G((1,2,3,4)),G((1,2))])4094sage: K=G2.subgroup([G2((1,2,3))])4095sage: H=G1.subgroup([G1(())])40964097``H`` is subgroup of ``K``, and therefore we have::40984099sage: H<K4100True4101sage: K<H4102False41034104In the next example, both ``H`` and ``H2`` are trivial4105groups, but the ambient group of ``H2`` is strictly contained4106in the ambient group of ``H``, and thus we obtain::41074108sage: H2=G2.subgroup([G2(())])4109sage: H2<H4110True41114112Of course, ``G`` as a permutation group and ``G`` considered4113as sub-group of itself are different objects. But they4114compare equal, both when considered as permutation groups4115or permutation subgroups::41164117sage: G3 = G.subgroup([G((1,2,3,4,5,6)),G((1,2))])4118sage: G4119Symmetric group of order 6! as a permutation group4120sage: G34121Subgroup of (Symmetric group of order 6! as a permutation group) generated by [(1,2), (1,2,3,4,5,6)]4122sage: G is G34123False4124sage: G == G3 # as permutation groups4125True4126sage: G3 == G # as permutation subgroups4127True41284129TESTS::41304131sage: G = SymmetricGroup(4)4132sage: G.subgroup([G((1,2,3))]) == G.subgroup([G((2,3,1))])4133True4134sage: G.subgroup([G((1,2,3))]) == G.subgroup([G((1,3,2))])4135True41364137"""4138if self is other:4139return 04140if not isinstance(other, PermutationGroup_generic):4141return -14142c = PermutationGroup_generic.__cmp__(self, other)4143if c:4144return c4145if hasattr(other, 'ambient_group'):4146o_ambient = other.ambient_group()4147else:4148o_ambient = other4149return cmp(self.ambient_group(), o_ambient)41504151def _repr_(self):4152r"""4153Returns a string representation / description of the permutation4154subgroup.41554156EXAMPLES:41574158An example involving the dihedral group on four elements,4159`D_8`::41604161sage: G = DihedralGroup(4)4162sage: H = CyclicPermutationGroup(4)4163sage: gens = H.gens()4164sage: S = PermutationGroup_subgroup(G, list(gens))4165sage: S4166Subgroup of (Dihedral group of order 8 as a permutation group) generated by [(1,2,3,4)]4167sage: S._repr_()4168'Subgroup of (Dihedral group of order 8 as a permutation group) generated by [(1,2,3,4)]'4169"""4170s = "Subgroup of (%s) generated by %s"%(self.ambient_group(), self.gens())4171return s41724173def _latex_(self):4174r"""4175Return LaTeX representation of this group.41764177EXAMPLES:41784179An example involving the dihedral group on four elements,4180`D_8`::41814182sage: G = DihedralGroup(4)4183sage: H = CyclicPermutationGroup(4)4184sage: gens = H.gens()4185sage: S = PermutationGroup_subgroup(G, list(gens))4186sage: latex(S)4187\hbox{Subgroup } \langle (1,2,3,4) \rangle \hbox{ of } \langle (1,2,3,4), (1,4)(2,3) \rangle4188sage: S._latex_()4189'\\hbox{Subgroup } \\langle (1,2,3,4) \\rangle \\hbox{ of } \\langle (1,2,3,4), (1,4)(2,3) \\rangle'4190"""4191gens = '\\langle ' + \4192', '.join([x._latex_() for x in self.gens()]) + ' \\rangle'4193return '\\hbox{Subgroup } %s \\hbox{ of } %s' % \4194(gens, self.ambient_group()._latex_())41954196def ambient_group(self):4197"""4198Return the ambient group related to ``self``.41994200EXAMPLES:42014202An example involving the dihedral group on four elements,4203`D_8`::42044205sage: G = DihedralGroup(4)4206sage: H = CyclicPermutationGroup(4)4207sage: gens = H.gens()4208sage: S = PermutationGroup_subgroup(G, list(gens))4209sage: S.ambient_group()4210Dihedral group of order 8 as a permutation group4211sage: S.ambient_group() == G4212True4213"""4214return self._ambient_group421542164217def is_normal(self, other=None):4218"""4219Return ``True`` if this group is a normal subgroup of4220``other``. If ``other`` is not specified, then it is assumed4221to be the ambient group.42224223EXAMPLES::42244225sage: S = SymmetricGroup(['a','b','c'])4226sage: H = S.subgroup([('a', 'b', 'c')]); H4227Subgroup of (Symmetric group of order 3! as a permutation group) generated by [('a','b','c')]4228sage: H.is_normal()4229True42304231"""4232if other is None:4233other = self.ambient_group()4234return PermutationGroup_generic.is_normal(self, other)4235423642374238