Path: blob/master/src/sage/groups/perm_gps/permgroup_element.pyx
8815 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.old as group5960include "sage/ext/stdsage.pxi"61include "sage/ext/interrupt.pxi"62from cpython.list cimport *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 coerce69from sage.misc.superseded import deprecated_function_alias7071import operator7273from sage.rings.fast_arith cimport arith_llong74cdef arith_llong arith = arith_llong()75cdef extern from *:76long long LONG_LONG_MAX7778#import permgroup_named7980def make_permgroup_element(G, x):81"""82Returns a PermutationGroupElement given the permutation group83``G`` and the permutation ``x`` in list notation.8485This is function is used when unpickling old (pre-domain) versions86of permutation groups and their elements. This now does a bit of87processing and calls :func:`make_permgroup_element_v2` which is88used in unpickling the current PermutationGroupElements.8990EXAMPLES::9192sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element93sage: S = SymmetricGroup(3)94sage: make_permgroup_element(S, [1,3,2])95(2,3)96"""97domain = FiniteEnumeratedSet(range(1, len(x)+1))98return make_permgroup_element_v2(G, x, domain)99100def make_permgroup_element_v2(G, x, domain):101"""102Returns a PermutationGroupElement given the permutation group103``G``, the permutation ``x`` in list notation, and the domain104``domain`` of the permutation group.105106This is function is used when unpickling permutation groups and107their elements.108109EXAMPLES::110111sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element_v2112sage: S = SymmetricGroup(3)113sage: make_permgroup_element_v2(S, [1,3,2], S.domain())114(2,3)115"""116# Note that it has to be in-sync with the __init__ method of117# PermutationGroup_generic since the elements have to be created118# before the PermutationGroup_generic is initialized. The119# constructor for PermutationGroupElement requires that120# G._domain_to_gap be set.121G._domain = domain122G._deg = len(domain)123G._domain_to_gap = dict([(key, i+1) for i, key in enumerate(domain)])124G._domain_from_gap = dict([(i+1, key) for i, key in enumerate(domain)])125return G(x, check=False)126127128def is_PermutationGroupElement(x):129"""130Returns True if ``x`` is a PermutationGroupElement.131132EXAMPLES::133134sage: p = PermutationGroupElement([(1,2),(3,4,5)])135sage: from sage.groups.perm_gps.permgroup_element import is_PermutationGroupElement136sage: is_PermutationGroupElement(p)137True138"""139return isinstance(x, PermutationGroupElement)140141def string_to_tuples(g):142"""143EXAMPLES::144145sage: from sage.groups.perm_gps.permgroup_element import string_to_tuples146sage: string_to_tuples('(1,2,3)')147[(1, 2, 3)]148sage: string_to_tuples('(1,2,3)(4,5)')149[(1, 2, 3), (4, 5)]150sage: string_to_tuples(' (1,2, 3) (4,5)')151[(1, 2, 3), (4, 5)]152sage: string_to_tuples('(1,2)(3)')153[(1, 2), (3,)]154"""155from sage.misc.all import sage_eval156157if not isinstance(g, str):158raise ValueError, "g (= %s) must be a string"%g159elif g == '()':160return []161g = g.replace('\n','').replace(' ', '').replace(')(', '),(').replace(')', ',)')162g = '[' + g + ']'163return sage_eval(g, preparse=False)164165def standardize_generator(g, convert_dict=None):166"""167Standardizes the input for permutation group elements to a list of168tuples. This was factored out of the169PermutationGroupElement.__init__ since170PermutationGroup_generic.__init__ needs to do the same computation171in order to compute the domain of a group when it's not explicitly172specified.173174INPUT:175176- ``g`` - a list, tuple, string, GapElement,177PermutationGroupElement, Permutation178179- ``convert_dict`` - (optional) a dictionary used to convert the180points to a number compatible with GAP.181182OUTPUT:183184The permutation in as a list of cycles.185186EXAMPLES::187188sage: from sage.groups.perm_gps.permgroup_element import standardize_generator189sage: standardize_generator('(1,2)')190[(1, 2)]191192sage: p = PermutationGroupElement([(1,2)])193sage: standardize_generator(p)194[(1, 2)]195sage: standardize_generator(p._gap_())196[(1, 2)]197sage: standardize_generator((1,2))198[(1, 2)]199sage: standardize_generator([(1,2)])200[(1, 2)]201sage: standardize_generator(Permutation([2,1,3]))202[(1, 2), (3,)]203204::205206sage: d = {'a': 1, 'b': 2}207sage: p = SymmetricGroup(['a', 'b']).gen(0); p208('a','b')209sage: standardize_generator(p, convert_dict=d)210[(1, 2)]211sage: standardize_generator(p._gap_(), convert_dict=d)212[(1, 2)]213sage: standardize_generator(('a','b'), convert_dict=d)214[(1, 2)]215sage: standardize_generator([('a','b')], convert_dict=d)216[(1, 2)]217"""218from sage.interfaces.gap import GapElement219from sage.combinat.permutation import Permutation220from sage.libs.pari.all import pari_gen221222if isinstance(g, pari_gen):223g = list(g)224225needs_conversion = True226if isinstance(g, GapElement):227g = str(g)228needs_conversion = False229if isinstance(g, Permutation):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.domain(), 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[(\text{\texttt{a}},\text{\texttt{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_demo 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()918919list = deprecated_function_alias(14319, domain)920921cpdef domain(self):922"""923Returns the domain of self.924925EXAMPLES::926927sage: G = SymmetricGroup(4)928sage: x = G([2,1,4,3]); x929(1,2)(3,4)930sage: v = x.domain(); v931[2, 1, 4, 3]932sage: type(v[0])933<type 'int'>934sage: x = G([2,1]); x935(1,2)936sage: x.domain()937[2, 1, 3, 4]938939TESTS::940941sage: S = SymmetricGroup(0)942sage: x = S.one(); x943()944sage: x.domain()945[]946"""947cdef int i948949#We need to do this to handle the case of SymmetricGroup(0)950#where the domain is (), but the permutation group element has951#an underlying representation of [1]. The 1 doesn't952#correspond to anything in the domain953if len(self._parent._domain) == 0:954return []955else:956from_gap = self._parent._domain_from_gap957return [from_gap[self.perm[i]+1] for i from 0 <= i < self.n]958959def __hash__(self):960"""961Return hash of this permutation.962963EXAMPLES::964965sage: G = SymmetricGroup(5)966sage: s = G([2,1,5,3,4])967sage: s.tuple()968(2, 1, 5, 3, 4)969sage: hash(s)9701592966088 # 32-bit9712865702456085625800 # 64-bit972sage: hash(s.tuple())9731592966088 # 32-bit9742865702456085625800 # 64-bit975"""976return hash(tuple(self._gap_list()))977978def tuple(self):979"""980Return tuple of images of the domain under self.981982EXAMPLES::983984sage: G = SymmetricGroup(5)985sage: s = G([2,1,5,3,4])986sage: s.tuple()987(2, 1, 5, 3, 4)988989sage: S = SymmetricGroup(['a', 'b'])990sage: S.gen().tuple()991('b', 'a')992"""993if self.__tuple is None:994self.__tuple = tuple(self.domain())995return self.__tuple996997def dict(self):998"""999Returns a dictionary associating each element of the domain with its1000image.10011002EXAMPLES::10031004sage: G = SymmetricGroup(4)1005sage: g = G((1,2,3,4)); g1006(1,2,3,4)1007sage: v = g.dict(); v1008{1: 2, 2: 3, 3: 4, 4: 1}1009sage: type(v[1])1010<type 'int'>1011sage: x = G([2,1]); x1012(1,2)1013sage: x.dict()1014{1: 2, 2: 1, 3: 3, 4: 4}1015"""1016from_gap = self._parent._domain_from_gap1017to_gap = self._parent._domain_to_gap1018cdef int i1019return {e:from_gap[self.perm[i-1]+1] for e,i in to_gap.iteritems()}10201021def order(self):1022"""1023Return the order of this group element, which is the smallest1024positive integer `n` for which `g^n = 1`.10251026EXAMPLES::10271028sage: s = PermutationGroupElement('(1,2)(3,5,6)')1029sage: s.order()1030610311032TESTS::10331034sage: prod(primes(150))103514921823509392793200588757366158410685475838633268645304101036sage: L = [tuple(range(sum(primes(p))+1, sum(primes(p))+1+p)) for p in primes(150)]1037sage: PermutationGroupElement(L).order()103814921823509392793200588757366158410685475838633268645304101039"""1040order = None1041cdef long long order_c = 11042cdef int cycle_len1043cdef int i, k1044cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1045for i from 0 <= i < self.n: seen[i] = 01046for i from 0 <= i < self.n:1047if seen[i] or self.perm[i] == i:1048continue1049k = self.perm[i]1050cycle_len = 11051while k != i:1052seen[k] = 11053k = self.perm[k]1054cycle_len += 11055if order is not None:1056order = order.lcm(cycle_len)1057else:1058order_c = (order_c * cycle_len) / arith.c_gcd_longlong(order_c, cycle_len)1059if order_c > LONG_LONG_MAX / (self.n - i):1060order = Integer(order_c)1061sage_free(seen)1062return int(order_c) if order is None else order10631064def inverse(self):1065r"""1066Returns the inverse permutation.10671068OUTPUT:10691070For an element of a permutation group, this method returns the inverse1071element, which is both the inverse function and the inverse as an1072element of a group.10731074EXAMPLES::10751076sage: s = PermutationGroupElement("(1,2,3)(4,5)")1077sage: s.inverse()1078(1,3,2)(4,5)10791080sage: A = AlternatingGroup(4)1081sage: t = A("(1,2,3)")1082sage: t.inverse()1083(1,3,2)10841085There are several ways (syntactically) to get an inverse1086of a permutation group element. ::10871088sage: s = PermutationGroupElement("(1,2,3,4)(6,7,8)")1089sage: s.inverse() == s^-11090True1091sage: s.inverse() == ~s1092True1093"""1094return ~self10951096def sign(self):1097"""1098Returns the sign of self, which is `(-1)^{s}`, where1099`s` is the number of swaps.11001101EXAMPLES::11021103sage: s = PermutationGroupElement('(1,2)(3,5,6)')1104sage: s.sign()1105-111061107ALGORITHM: Only even cycles contribute to the sign, thus11081109.. math::11101111sign(sigma) = (-1)^{\sum_c len(c)-1}111211131114where the sum is over cycles in self.1115"""1116cdef int cycle_len_sum = 01117cdef int i, k1118cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1119for i from 0 <= i < self.n: seen[i] = 01120for i from 0 <= i < self.n:1121if seen[i] or self.perm[i] == i:1122continue1123k = self.perm[i]1124while k != i:1125seen[k] = 11126k = self.perm[k]1127cycle_len_sum += 11128sage_free(seen)1129return 1 - 2*(cycle_len_sum % 2) # == (-1)^cycle_len113011311132def orbit(self, n, bint sorted=True):1133"""1134Returns the orbit of the integer `n` under this group1135element, as a sorted list.11361137EXAMPLES::11381139sage: G = PermutationGroup(['(1,2,3)(4,5)'])1140sage: g = G.gen(0)1141sage: g.orbit(4)1142[4, 5]1143sage: g.orbit(3)1144[1, 2, 3]1145sage: g.orbit(10)1146[10]11471148::11491150sage: s = SymmetricGroup(['a', 'b']).gen(0); s1151('a','b')1152sage: s.orbit('a')1153['a', 'b']1154"""1155to_gap = self._parent._domain_to_gap1156from_gap = self._parent._domain_from_gap1157try:1158n = to_gap[n]1159except KeyError:1160return [n]11611162cdef int i = n1163cdef int start = i1164if 1 <= i <= self.n:1165L = [from_gap[i]]1166i = self.perm[i-1]+11167while i != start:1168PyList_Append(L,from_gap[i])1169i = self.perm[i-1]+11170if sorted:1171L.sort()1172return L1173else:1174return from_gap[n]11751176def cycles(self):1177"""1178Return self as a list of disjoint cycles.11791180EXAMPLES::11811182sage: G = PermutationGroup(['(1,2,3)(4,5,6,7)'])1183sage: g = G.01184sage: g.cycles()1185[(1,2,3), (4,5,6,7)]1186sage: a, b = g.cycles()1187sage: a(1), b(1)1188(2, 1)1189"""1190L = []1191cdef PermutationGroupElement cycle1192cdef int i, j, k, next_k1193cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1194for i from 0 <= i < self.n: seen[i] = 01195for i from 0 <= i < self.n:1196if seen[i] or self.perm[i] == i:1197continue1198cycle = self._new_c()1199for j from 0 <= j < self.n: cycle.perm[j] = j1200k = cycle.perm[i] = self.perm[i]1201while k != i:1202seen[k] = 11203next_k = cycle.perm[k] = self.perm[k]1204k = next_k1205PyList_Append(L, cycle)1206sage_free(seen)1207return L12081209def cycle_tuples(self, singletons=False):1210"""1211Return self as a list of disjoint cycles, represented as tuples1212rather than permutation group elements.12131214INPUT:12151216- ``singletons`` - boolean (default: False) whether or not consider the1217cycle that correspond to fixed point12181219EXAMPLES::12201221sage: p = PermutationGroupElement('(2,6)(4,5,1)')1222sage: p.cycle_tuples()1223[(1, 4, 5), (2, 6)]1224sage: p.cycle_tuples(singletons=True)1225[(1, 4, 5), (2, 6), (3,)]12261227EXAMPLES::12281229sage: S = SymmetricGroup(4)1230sage: S.gen(0).cycle_tuples()1231[(1, 2, 3, 4)]12321233::12341235sage: S = SymmetricGroup(['a','b','c','d'])1236sage: S.gen(0).cycle_tuples()1237[('a', 'b', 'c', 'd')]1238sage: S([('a', 'b'), ('c', 'd')]).cycle_tuples()1239[('a', 'b'), ('c', 'd')]1240"""1241from_gap = self._parent._domain_from_gap1242L = []1243cdef int i, k1244cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)1245for i from 0 <= i < self.n: seen[i] = 01246for i from 0 <= i < self.n:1247if seen[i]:1248continue1249if self.perm[i] == i:1250if singletons:1251PyList_Append(L, (from_gap[i+1],))1252# it is not necessary to put seen[i] to 1 as we will never1253# see i again1254else:1255continue1256else:1257cycle = [from_gap[i+1]]1258k = self.perm[i]1259while k != i:1260PyList_Append(cycle, from_gap[k+1])1261seen[k] = 11262k = self.perm[k]1263PyList_Append(L, tuple(cycle))1264sage_free(seen)1265return L12661267def cycle_string(self, singletons=False):1268"""1269Return string representation of this permutation.12701271EXAMPLES::12721273sage: g = PermutationGroupElement([(1,2,3),(4,5)])1274sage: g.cycle_string()1275'(1,2,3)(4,5)'12761277sage: g = PermutationGroupElement([3,2,1])1278sage: g.cycle_string(singletons=True)1279'(1,3)(2)'1280"""1281cycles = self.cycle_tuples(singletons)1282if len(cycles) == 0:1283return '()'1284return ''.join([repr(c) for c in cycles]).replace(', ',',').replace(',)',')')12851286def has_descent(self, i, side = "right", positive = False):1287"""1288INPUT:12891290- ``i``: an element of the index set1291- ``side``: "left" or "right" (default: "right")1292- ``positive``: a boolean (default: False)12931294Returns whether ``self`` has a left (resp. right) descent at1295position ``i``. If ``positive`` is True, then test for a non1296descent instead.12971298Beware that, since permutations are acting on the right, the1299meaning of descents is the reverse of the usual1300convention. Hence, ``self`` has a left descent at position1301``i`` if ``self(i) > self(i+1)``.13021303EXAMPLES::13041305sage: S = SymmetricGroup([1,2,3])1306sage: S.one().has_descent(1)1307False1308sage: S.one().has_descent(2)1309False1310sage: s = S.simple_reflections()1311sage: x = s[1]*s[2]1312sage: x.has_descent(1, side = "right")1313False1314sage: x.has_descent(2, side = "right")1315True1316sage: x.has_descent(1, side = "left")1317True1318sage: x.has_descent(2, side = "left")1319False1320sage: S._test_has_descent()13211322The symmetric group acting on a set not of the form1323`(1,\dots,n)` is also supported::13241325sage: S = SymmetricGroup([2,4,1])1326sage: s = S.simple_reflections()1327sage: x = s[2]*s[4]1328sage: x.has_descent(4)1329True1330sage: S._test_has_descent()1331"""1332to_gap = self._parent._domain_to_gap1333from_gap = self._parent._domain_from_gap1334if side == "right":1335self = ~self13361337try:1338i1 = from_gap[to_gap[i]+1]1339except KeyError:1340return False13411342return (to_gap[self(i)] > to_gap[self(i1)]) is not positive13431344def matrix(self):1345"""1346Returns deg x deg permutation matrix associated to the permutation1347self13481349EXAMPLES::13501351sage: G = PermutationGroup(['(1,2,3)(4,5)'])1352sage: g = G.gen(0)1353sage: g.matrix()1354[0 1 0 0 0]1355[0 0 1 0 0]1356[1 0 0 0 0]1357[0 0 0 0 1]1358[0 0 0 1 0]1359"""1360M = MatrixSpace(ZZ, self.n, self.n, sparse=True)1361cdef int i1362entries = {}1363for i from 0 <= i < self.n:1364entries[i, self.perm[i]] = 11365return M(entries)13661367def word_problem(self, words, display=True):1368"""1369G and H are permutation groups, g in G, H is a subgroup of G1370generated by a list (words) of elements of G. If g is in H, return1371the expression for g as a word in the elements of (words).13721373This function does not solve the word problem in Sage. Rather it1374pushes it over to GAP, which has optimized algorithms for the word1375problem. Essentially, this function is a wrapper for the GAP1376functions "EpimorphismFromFreeGroup" and1377"PreImagesRepresentative".13781379EXAMPLE::13801381sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]], canonicalize=False)1382sage: g1, g2 = G.gens()1383sage: h = g1^2*g2*g11384sage: h.word_problem([g1,g2], False)1385('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')1386sage: h.word_problem([g1,g2])1387x1^2*x2^-1*x11388[['(1,2,3)(4,5)', 2], ['(3,4)', -1], ['(1,2,3)(4,5)', 1]]1389('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')1390"""1391if not self._parent._has_natural_domain():1392raise NotImplementedError13931394import copy1395from sage.groups.perm_gps.permgroup import PermutationGroup1396from sage.interfaces.all import gap13971398G = gap(words[0].parent())1399g = words[0].parent()(self)1400H = gap.Group(words)1401ans = G.EpimorphismFromFreeGroup().PreImagesRepresentative(g)14021403l1 = str(ans)1404l2 = copy.copy(l1)1405l4 = []1406l3 = l1.split("*")1407for i in range(1,len(words)+1):1408l2 = l2.replace("x"+str(i),str(words[i-1]))14091410if display:1411for i in range(len(l3)): ## parsing the word for display1412if len(l3[i].split("^"))==2:1413l4.append([l3[i].split("^")[0],int(l3[i].split("^")[1])])1414if len(l3[i].split("^"))==1:1415l4.append([l3[i].split("^")[0],1])1416l5 = copy.copy(l4)1417for i in range(len(l4)):1418for j in range(1,len(words)+1):1419l5[i][0] = l5[i][0].replace("x"+str(j),str(words[j-1]))1420print " ",l11421print " ",l51422return l1,l214231424def conjugacy_class(self):1425r"""1426Return the conjugacy class of ``self``.14271428EXAMPLES::14291430sage: D = DihedralGroup(5)1431sage: g = D((1,3,5,2,4))1432sage: g.conjugacy_class()1433Conjugacy class of (1,3,5,2,4) in Dihedral group of order 10 as a permutation group1434"""1435from sage.groups.conjugacy_classes import ConjugacyClassGAP1436return ConjugacyClassGAP(self.parent(), self)143714381439cdef bint is_valid_permutation(int* perm, int n):1440"""1441This is used in the __init__ method.14421443Returns True iff the first n elements of perm are literally a1444permutation of [0, ..., n-1].14451446TESTS::14471448sage: S = SymmetricGroup(10)1449sage: PermutationGroupElement([2,1],S,check=False)1450(1,2)1451sage: PermutationGroupElement([1,1],S,check=False)1452Traceback (most recent call last):1453...1454ValueError: Invalid permutation vector: [1, 1]1455sage: PermutationGroupElement([1,-1],S,check=False)1456Traceback (most recent call last):1457...1458ValueError: Invalid permutation vector: [1, -1]1459sage: PermutationGroupElement([1,2,3,10],S,check=False)1460Traceback (most recent call last):1461...1462ValueError: Invalid permutation vector: [1, 2, 3, 10]1463"""1464cdef int i, ix1465# make everything is in bounds1466for i from 0 <= i < n:1467if not 0 <= perm[i] < n:1468return False1469# mark hit points by sign1470for i from 0 <= i < n:1471ix = -1-perm[i] if perm[i] < 0 else perm[i]1472perm[ix] = -1-perm[ix]1473# make sure everything is hit once, and reset signs1474for i from 0 <= i < n:1475if perm[i] >= 0:1476return False1477perm[i] = -1-perm[i]14781479return True1480148114821483