Path: blob/master/sage/groups/perm_gps/permgroup_element.pyx
4058 views
"""1Permutation group elements23AUTHORS:45- David Joyner (2006-02)67- David Joyner (2006-03): word problem method and reorganization89- Robert Bradshaw (2007-11): convert to Cython1011EXAMPLES: The Rubik's cube group::1213sage: f= [(17,19,24,22),(18,21,23,20),(6,25,43,16),(7,28,42,13),(8,30,41,11)]14sage: b=[(33,35,40,38),(34,37,39,36),( 3, 9,46,32),( 2,12,47,29),( 1,14,48,27)]15sage: l=[( 9,11,16,14),(10,13,15,12),( 1,17,41,40),( 4,20,44,37),( 6,22,46,35)]16sage: r=[(25,27,32,30),(26,29,31,28),( 3,38,43,19),( 5,36,45,21),( 8,33,48,24)]17sage: u=[( 1, 3, 8, 6),( 2, 5, 7, 4),( 9,33,25,17),(10,34,26,18),(11,35,27,19)]18sage: d=[(41,43,48,46),(42,45,47,44),(14,22,30,38),(15,23,31,39),(16,24,32,40)]19sage: cube = PermutationGroup([f,b,l,r,u,d])20sage: F=cube.gens()[0]21sage: B=cube.gens()[1]22sage: L=cube.gens()[2]23sage: R=cube.gens()[3]24sage: U=cube.gens()[4]25sage: D=cube.gens()[5]26sage: cube.order()274325200327448985600028sage: F.order()2943031The interested user may wish to explore the following commands:32move = cube.random_element() and time word_problem([F,B,L,R,U,D],33move, False). This typically takes about 5 minutes (on a 2 Ghz34machine) and outputs a word ('solving' the cube in the position35move) with about 60 terms or so.3637OTHER EXAMPLES: We create element of a permutation group of large38degree.3940::4142sage: G = SymmetricGroup(30)43sage: s = G(srange(30,0,-1)); s44(1,30)(2,29)(3,28)(4,27)(5,26)(6,25)(7,24)(8,23)(9,22)(10,21)(11,20)(12,19)(13,18)(14,17)(15,16)45"""4647###########################################################################48# Copyright (C) 2006 William Stein <[email protected]>49# Copyright (C) 2006 David Joyner50#51# Distributed under the terms of the GNU General Public License (GPL)52# http://www.gnu.org/licenses/53###########################################################################545556import random5758import sage.groups.group as group5960include "../../ext/stdsage.pxi"61include "../../ext/interrupt.pxi"62include "../../ext/python_list.pxi"6364from sage.rings.all import ZZ, Integer, is_MPolynomial, is_Polynomial65from sage.matrix.all import MatrixSpace66from sage.interfaces.all import gap, is_GapElement, is_ExpectElement67from sage.sets.finite_enumerated_set import FiniteEnumeratedSet68import sage.structure.coerce as coerce6970import operator7172from sage.rings.fast_arith cimport arith_llong73cdef arith_llong arith = arith_llong()74cdef extern from *:75long long LONG_LONG_MAX7677#import permgroup_named7879def make_permgroup_element(G, x):80"""81Returns a PermutationGroupElement given the permutation group82``G`` and the permutation ``x`` in list notation.8384This is function is used when unpickling old (pre-domain) versions85of permutation groups and their elements. This now does a bit of86processing and calls :func:`make_permgroup_element_v2` which is87used in unpickling the current PermutationGroupElements.8889EXAMPLES::9091sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element92sage: S = SymmetricGroup(3)93sage: make_permgroup_element(S, [1,3,2])94(2,3)95"""96domain = FiniteEnumeratedSet(range(1, len(x)+1))97return make_permgroup_element_v2(G, x, domain)9899def make_permgroup_element_v2(G, x, domain):100"""101Returns a PermutationGroupElement given the permutation group102``G``, the permutation ``x`` in list notation, and the domain103``domain`` of the permutation group.104105This is function is used when unpickling permutation groups and106their elements.107108EXAMPLES::109110sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element_v2111sage: S = SymmetricGroup(3)112sage: make_permgroup_element_v2(S, [1,3,2], S.domain())113(2,3)114"""115# Note that it has to be in-sync with the __init__ method of116# PermutationGroup_generic since the elements have to be created117# before the PermutationGroup_generic is initialized. The118# constructor for PermutationGroupElement requires that119# G._domain_to_gap be set.120G._domain = domain121G._deg = len(domain)122G._domain_to_gap = dict([(key, i+1) for i, key in enumerate(domain)])123G._domain_from_gap = dict([(i+1, key) for i, key in enumerate(domain)])124return G(x, check=False)125126127def is_PermutationGroupElement(x):128"""129Returns True if ``x`` is a PermutationGroupElement.130131EXAMPLES::132133sage: p = PermutationGroupElement([(1,2),(3,4,5)])134sage: from sage.groups.perm_gps.permgroup_element import is_PermutationGroupElement135sage: is_PermutationGroupElement(p)136True137"""138return isinstance(x, PermutationGroupElement)139140def string_to_tuples(g):141"""142EXAMPLES::143144sage: from sage.groups.perm_gps.permgroup_element import string_to_tuples145sage: string_to_tuples('(1,2,3)')146[(1, 2, 3)]147sage: string_to_tuples('(1,2,3)(4,5)')148[(1, 2, 3), (4, 5)]149sage: string_to_tuples(' (1,2, 3) (4,5)')150[(1, 2, 3), (4, 5)]151sage: string_to_tuples('(1,2)(3)')152[(1, 2), (3,)]153"""154from sage.misc.all import sage_eval155156if not isinstance(g, str):157raise ValueError, "g (= %s) must be a string"%g158elif g == '()':159return []160g = g.replace('\n','').replace(' ', '').replace(')(', '),(').replace(')', ',)')161g = '[' + g + ']'162return sage_eval(g, preparse=False)163164def standardize_generator(g, convert_dict=None):165"""166Standardizes the input for permutation group elements to a list of167tuples. This was factored out of the168PermutationGroupElement.__init__ since169PermutationGroup_generic.__init__ needs to do the same computation170in order to compute the domain of a group when it's not explicitly171specified.172173INPUT:174175- ``g`` - a list, tuple, string, GapElement,176PermuationGroupElement, Permutation177178- ``convert_dict`` - (optional) a dictionary used to convert the179points to a number compatible with GAP.180181OUTPUT:182183The permutation in as a list of cycles.184185EXAMPLES::186187sage: from sage.groups.perm_gps.permgroup_element import standardize_generator188sage: standardize_generator('(1,2)')189[(1, 2)]190191sage: p = PermutationGroupElement([(1,2)])192sage: standardize_generator(p)193[(1, 2)]194sage: standardize_generator(p._gap_())195[(1, 2)]196sage: standardize_generator((1,2))197[(1, 2)]198sage: standardize_generator([(1,2)])199[(1, 2)]200sage: standardize_generator(Permutation([2,1,3]))201[(1, 2), (3,)]202203::204205sage: d = {'a': 1, 'b': 2}206sage: p = SymmetricGroup(['a', 'b']).gen(0); p207('a','b')208sage: standardize_generator(p, convert_dict=d)209[(1, 2)]210sage: standardize_generator(p._gap_(), convert_dict=d)211[(1, 2)]212sage: standardize_generator(('a','b'), convert_dict=d)213[(1, 2)]214sage: standardize_generator([('a','b')], convert_dict=d)215[(1, 2)]216"""217from sage.interfaces.gap import GapElement218from sage.combinat.permutation import Permutation219from sage.libs.pari.gen import gen220from sage.combinat.permutation import Permutation_class221222if isinstance(g, gen):223g = list(g)224225needs_conversion = True226if isinstance(g, GapElement):227g = str(g)228needs_conversion = False229if isinstance(g, Permutation_class):230return g.cycle_tuples()231if isinstance(g, PermutationGroupElement):232g = g.cycle_tuples()233if isinstance(g, str):234g = string_to_tuples(g)235if isinstance(g, tuple) and (len(g) == 0 or not isinstance(g[0], tuple)):236g = [g]237238#Get the permutation in list notation239if PyList_CheckExact(g) and (len(g) == 0 or not PY_TYPE_CHECK(g[0], tuple)):240if convert_dict is not None and needs_conversion:241g = [convert_dict[x] for x in g]242243244#FIXME: Currently, we need to verify that g defines an actual245#permutation since the constructor Permutation does not246#perform this check. When it does, we should remove this code.247#See #8392248if set(g) != set(range(1, len(g)+1)):249raise ValueError, "Invalid permutation vector: %s"%g250return Permutation(g).cycle_tuples()251else:252if convert_dict is not None and needs_conversion:253g = [tuple([convert_dict[x] for x in cycle])for cycle in g]254255return g256257cdef class PermutationGroupElement(MultiplicativeGroupElement):258"""259An element of a permutation group.260261EXAMPLES::262263sage: G = PermutationGroup(['(1,2,3)(4,5)'])264sage: G265Permutation Group with generators [(1,2,3)(4,5)]266sage: g = G.random_element()267sage: g in G268True269sage: g = G.gen(0); g270(1,2,3)(4,5)271sage: print g272(1,2,3)(4,5)273sage: g*g274(1,3,2)275sage: g**(-1)276(1,3,2)(4,5)277sage: g**2278(1,3,2)279sage: G = PermutationGroup([(1,2,3)])280sage: g = G.gen(0); g281(1,2,3)282sage: g.order()2833284285This example illustrates how permutations act on multivariate286polynomials.287288::289290sage: R = PolynomialRing(RationalField(), 5, ["x","y","z","u","v"])291sage: x, y, z, u, v = R.gens()292sage: f = x**2 - y**2 + 3*z**2293sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])294sage: sigma = G.gen(0)295sage: f * sigma2963*x^2 + y^2 - z^2297"""298def __init__(self, g, parent = None, check = True):299r"""300Create element of a permutation group.301302There are several ways to define a permutation group element:303304305- Define a permutation group `G`, then use306``G.gens()`` and multiplication \* to construct307elements.308309- Define a permutation group `G`, then use e.g.,310``G([(1,2),(3,4,5)])`` to construct an element of the311group. You could also use ``G('(1,2)(3,4,5)')``312313- Use e.g.,314``PermutationGroupElement([(1,2),(3,4,5)])`` or315``PermutationGroupElement('(1,2)(3,4,5)')`` to make a316permutation group element with parent `S_5`.317318319INPUT:320321322- ``g`` - defines element323324- ``parent (optional)`` - defines parent group (g must325be in parent if specified, or a TypeError is raised).326327- ``check`` - bool (default: True), if False assumes g328is a gap element in parent (if specified).329330331EXAMPLES: We illustrate construction of permutation using several332different methods.333334First we construct elements by multiplying together generators for335a group.336337::338339sage: G = PermutationGroup(['(1,2)(3,4)', '(3,4,5,6)'], canonicalize=False)340sage: s = G.gens()341sage: s[0]342(1,2)(3,4)343sage: s[1]344(3,4,5,6)345sage: s[0]*s[1]346(1,2)(3,5,6)347sage: (s[0]*s[1]).parent()348Permutation Group with generators [(1,2)(3,4), (3,4,5,6)]349350Next we illustrate creation of a permutation using coercion into an351already-created group.352353::354355sage: g = G([(1,2),(3,5,6)])356sage: g357(1,2)(3,5,6)358sage: g.parent()359Permutation Group with generators [(1,2)(3,4), (3,4,5,6)]360sage: g == s[0]*s[1]361True362363We can also use a string instead of a list to specify the364permutation.365366::367368sage: h = G('(1,2)(3,5,6)')369sage: g == h370True371372We can also make a permutation group element directly using the373``PermutationGroupElement`` command. Note that the374parent is then the full symmetric group `S_n`, where375`n` is the largest integer that is moved by the376permutation.377378::379380sage: k = PermutationGroupElement('(1,2)(3,5,6)')381sage: k382(1,2)(3,5,6)383sage: k.parent()384Symmetric group of order 6! as a permutation group385386Note the comparison of permutations doesn't require that the parent387groups are the same.388389::390391sage: k == g392True393394Arithmetic with permutations having different parents is also395defined::396397sage: k*g398(3,6,5)399sage: (k*g).parent()400Symmetric group of order 6! as a permutation group401402::403404sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])405sage: loads(dumps(G.0)) == G.0406True407408EXAMPLES::409410sage: k = PermutationGroupElement('(1,2)(3,5,6)')411sage: k._gap_()412(1,2)(3,5,6)413sage: k._gap_().parent()414Gap415416List notation::417418sage: PermutationGroupElement([1,2,4,3,5])419(3,4)420421TESTS::422423sage: PermutationGroupElement(())424()425sage: PermutationGroupElement([()])426()427"""428from sage.groups.perm_gps.permgroup_named import SymmetricGroup429from sage.groups.perm_gps.permgroup import PermutationGroup_generic430from sage.combinat.permutation import from_cycles431432convert_dict = parent._domain_to_gap if parent is not None else None433try:434v = standardize_generator(g, convert_dict)435except KeyError:436raise ValueError, "Invalid permutation vector: %s" % g437438439degree = max([1] + [max(cycle+(1,)) for cycle in v])440v = from_cycles(degree, v)441442self.__gap = 'PermList(%s)'%v443444if parent is None:445parent = SymmetricGroup(len(v))446447if check and parent.__class__ != SymmetricGroup:448if not (parent is None or isinstance(parent, PermutationGroup_generic)):449raise TypeError, 'parent must be a permutation group'450if parent is not None:451P = parent._gap_()452if not P.parent()(self.__gap) in P:453raise TypeError, 'permutation %s not in %s'%(g, parent)454455Element.__init__(self, parent)456457self.n = max(parent.degree(), 1)458459if self.perm is NULL or self.perm is self.perm_buf:460self.perm = <int *>sage_malloc(sizeof(int) * self.n)461else:462self.perm = <int *>sage_realloc(self.perm, sizeof(int) * self.n)463464465cdef int i, vn = len(v)466assert(vn <= self.n)467for i from 0 <= i < vn:468self.perm[i] = v[i] - 1469for i from vn <= i < self.n:470self.perm[i] = i471472# We do this check even if check=False because it's fast473# (relative to other things in this function) and the474# rest of the code is assumes that self.perm specifies475# a valid permutation (else segfaults, infinite loops may occur).476if not is_valid_permutation(self.perm, vn):477raise ValueError, "Invalid permutation vector: %s" % v478479def __cinit__(self, g = None, parent = None, check = True):480self.perm = NULL481482def __dealloc__(self):483if self.perm is not NULL and self.perm is not self.perm_buf:484sage_free(self.perm)485486def __reduce__(self):487"""488Returns a function and its arguments needed to create this489permutation group element. This is used in pickling.490491EXAMPLES::492493sage: g = PermutationGroupElement([(1,2,3),(4,5)]); g494(1,2,3)(4,5)495sage: func, args = g.__reduce__()496sage: func(*args)497(1,2,3)(4,5)498"""499return make_permgroup_element_v2, (self._parent, self.list(), self._parent.domain())500501cdef PermutationGroupElement _new_c(self):502cdef PermutationGroupElement other = PY_NEW_SAME_TYPE(self)503if HAS_DICTIONARY(self):504other.__class__ = self.__class__505other._parent = self._parent506other.n = self.n507if other.n <= sizeof(other.perm_buf) / sizeof(int):508other.perm = other.perm_buf509else:510other.perm = <int *>sage_malloc(sizeof(int) * other.n)511return other512513def _gap_(self, gap=None):514"""515Returns516517EXAMPLES::518519sage: g = PermutationGroupElement([(1,2,3),(4,5)]); g520(1,2,3)(4,5)521sage: a = g._gap_(); a522(1,2,3)(4,5)523sage: g._gap_() is g._gap_()524True525526Note that only one GapElement is cached:527528sage: gap2 = Gap()529sage: b = g._gap_(gap2)530sage: c = g._gap_()531sage: a is c532False533"""534if (self._gap_element is None or535(gap is not None and self._gap_element._parent is not gap)):536if gap is None:537from sage.interfaces.gap import gap538self._gap_element = gap(self._gap_init_())539return self._gap_element540541def _gap_init_(self):542"""543Returns a GAP string representation for this544PermutationGroupElement.545546EXAMPLES::547548sage: g = PermutationGroupElement([(1,2,3),(4,5)])549sage: g._gap_init_()550'PermList([2, 3, 1, 5, 4])'551"""552return 'PermList(%s)'%self._gap_list()553554def _repr_(self):555"""556Return string representation of this permutation.557558EXAMPLES:559560We create the permutation `(1,2,3)(4,5)` and561print it. ::562563sage: g = PermutationGroupElement([(1,2,3),(4,5)])564sage: g._repr_()565'(1,2,3)(4,5)'566567Permutation group elements support renaming them so they print568however you want, as illustrate below::569570sage: g.rename('sigma')571sage: g572sigma573sage: g.rename()574sage: g575(1,2,3)(4,5)576"""577return self.cycle_string()578579def _latex_(self):580r"""581Returns a latex representation of this permutation.582583EXAMPLES::584585sage: g = PermutationGroupElement([(1,2,3),(4,5)])586sage: latex(g)587(1,2,3)(4,5)588589sage: S = SymmetricGroup(['a', 'b'])590sage: latex(S.gens())591\left[(\verb|a|,\verb|b|)\right]592"""593from sage.misc.latex import latex594return "".join(["(" + ",".join([latex(x) for x in cycle])+")" for cycle in self.cycle_tuples()])595596def __getitem__(self, i):597"""598Return the ith permutation cycle in the disjoint cycle599representation of self.600601INPUT:602603604- ``i`` - integer605606607OUTPUT: a permutation group element608609EXAMPLE::610611sage: G = PermutationGroup([[(1,2,3),(4,5)]],5)612sage: g = G.gen(0)613sage: g[0]614(1,2,3)615sage: g[1]616(4,5)617"""618return self.cycles()[i]619620def __cmp__(PermutationGroupElement self, PermutationGroupElement right):621"""622Compare group elements self and right.623624EXAMPLES::625626sage: G = PermutationGroup([[(3,4)], [(1,2,3),(4,5)]])627sage: G.gen(0) != G.gen(1)628True629sage: G.gen(0) == G.gen(1)630False631632Permutations are ordered left lexicographically on their633associated 'lists'; thus in the symmetric group S(5), the634permutation (1,2)(3,4), which corresponds to the list635[2,1,4,3,5], is larger than (1,2), which corresponds to the636list [2,1,3,4,5].637638::639640sage: S = SymmetricGroup(5)641sage: S("(1,2)(3,4)") < S("(1,2)")642False643sage: S("(1,2)(3,4)") > S("(1,2)")644True645646TESTS:647648Verify that we fixed bug #5537::649650sage: h = PermutationGroupElement('(1,3,2)')651sage: k = PermutationGroupElement('(1,2,3),(4,5)')652sage: k^2 == h, h == k^2653(True, True)654sage: k^6 == PermutationGroupElement('()')655True656"""657cdef int i658cdef int swap = 1659if right.n < self.n:660self, right = right, self661swap = -1662for i from 0 <= i < self.n:663if self.perm[i] < right.perm[i]:664return -swap665elif self.perm[i] > right.perm[i]:666return swap667for i from self.n <= i < right.n:668if i < right.perm[i]:669return -swap670elif i > right.perm[i]:671return swap672return 0673674def __call__(self, i):675"""676Returns the image of the integer i under this permutation.677Alternately, if i is a list, tuple or string, returns the result of678self acting on i.679680EXAMPLE::681682sage: G = PermutationGroup(['(1,2,3)(4,5)'])683sage: G684Permutation Group with generators [(1,2,3)(4,5)]685sage: g = G.gen(0)686sage: g(5)6874688sage: g('abcde')689'bcaed'690sage: g([0,1,2,3,4])691[1, 2, 0, 4, 3]692sage: g(('who','what','when','where','why'))693('what', 'when', 'who', 'why', 'where')694695::696697sage: g(x)698Traceback (most recent call last):699...700ValueError: Must be in the domain or a list, tuple or string.701sage: g(3/2)702Traceback (most recent call last):703...704ValueError: Must be in the domain or a list, tuple or string.705"""706to_gap = self._parent._domain_to_gap707from_gap = self._parent._domain_from_gap708cdef int j709710try:711i = to_gap[i]712except (KeyError, TypeError):713# We currently have to include this to maintain the714# current behavior where if you pass in an integer which715# is not in the domain of the permutation group, then that716# integer itself will be returned.717if isinstance(i, (long, int, Integer)):718return i719720721if not isinstance(i,(list,tuple,str)):722raise ValueError, "Must be in the domain or a list, tuple or string."723724permuted = [i[self.perm[j]] for j from 0 <= j < self.n]725if PY_TYPE_CHECK(i, tuple):726permuted = tuple(permuted)727elif PY_TYPE_CHECK(i, str):728permuted = ''.join(permuted)729permuted += i[self.n:]730return permuted731else:732j = i733if 1 <= j <= self.n:734return from_gap[self.perm[j-1]+1]735else:736return from_gap[i]737738cpdef list _act_on_list_on_position(self, list x):739"""740Returns the right action of ``self`` on the list ``x``. This is the741action on positions.742743EXAMPLES::744745sage: G = PermutationGroup([[(1,2,3,4,5,6)]])746sage: p = G.gen()^2; p747(1,3,5)(2,4,6)748sage: p._act_on_list_on_position([1,2,3,4,5,6])749[3, 4, 5, 6, 1, 2]750sage: p._act_on_list_on_position(['a','b','c','d','e','f'])751['c', 'd', 'e', 'f', 'a', 'b']752sage: p._act_on_list_on_position(['c','d','e','f','a','b'])753['e', 'f', 'a', 'b', 'c', 'd']754sage: p._act_on_list_on_position([])755Traceback (most recent call last):756...757AssertionError: (1,3,5)(2,4,6) and [] should have the same length758sage: p._act_on_list_on_position([1,2,3,4,5,6,7])759Traceback (most recent call last):760...761AssertionError: (1,3,5)(2,4,6) and [1, 2, 3, 4, 5, 6, 7] should have the same length762"""763assert len(x) == self.n, '%s and %s should have the same length'%(self, x)764return [ x[self.perm[i]] for i in range(self.n) ]765766cpdef ClonableIntArray _act_on_array_on_position(self, ClonableIntArray x):767"""768Returns the right action of ``self`` on the ClonableIntArray769``x``. This is the action on positions.770771EXAMPLES::772773sage: from sage.structure.list_clone import IncreasingIntArrays774sage: v = IncreasingIntArrays()([1,2,3,4])775sage: G = PermutationGroup([[(1,2,3,4)]])776sage: id = G.identity()777sage: id._act_on_array_on_position(v)778[1, 2, 3, 4]779"""780cdef int i781cdef ClonableIntArray y782cdef int l = self.n783assert x._len == l, '%s and %s should have the same length'%(self, x)784y = x.clone()785for i in range(l):786y._list[i] = x._list[self.perm[i]]787y.set_immutable()788return y789790cpdef _act_on_(self, x, bint self_on_left):791"""792Return the right action of self on left.793794For example, if f=left is a polynomial, then this function returns795f(sigma\*x), which is image of f under the right action of sigma on796the indeterminates. This is a right action since the image of797f(sigma\*x) under tau is f(sigma\*tau\*x).798799INPUT:800801802- ``left`` - element of space on which permutations803act from the right804805806EXAMPLES::807808sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])809sage: R.<x,y,z,u,v> = PolynomialRing(QQ,5)810sage: f = x^2 + y^2 - z^2 + 2*u^2811sage: sigma, tau = G.gens()812sage: f*sigma813-x^2 + y^2 + z^2 + 2*v^2814sage: f*tau815y^2 + z^2 - u^2 + 2*v^2816sage: f*(sigma*tau)8172*x^2 - y^2 + z^2 + u^2818sage: (f*sigma)*tau8192*x^2 - y^2 + z^2 + u^2820"""821if not self_on_left:822left = x823if is_Polynomial(left):824if self != 1:825raise ValueError, "%s does not act on %s"%(self, left.parent())826return left827elif is_MPolynomial(left):828R = left.parent()829vars = R.gens()830try:831sigma_x = [vars[self(i+1)-1] for i in range(R.ngens())]832except IndexError:833raise TypeError, "%s does not act on %s"%(self, left.parent())834return left(tuple(sigma_x))835836cpdef MonoidElement _mul_(left, MonoidElement _right):837"""838EXAMPLES::839840sage: S = SymmetricGroup(['a', 'b'])841sage: s = S([('a', 'b')]); s842('a','b')843sage: s*s844()845"""846cdef PermutationGroupElement prod = left._new_c()847cdef PermutationGroupElement right = <PermutationGroupElement>_right848cdef int i849for i from 0 <= i < left.n:850prod.perm[i] = right.perm[left.perm[i]]851return prod852853def __invert__(self):854"""855Return the inverse of this permutation.856857EXAMPLES::858859sage: g = PermutationGroupElement('(1,2,3)(4,5)')860sage: ~g861(1,3,2)(4,5)862sage: (~g) * g863()864"""865cdef PermutationGroupElement inv = self._new_c()866cdef int i867for i from 0 <= i < self.n:868inv.perm[self.perm[i]] = i869return inv870871cpdef _gap_list(self):872"""873Returns this permutation in list notation compatible with the874GAP numbering.875876EXAMPLES::877878sage: S = SymmetricGroup(3)879sage: s = S.gen(0); s880(1,2,3)881sage: s._gap_list()882[2, 3, 1]883884::885886sage: S = SymmetricGroup(['a', 'b', 'c'])887sage: s = S.gen(0); s888('a','b','c')889sage: s._gap_list()890[2, 3, 1]891"""892cdef int i893return [self.perm[i]+1 for i from 0 <= i < self.n]894895def _gap_cycle_string(self):896"""897Returns a cycle string for this permutation compatible with898the GAP numbering.899900EXAMPLES::901902sage: S = SymmetricGroup(3)903sage: s = S.gen(0); s904(1,2,3)905sage: s._gap_cycle_string()906'(1,2,3)'907908::909910sage: S = SymmetricGroup(['a', 'b', 'c'])911sage: s = S.gen(0); s912('a','b','c')913sage: s._gap_cycle_string()914'(1,2,3)'915"""916from sage.combinat.permutation import Permutation917return Permutation(self._gap_list()).cycle_string()918919cpdef list(self):920"""921Returns list of the images of the integers from 1 to n under this922permutation as a list of Python ints.923924EXAMPLES::925926sage: G = SymmetricGroup(4)927sage: x = G([2,1,4,3]); x928(1,2)(3,4)929sage: v = x.list(); v930[2, 1, 4, 3]931sage: type(v[0])932<type 'int'>933sage: x = G([2,1]); x934(1,2)935sage: x.list()936[2, 1, 3, 4]937938TESTS::939940sage: S = SymmetricGroup(0)941sage: x = S.one(); x942()943sage: x.list()944[]945"""946cdef int i947948#We need to do this to handle the case of SymmetricGroup(0)949#where the domain is (), but the permutation group element has950#an underlying representation of [1]. The 1 doesn't951#correspond to anything in the domain952if len(self._parent._domain) == 0:953return []954else:955from_gap = self._parent._domain_from_gap956return [from_gap[self.perm[i]+1] for i from 0 <= i < self.n]957958def __hash__(self):959"""960Return hash of this permutation.961962EXAMPLES::963964sage: G = SymmetricGroup(5)965sage: s = G([2,1,5,3,4])966sage: s.tuple()967(2, 1, 5, 3, 4)968sage: hash(s)9691592966088 # 32-bit9702865702456085625800 # 64-bit971sage: hash(s.tuple())9721592966088 # 32-bit9732865702456085625800 # 64-bit974"""975return hash(tuple(self._gap_list()))976977def tuple(self):978"""979Return tuple of images of the domain under self.980981EXAMPLES::982983sage: G = SymmetricGroup(5)984sage: s = G([2,1,5,3,4])985sage: s.tuple()986(2, 1, 5, 3, 4)987988sage: S = SymmetricGroup(['a', 'b'])989sage: S.gen().tuple()990('b', 'a')991"""992if self.__tuple is None:993self.__tuple = tuple(self.list())994return self.__tuple995996def dict(self):997"""998Returns list of the images of the integers from 1 to n under this999permutation as a list of Python ints.10001001.. note::10021003:meth:`.list` returns a zero-indexed list. :meth:`.dict`1004return the actual mapping of the permutation, which will be1005indexed starting with 1.10061007EXAMPLES::10081009sage: G = SymmetricGroup(4)1010sage: g = G((1,2,3,4)); g1011(1,2,3,4)1012sage: v = g.dict(); v1013{1: 2, 2: 3, 3: 4, 4: 1}1014sage: type(v[1])1015<type 'int'>1016sage: x = G([2,1]); x1017(1,2)1018sage: x.dict()1019{1: 2, 2: 1, 3: 3, 4: 4}1020"""1021from_gap = self._parent._domain_from_gap1022cdef int i1023u = {}1024for i from 0 <= i < self.n:1025u[i+1] = from_gap[self.perm[i]+1]1026return u10271028def order(self):1029"""1030Return the order of this group element, which is the smallest1031positive integer `n` for which `g^n = 1`.10321033EXAMPLES::10341035sage: s = PermutationGroupElement('(1,2)(3,5,6)')1036sage: s.order()1037610381039TESTS::10401041sage: prod(primes(150))104214921823509392793200588757366158410685475838633268645304101043sage: L = [tuple(range(sum(primes(p))+1, sum(primes(p))+1+p)) for p in primes(150)]1044sage: PermutationGroupElement(L).order()104514921823509392793200588757366158410685475838633268645304101046"""1047order = None1048cdef long long order_c = 11049cdef int cycle_len1050cdef int i, k1051cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1052for i from 0 <= i < self.n: seen[i] = 01053for i from 0 <= i < self.n:1054if seen[i] or self.perm[i] == i:1055continue1056k = self.perm[i]1057cycle_len = 11058while k != i:1059seen[k] = 11060k = self.perm[k]1061cycle_len += 11062if order is not None:1063order = order.lcm(cycle_len)1064else:1065order_c = (order_c * cycle_len) / arith.c_gcd_longlong(order_c, cycle_len)1066if order_c > LONG_LONG_MAX / (self.n - i):1067order = Integer(order_c)1068sage_free(seen)1069return int(order_c) if order is None else order10701071def inverse(self):1072r"""1073Returns the inverse permutation.10741075OUTPUT:10761077For an element of a permutation group, this method returns the inverse1078element, which is both the inverse function and the inverse as an1079element of a group.10801081EXAMPLES::10821083sage: s = PermutationGroupElement("(1,2,3)(4,5)")1084sage: s.inverse()1085(1,3,2)(4,5)10861087sage: A = AlternatingGroup(4)1088sage: t = A("(1,2,3)")1089sage: t.inverse()1090(1,3,2)10911092There are several ways (syntactically) to get an inverse1093of a permutation group element. ::10941095sage: s = PermutationGroupElement("(1,2,3,4)(6,7,8)")1096sage: s.inverse() == s^-11097True1098sage: s.inverse() == ~s1099True1100"""1101return ~self11021103def sign(self):1104"""1105Returns the sign of self, which is `(-1)^{s}`, where1106`s` is the number of swaps.11071108EXAMPLES::11091110sage: s = PermutationGroupElement('(1,2)(3,5,6)')1111sage: s.sign()1112-111131114ALGORITHM: Only even cycles contribute to the sign, thus11151116.. math::11171118sign(sigma) = (-1)^{\sum_c len(c)-1}111911201121where the sum is over cycles in self.1122"""1123cdef int cycle_len_sum = 01124cdef int i, k1125cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1126for i from 0 <= i < self.n: seen[i] = 01127for i from 0 <= i < self.n:1128if seen[i] or self.perm[i] == i:1129continue1130k = self.perm[i]1131while k != i:1132seen[k] = 11133k = self.perm[k]1134cycle_len_sum += 11135sage_free(seen)1136return 1 - 2*(cycle_len_sum % 2) # == (-1)^cycle_len113711381139def orbit(self, n, bint sorted=True):1140"""1141Returns the orbit of the integer `n` under this group1142element, as a sorted list.11431144EXAMPLES::11451146sage: G = PermutationGroup(['(1,2,3)(4,5)'])1147sage: g = G.gen(0)1148sage: g.orbit(4)1149[4, 5]1150sage: g.orbit(3)1151[1, 2, 3]1152sage: g.orbit(10)1153[10]11541155::11561157sage: s = SymmetricGroup(['a', 'b']).gen(0); s1158('a','b')1159sage: s.orbit('a')1160['a', 'b']1161"""1162to_gap = self._parent._domain_to_gap1163from_gap = self._parent._domain_from_gap1164try:1165n = to_gap[n]1166except KeyError:1167return [n]11681169cdef int i = n1170cdef int start = i1171if 1 <= i <= self.n:1172L = [from_gap[i]]1173i = self.perm[i-1]+11174while i != start:1175PyList_Append(L,from_gap[i])1176i = self.perm[i-1]+11177if sorted:1178L.sort()1179return L1180else:1181return from_gap[n]11821183def cycles(self):1184"""1185Return self as a list of disjoint cycles.11861187EXAMPLES::11881189sage: G = PermutationGroup(['(1,2,3)(4,5,6,7)'])1190sage: g = G.01191sage: g.cycles()1192[(1,2,3), (4,5,6,7)]1193sage: a, b = g.cycles()1194sage: a(1), b(1)1195(2, 1)1196"""1197L = []1198cdef PermutationGroupElement cycle1199cdef int i, j, k, next_k1200cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1201for i from 0 <= i < self.n: seen[i] = 01202for i from 0 <= i < self.n:1203if seen[i] or self.perm[i] == i:1204continue1205cycle = self._new_c()1206for j from 0 <= j < self.n: cycle.perm[j] = j1207k = cycle.perm[i] = self.perm[i]1208while k != i:1209seen[k] = 11210next_k = cycle.perm[k] = self.perm[k]1211k = next_k1212PyList_Append(L, cycle)1213sage_free(seen)1214return L12151216def cycle_tuples(self, singletons=False):1217"""1218Return self as a list of disjoint cycles, represented as tuples1219rather than permutation group elements.12201221INPUT:12221223- ``singletons`` - boolean (default: False) whether or not consider the1224cycle that correspond to fixed point12251226EXAMPLES::12271228sage: p = PermutationGroupElement('(2,6)(4,5,1)')1229sage: p.cycle_tuples()1230[(1, 4, 5), (2, 6)]1231sage: p.cycle_tuples(singletons=True)1232[(1, 4, 5), (2, 6), (3,)]12331234EXAMPLES::12351236sage: S = SymmetricGroup(4)1237sage: S.gen(0).cycle_tuples()1238[(1, 2, 3, 4)]12391240::12411242sage: S = SymmetricGroup(['a','b','c','d'])1243sage: S.gen(0).cycle_tuples()1244[('a', 'b', 'c', 'd')]1245sage: S([('a', 'b'), ('c', 'd')]).cycle_tuples()1246[('a', 'b'), ('c', 'd')]1247"""1248from_gap = self._parent._domain_from_gap1249L = []1250cdef int i, k1251cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1252for i from 0 <= i < self.n: seen[i] = 01253for i from 0 <= i < self.n:1254if seen[i]:1255continue1256if self.perm[i] == i:1257if singletons:1258PyList_Append(L, (from_gap[i+1],))1259# it is not necessary to put seen[i] to 1 as we will never1260# see i again1261else:1262continue1263else:1264cycle = [from_gap[i+1]]1265k = self.perm[i]1266while k != i:1267PyList_Append(cycle, from_gap[k+1])1268seen[k] = 11269k = self.perm[k]1270PyList_Append(L, tuple(cycle))1271sage_free(seen)1272return L12731274def cycle_string(self, singletons=False):1275"""1276Return string representation of this permutation.12771278EXAMPLES::12791280sage: g = PermutationGroupElement([(1,2,3),(4,5)])1281sage: g.cycle_string()1282'(1,2,3)(4,5)'12831284sage: g = PermutationGroupElement([3,2,1])1285sage: g.cycle_string(singletons=True)1286'(1,3)(2)'1287"""1288cycles = self.cycle_tuples(singletons)1289if len(cycles) == 0:1290return '()'1291return ''.join([repr(c) for c in cycles]).replace(', ',',').replace(',)',')')12921293def has_descent(self, i, side = "right", positive = False):1294"""1295INPUT:12961297- ``i``: an element of the index set1298- ``side``: "left" or "right" (default: "right")1299- ``positive``: a boolean (default: False)13001301Returns whether ``self`` has a left (resp. right) descent at1302position ``i``. If ``positive`` is True, then test for a non1303descent instead.13041305Beware that, since permutations are acting on the right, the1306meaning of descents is the reverse of the usual1307convention. Hence, ``self`` has a left descent at position1308``i`` if ``self(i) > self(i+1)``.13091310EXAMPLES::13111312sage: S = SymmetricGroup([1,2,3])1313sage: S.one().has_descent(1)1314False1315sage: S.one().has_descent(2)1316False1317sage: s = S.simple_reflections()1318sage: x = s[1]*s[2]1319sage: x.has_descent(1, side = "right")1320False1321sage: x.has_descent(2, side = "right")1322True1323sage: x.has_descent(1, side = "left")1324True1325sage: x.has_descent(2, side = "left")1326False1327sage: S._test_has_descent()13281329The symmetric group acting on a set not of the form1330`(1,\dots,n)` is also supported::13311332sage: S = SymmetricGroup([2,4,1])1333sage: s = S.simple_reflections()1334sage: x = s[2]*s[4]1335sage: x.has_descent(4)1336True1337sage: S._test_has_descent()1338"""1339to_gap = self._parent._domain_to_gap1340from_gap = self._parent._domain_from_gap1341if side == "right":1342self = ~self13431344try:1345i1 = from_gap[to_gap[i]+1]1346except KeyError:1347return False13481349return (to_gap[self(i)] > to_gap[self(i1)]) is not positive13501351def matrix(self):1352"""1353Returns deg x deg permutation matrix associated to the permutation1354self13551356EXAMPLES::13571358sage: G = PermutationGroup(['(1,2,3)(4,5)'])1359sage: g = G.gen(0)1360sage: g.matrix()1361[0 1 0 0 0]1362[0 0 1 0 0]1363[1 0 0 0 0]1364[0 0 0 0 1]1365[0 0 0 1 0]1366"""1367M = MatrixSpace(ZZ, self.n, self.n, sparse=True)1368cdef int i1369entries = {}1370for i from 0 <= i < self.n:1371entries[i, self.perm[i]] = 11372return M(entries)13731374def word_problem(self, words, display=True):1375"""1376G and H are permutation groups, g in G, H is a subgroup of G1377generated by a list (words) of elements of G. If g is in H, return1378the expression for g as a word in the elements of (words).13791380This function does not solve the word problem in Sage. Rather it1381pushes it over to GAP, which has optimized algorithms for the word1382problem. Essentially, this function is a wrapper for the GAP1383functions "EpimorphismFromFreeGroup" and1384"PreImagesRepresentative".13851386EXAMPLE::13871388sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]], canonicalize=False)1389sage: g1, g2 = G.gens()1390sage: h = g1^2*g2*g11391sage: h.word_problem([g1,g2], False)1392('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')1393sage: h.word_problem([g1,g2])1394x1^2*x2^-1*x11395[['(1,2,3)(4,5)', 2], ['(3,4)', -1], ['(1,2,3)(4,5)', 1]]1396('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')1397"""1398if not self._parent._has_natural_domain():1399raise NotImplementedError14001401import copy1402from sage.groups.perm_gps.permgroup import PermutationGroup1403from sage.interfaces.all import gap14041405G = gap(words[0].parent())1406g = words[0].parent()(self)1407H = gap.Group(words)1408ans = G.EpimorphismFromFreeGroup().PreImagesRepresentative(g)14091410l1 = str(ans)1411l2 = copy.copy(l1)1412l4 = []1413l3 = l1.split("*")1414for i in range(1,len(words)+1):1415l2 = l2.replace("x"+str(i),str(words[i-1]))14161417if display:1418for i in range(len(l3)): ## parsing the word for display1419if len(l3[i].split("^"))==2:1420l4.append([l3[i].split("^")[0],int(l3[i].split("^")[1])])1421if len(l3[i].split("^"))==1:1422l4.append([l3[i].split("^")[0],1])1423l5 = copy.copy(l4)1424for i in range(len(l4)):1425for j in range(1,len(words)+1):1426l5[i][0] = l5[i][0].replace("x"+str(j),str(words[j-1]))1427print " ",l11428print " ",l51429return l1,l214301431143214331434cdef bint is_valid_permutation(int* perm, int n):1435"""1436This is used in the __init__ method.14371438Returns True iff the first n elements of perm are literally a1439permutation of [0, ..., n-1].14401441TESTS::14421443sage: S = SymmetricGroup(10)1444sage: PermutationGroupElement([2,1],S,check=False)1445(1,2)1446sage: PermutationGroupElement([1,1],S,check=False)1447Traceback (most recent call last):1448...1449ValueError: Invalid permutation vector: [1, 1]1450sage: PermutationGroupElement([1,-1],S,check=False)1451Traceback (most recent call last):1452...1453ValueError: Invalid permutation vector: [1, -1]1454sage: PermutationGroupElement([1,2,3,10],S,check=False)1455Traceback (most recent call last):1456...1457ValueError: Invalid permutation vector: [1, 2, 3, 10]1458"""1459cdef int i, ix1460# make everything is in bounds1461for i from 0 <= i < n:1462if not 0 <= perm[i] < n:1463return False1464# mark hit points by sign1465for i from 0 <= i < n:1466ix = -1-perm[i] if perm[i] < 0 else perm[i]1467perm[ix] = -1-perm[ix]1468# make sure everything is hit once, and reset signs1469for i from 0 <= i < n:1470if perm[i] >= 0:1471return False1472perm[i] = -1-perm[i]14731474return True1475147614771478