Path: blob/master/sage/combinat/integer_vectors_mod_permgroup.py
4045 views
r"""1Integer vectors modulo the action of a permutation group2"""3#*****************************************************************************4# Copyright (C) 2010-12 Nicolas Borie <nicolas.borie at math dot u-psud.fr>5#6# Distributed under the terms of the GNU General Public License (GPL)7#8# The full text of the GPL is available at:9# http://www.gnu.org/licenses/10#*****************************************************************************1112from itertools import imap13from sage.structure.unique_representation import UniqueRepresentation14from sage.rings.semirings.non_negative_integer_semiring import NN1516from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets17from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets1819from sage.structure.list_clone import ClonableIntArray20from sage.combinat.backtrack import SearchForest2122from sage.combinat.enumeration_mod_permgroup import is_canonical, orbit, canonical_children, canonical_representative_of_orbit_of2324from sage.combinat.integer_vector import IntegerVectors2526class IntegerVectorsModPermutationGroup(UniqueRepresentation):27r"""28Returns an enumerated set containing integer vectors which are29maximal in their orbit under the action of the permutation group30``G`` for the lexicographic order.3132In Sage, a permutation group `G` is viewed as a subgroup of the33symmetric group `S_n` of degree `n` and `n` is said to be the degree34of `G`. Any integer vector `v` is said to be canonical if it35is maximal in its orbit under the action of `G`. `v` is36canonical if and only if3738.. math::3940v = \max_{\text{lex order}} \{g \cdot v | g \in G \}4142The action of `G` is on position. This means for example that the43simple transposition `s_1 = (1, 2)` swaps the first and the second entries44of any integer vector `v = [a_1, a_2, a_3, \dots , a_n]`4546.. math::4748s_1 \cdot v = [a_2, a_1, a_3, \dots , a_n]4950This functions returns a parent which contains a single integer51vector by orbit under the action of the permutation group `G`. The52approach chosen here is to keep the maximal integer vector for the53lexicographic order in each orbit. Such maximal vector will be54called canonical integer vector under the action of the55permutation group `G`.5657INPUT:5859- ``G`` - a permutation group60- ``sum`` - (default: None) - a nonnegative integer61- ``max_part`` - (default: None) - a nonnegative integer setting the62maximum of entries of elements63- ``sgs`` - (default: None) - a strong generating system of the64group `G`. If you do not provide it, it will be calculated at the65creation of the parent6667OUTPUT:6869- If ``sum`` and ``max_part`` are None, it returns the infinite enumerated70set of all integer vectors (list of integers) maximal in their orbit for71the lexicographic order.7273- If ``sum`` is an integer, it returns a finite enumerated set containing74all integer vectors maximal in their orbit for the lexicographic order75and whose entries sum to ``sum``.7677EXAMPLES:7879Here is the set enumerating integer vectors modulo the action of the cyclic80group of `3` elements::8182sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]))83sage: I.category()84Join of Category of infinite enumerated sets and Category of quotients of sets85sage: I.cardinality()86+Infinity87sage: I.list()88Traceback (most recent call last):89...90NotImplementedError: infinite list91sage: p = iter(I)92sage: for i in range(10): p.next()93[0, 0, 0]94[1, 0, 0]95[2, 0, 0]96[1, 1, 0]97[3, 0, 0]98[2, 1, 0]99[2, 0, 1]100[1, 1, 1]101[4, 0, 0]102[3, 1, 0]103104The method105:meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_All.is_canonical`106tests if any integer vector is maximal in its orbit. This method107is also used in the containment test::108109sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))110sage: I.is_canonical([5,2,0,4])111True112sage: I.is_canonical([5,0,6,4])113False114sage: I.is_canonical([1,1,1,1])115True116sage: [2,3,1,0] in I117False118sage: [5,0,5,0] in I119True120sage: 'Bla' in I121False122sage: I.is_canonical('bla')123Traceback (most recent call last):124...125AssertionError: bla should be a list or a integer vector126127If you give a value to the extra argument ``sum``, the set returned128will be a finite set containing only canonical vectors whose entries129sum to ``sum``.::130131sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), sum=6)132sage: I.cardinality()13310134sage: I.list()135[[6, 0, 0], [5, 1, 0], [5, 0, 1], [4, 2, 0], [4, 1, 1],136[4, 0, 2], [3, 3, 0], [3, 2, 1], [3, 1, 2], [2, 2, 2]]137sage: I.category()138Join of Category of finite enumerated sets and Category of quotients of sets139140To get the orbit of any integer vector `v` under the action of the group,141use the method :meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_All.orbit`;142we convert the returned set of vectors into a list in increasing lexicographic order,143to get a reproducible test::144145sage: from sage.combinat.enumeration_mod_permgroup import lex_cmp146sage: sorted(I.orbit([6,0,0]), cmp=lex_cmp)147[[0, 0, 6], [0, 6, 0], [6, 0, 0]]148sage: sorted(I.orbit([5,1,0]), cmp=lex_cmp)149[[0, 5, 1], [1, 0, 5], [5, 1, 0]]150sage: I.orbit([2,2,2])151set([[2, 2, 2]])152153TESTS:154155Let us check that canonical integer vectors of the symmetric group156are just sorted list of integers::157158sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time159sage: p = iter(I) # long time160sage: for i in range(100): # long time161... v = list(p.next())162... assert sorted(v, reverse=True) == v163164We now check that there is as much of canonical vectors under the165symmetric group `S_n` whose entries sum to `d` than partitions of166`d` of at most `n` parts::167168sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time169sage: for i in range(10): # long time170... d1 = I.subset(i).cardinality()171... d2 = Partitions(i, max_length=5).cardinality()172... print d1173... assert d1 == d217411751176217731785179718010181131821818323184185We present a last corner case: trivial groups. For the trivial186group ``G`` acting on a list of length `n`, all integer vectors of187length `n` are canonical::188189sage: G = PermutationGroup([[(6,)]]) # long time190sage: G.cardinality() # long time1911192sage: I = IntegerVectorsModPermutationGroup(G) # long time193sage: for i in range(10): # long time194... d1 = I.subset(i).cardinality()195... d2 = IntegerVectors(i,6).cardinality()196... print d1197... assert d1 == d219811996200212015620212620325220446220579220612872072002208"""209@staticmethod210def __classcall__(cls, G, sum=None, max_part=None, sgs=None):211r"""212TESTS::213214sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]))215sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), None)216sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 2)217sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), -2)218Traceback (most recent call last):219...220ValueError: Value -2 in not in Non negative integer semiring.221sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 8, max_part=5)222"""223if sum is None and max_part is None:224return IntegerVectorsModPermutationGroup_All(G, sgs=sgs)225else:226if sum is not None:227assert (sum == NN(sum))228if max_part is not None:229assert (max_part == NN(max_part))230return IntegerVectorsModPermutationGroup_with_constraints(G, sum, max_part, sgs=sgs)231232class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, SearchForest):233r"""234A class for integer vectors enumerated up to the action of a235permutation group.236237A Sage permutation group is viewed as a subgroup of the symmetric238group `S_n` for a certain `n`. This group has a natural action by239position on vectors of length `n`. This class implements a set240which keeps a single vector for each orbit. We say that a vector241is canonical if it is the maximum in its orbit under the action of242the permutation group for the lexicographic order.243244EXAMPLES::245246sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))247sage: I248Integer vectors of length 4 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]249sage: I.cardinality()250+Infinity251sage: TestSuite(I).run()252sage: it = iter(I)253sage: [it.next(), it.next(), it.next(), it.next(), it.next()]254[[0, 0, 0, 0],255[1, 0, 0, 0],256[2, 0, 0, 0],257[1, 1, 0, 0],258[1, 0, 1, 0]]259sage: x = it.next(); x260[3, 0, 0, 0]261sage: I.first()262[0, 0, 0, 0]263264TESTS::265266sage: TestSuite(I).run()267"""268def __init__(self, G, sgs=None):269"""270TESTS::271272sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))273sage: I274Integer vectors of length 4 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]275sage: I.category()276Join of Category of infinite enumerated sets and Category of quotients of sets277sage: TestSuite(I).run()278"""279SearchForest.__init__(self, algorithm = 'breadth', category = InfiniteEnumeratedSets().Quotients())280self._permgroup = G281self.n = G.degree()282283# self.sgs: strong_generating_system284if sgs is None:285self._sgs = G.strong_generating_system()286else:287self._sgs = map(lambda x: list(x), list(sgs))288289def _repr_(self):290"""291TESTS::292293sage: IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]))294Integer vectors of length 3 enumerated up to the action of Permutation Group with generators [(1,2,3)]295"""296return "Integer vectors of length %s enumerated up to the action of %s"%(str(self.n), self._permgroup.__repr__())297298def ambient(self):299r"""300Return the ambient space from which ``self`` is a quotient.301302EXAMPLES::303304sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))305sage: S.ambient()306Integer vectors307"""308# TODO: Fix me once 'IntegerVectors(length=bla)' will return309# the integer vectors of length bla310return IntegerVectors(length=self.n)311312def lift(self, elt):313r"""314Lift the element ``elt`` inside the ambient space from which ``self`` is a quotient.315316EXAMPLES::317318sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))319sage: v = S.lift(S([4,3,0,1])); v320[4, 3, 0, 1]321sage: type(v)322<type 'list'>323"""324# TODO: For now, Sage integer vectors are just python list.325# Once Integer vectors will have an element class, update this326# code properly327return list(elt)328329def retract(self, elt):330r"""331Return the canonical representative of the orbit of the332integer ``elt`` under the action of the permutation group333defining ``self``.334335If the element ``elt`` is already maximal in its orbit for336the lexicographic order, ``elt`` is thus the good337representative for its orbit.338339EXAMPLES::340341sage: [0,0,0,0] in IntegerVectors(length=4)342True343sage: [1,0,0,0] in IntegerVectors(length=4)344True345sage: [0,1,0,0] in IntegerVectors(length=4)346True347sage: [1,0,1,0] in IntegerVectors(length=4)348True349sage: [0,1,0,1] in IntegerVectors(length=4)350True351sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))352sage: S.retract([0,0,0,0])353[0, 0, 0, 0]354sage: S.retract([1,0,0,0])355[1, 0, 0, 0]356sage: S.retract([0,1,0,0])357[1, 0, 0, 0]358sage: S.retract([1,0,1,0])359[1, 0, 1, 0]360sage: S.retract([0,1,0,1])361[1, 0, 1, 0]362"""363# TODO: Once Sage integer vector will have a data structure364# based on ClonableIntArray, remove the conversion intarray365assert len(elt) == self.n, "%s is a quotient set of %s"%(self, self.ambient())366intarray = self.element_class(self, elt, check=False)367return self.element_class(self, canonical_representative_of_orbit_of(self._sgs, intarray), check=False)368369def roots(self):370r"""371Returns the root of generation of ``self``. This method is372required to build the tree structure of ``self`` which373inherits from the class :class:`~sage.combinat.backtrack.SearchForest`.374375EXAMPLES::376377sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))378sage: I.roots()379[[0, 0, 0, 0]]380"""381return [self.element_class(self, self.n*[0,], check=False)]382383def children(self, x):384r"""385Returns the list of children of the element ``x``. This method386is required to build the tree structure of ``self`` which387inherits from the class :class:`~sage.combinat.backtrack.SearchForest`.388389EXAMPLES::390391sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))392sage: I.children(I([2,1,0,0], check=False))393[[2, 2, 0, 0], [2, 1, 1, 0], [2, 1, 0, 1]]394"""395return canonical_children(self._sgs, x, -1)396397def permutation_group(self):398r"""399Returns the permutation group given to define ``self``.400401EXAMPLES::402403sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))404sage: I.permutation_group()405Permutation Group with generators [(1,2,3,4)]406"""407return self._permgroup408409def is_canonical(self, v, check=True):410r"""411Returns ``True`` if the integer list ``v`` is maximal in its412orbit under the action of the permutation group given to413define ``self``. Such integer vectors are said to be414canonical. A vector `v` is canonical if and only if415416.. math::417418v = \max_{\text{lex order}} \{g \cdot v | g \in G \}419420EXAMPLES::421422sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))423sage: I.is_canonical([4,3,2,1])424True425sage: I.is_canonical([4,0,0,1])426True427sage: I.is_canonical([4,0,3,3])428True429sage: I.is_canonical([4,0,4,4])430False431"""432if check:433assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector'%v434assert (self.n == len(v)), '%s should be of length %s'%(v, self.n)435for p in v:436assert (p == NN(p)), 'Elements of %s should be integers'%s437return is_canonical(self._sgs, self.element_class(self, list(v), check=False))438439def __contains__(self, v):440"""441EXAMPLES::442443sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))444sage: [2,2,0,0] in I445True446sage: [2,0,1,0] in I447True448sage: [2,0,0,1] in I449True450sage: [2,0,0,2] in I451False452sage: [2,0,0,2,12] in I453False454"""455try:456return self.is_canonical(self.element_class(self, list(v), check=False), check=False)457except:458return False459460def __call__(self, v, check=True):461r"""462Returns an element of ``self`` constructed from ``v`` if463possible.464465TESTS::466467sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))468sage: I([3,2,1,0])469[3, 2, 1, 0]470"""471try:472if v.parent() is self:473return v474else:475raise ValueError, '%s shoud be a Python list of integer'%(v)476except:477return self.element_class(self, list(v), check=check)478479def orbit(self, v):480r"""481Returns the orbit of the integer vector ``v`` under the action of the482permutation group defining ``self``. The result is a set.483484EXAMPLES:485486In order to get reproducible doctests, we convert the returned sets487into lists in increasing lexicographic order::488489sage: from sage.combinat.enumeration_mod_permgroup import lex_cmp490sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))491sage: sorted(I.orbit([2,2,0,0]), cmp=lex_cmp)492[[0, 0, 2, 2], [0, 2, 2, 0], [2, 0, 0, 2], [2, 2, 0, 0]]493sage: sorted(I.orbit([2,1,0,0]), cmp=lex_cmp)494[[0, 0, 2, 1], [0, 2, 1, 0], [1, 0, 0, 2], [2, 1, 0, 0]]495sage: sorted(I.orbit([2,0,1,0]), cmp=lex_cmp)496[[0, 1, 0, 2], [0, 2, 0, 1], [1, 0, 2, 0], [2, 0, 1, 0]]497sage: sorted(I.orbit([2,0,2,0]), cmp=lex_cmp)498[[0, 2, 0, 2], [2, 0, 2, 0]]499sage: I.orbit([1,1,1,1])500set([[1, 1, 1, 1]])501"""502assert isinstance(v, (list, ClonableIntArray)), '%s shoud be a Python list or an element of %s'%(v, self)503try:504if v.parent() is self:505return orbit(self._sgs, v)506raise TypeError507except:508return orbit(self._sgs, self.element_class(self, v, check=False))509510def subset(self, sum=None, max_part=None):511r"""512Returns the subset of ``self`` containing integer vectors513whose entries sum to ``sum``.514515EXAMPLES::516517sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))518sage: S.subset(4)519Integer vectors of length 4 and of sum 4 enumerated up to520the action of Permutation Group with generators521[(1,2,3,4)]522"""523return IntegerVectorsModPermutationGroup_with_constraints(self.permutation_group(), sum, max_part)524525class Element(ClonableIntArray):526r"""527Element class for the set of integer vectors of given sum enumerated modulo528the action of a permutation group. These vector are clonable lists of integers529which must check conditions comming form the parent appearing in the method530:meth:`~sage.structure.list_clone.ClonableIntArray.check`.531532TESTS::533534sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))535sage: v = I.element_class(I, [4,3,2,1]); v536[4, 3, 2, 1]537sage: TestSuite(v).run()538sage: I.element_class(I, [4,3,2,5])539Traceback (most recent call last):540...541AssertionError542"""543def check(self):544r"""545Checks that ``self`` verify the invariants needed for546living in ``self.parent()``.547548EXAMPLES::549550sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))551sage: v = I.an_element()552sage: v.check()553sage: w = I([0,4,0,0], check=False); w554[0, 4, 0, 0]555sage: w.check()556Traceback (most recent call last):557...558AssertionError559"""560assert self.parent().is_canonical(self)561562563class IntegerVectorsModPermutationGroup_with_constraints(UniqueRepresentation, SearchForest):564r"""565This class models finite enumerated sets of integer vectors with566constraint enumerated up to the action of a permutation group.567Integer vectors are enumerated modulo the action of the568permutation group. To implement that, we keep a single integer569vector by orbit under the action of the permutation570group. Elements chosen are vectors maximal in their orbit for the571lexicographic order.572573For more information see :class:`IntegerVectorsModPermutationGroup`.574575EXAMPLES::576577sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=1)578sage: I.list()579[[0, 0, 0, 0], [1, 0, 0, 0], [1, 1, 0, 0], [1, 0, 1, 0], [1, 1, 1, 0], [1, 1, 1, 1]]580sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=6, max_part=4)581sage: I.list()582[[4, 2, 0, 0], [4, 1, 1, 0], [4, 1, 0, 1], [4, 0, 2, 0], [4, 0, 1, 1],583[4, 0, 0, 2], [3, 3, 0, 0], [3, 2, 1, 0], [3, 2, 0, 1], [3, 1, 2, 0],584[3, 1, 1, 1], [3, 1, 0, 2], [3, 0, 3, 0], [3, 0, 2, 1], [3, 0, 1, 2],585[2, 2, 2, 0], [2, 2, 1, 1], [2, 1, 2, 1]]586587Here is the enumeration of unlabeled graphs over 5 vertices::588589sage: G = IntegerVectorsModPermutationGroup(TransitiveGroup(10,12), max_part=1) # optional590sage: G.cardinality() # optional59134592593TESTS::594595sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4)596sage: TestSuite(I).run()597"""598def __init__(self, G, d, max_part, sgs=None):599r"""600TESTS::601602sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4)603"""604SearchForest.__init__(self, algorithm = 'breadth', category = (FiniteEnumeratedSets(), FiniteEnumeratedSets().Quotients()))605self._permgroup = G606self.n = G.degree()607self._sum = d608if max_part is None:609self._max_part = -1610else:611self._max_part = max_part612613# self.sgs: strong_generating_system614if sgs is None:615self._sgs = G.strong_generating_system()616else:617self._sgs = map(lambda x: list(x), list(sgs))618619def _repr_(self):620r"""621TESTS::622623sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]])); S624Integer vectors of length 4 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]625sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6); S626Integer vectors of length 4 and of sum 6 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]627sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4); S628Vectors of length 4 and of sum 6 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]629sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=4); S630Integer vectors of length 4 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]631"""632if self._sum is not None:633if self._max_part >= 0:634return "Vectors of length %s and of sum %s whose entries is in {0, ..., %s} enumerated up to the action of %s"%(self.n, self._sum, self._max_part, self.permutation_group())635else:636return "Integer vectors of length %s and of sum %s enumerated up to the action of %s"%(self.n, self._sum, self.permutation_group())637else:638return "Integer vectors of length %s whose entries is in {0, ..., %s} enumerated up to the action of %s"%(self.n, self._max_part, self.permutation_group())639640def roots(self):641r"""642Returns the root of generation of ``self``.This method is643required to build the tree structure of ``self`` which644inherits from the class645:class:`~sage.combinat.backtrack.SearchForest`.646647EXAMPLES::648649sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))650sage: I.roots()651[[0, 0, 0, 0]]652"""653return [self.element_class(self, self.n*[0,], check=False)]654655def children(self, x):656r"""657Returns the list of children of the element ``x``. This method658is required to build the tree structure of ``self`` which659inherits from the class660:class:`~sage.combinat.backtrack.SearchForest`.661662EXAMPLES::663664sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))665sage: I.children(I([2,1,0,0], check=False))666[[2, 2, 0, 0], [2, 1, 1, 0], [2, 1, 0, 1]]667"""668return canonical_children(self._sgs, x, -1)669670def permutation_group(self):671r"""672Returns the permutation group given to define ``self``.673674EXAMPLES::675676sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 5)677sage: I.permutation_group()678Permutation Group with generators [(1,2,3)]679"""680return self._permgroup681682def __contains__(self, v):683r"""684TESTS::685686sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),6)687sage: [6,0,0,0] in I688True689sage: [5,0,1,0] in I690True691sage: [0,5,1,0] in I692False693sage: [3,0,1,3] in I694False695sage: [3,3,1,0] in I696False697"""698try:699return (self(v)).parent() is self700except:701return False702703def __call__(self, v, check=True):704r"""705Make `v` an element living in ``self``.706707TESTS::708709sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 4)710sage: v = I([2,1,0,1]); v711[2, 1, 0, 1]712sage: v.parent()713Integer vectors of length 4 and of sum 4 enumerated up to714the action of Permutation Group with generators715[(1,2,3,4)]716"""717try:718if v.parent() is self:719return v720else:721raise ValueError, '%s shoud be a Python list of integer'%(v)722except:723return self.element_class(self, list(v), check=check)724725def __iter__(self):726r"""727TESTS::728729sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4)730sage: for i in I: i731[4, 0, 0, 0]732[3, 1, 0, 0]733[3, 0, 1, 0]734[3, 0, 0, 1]735[2, 2, 0, 0]736[2, 1, 1, 0]737[2, 1, 0, 1]738[2, 0, 2, 0]739[2, 0, 1, 1]740[1, 1, 1, 1]741sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=7, max_part=3)742sage: for i in I: i743[3, 3, 1, 0]744[3, 3, 0, 1]745[3, 2, 2, 0]746[3, 2, 1, 1]747[3, 2, 0, 2]748[3, 1, 3, 0]749[3, 1, 2, 1]750[3, 1, 1, 2]751[3, 0, 2, 2]752[2, 2, 2, 1]753"""754if self._max_part < 0:755return self.elements_of_depth_iterator(self._sum)756else:757SF = SearchForest((self([0]*(self.n), check=False),),758lambda x : map(lambda y : self(y, check=False), canonical_children(self._sgs, x, self._max_part)),759algorithm = 'breadth')760if self._sum is None:761return iter(SF)762else:763return SF.elements_of_depth_iterator(self._sum)764765def is_canonical(self, v, check=True):766r"""767Returns ``True`` if the integer list ``v`` is maximal in its768orbit under the action of the permutation group given to769define ``self``. Such integer vectors are said to be770canonical. A vector `v` is canonical if and only if771772.. math::773774v = \max_{\text{lex order}} \{g \cdot v | g \in G \}775776EXAMPLES::777778sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=3)779sage: I.is_canonical([3,0,0,0])780True781sage: I.is_canonical([1,0,2,0])782False783sage: I.is_canonical([2,0,1,0])784True785"""786if check:787assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector'%v788assert (self.n == len(v)), '%s should be of length %s'%(v, self.n)789for p in v:790assert (p == NN(p)), 'Elements of %s should be integers'%s791return is_canonical(self._sgs, self.element_class(self, list(v), check=False))792793def ambient(self):794r"""795Return the ambient space from which ``self`` is a quotient.796797EXAMPLES::798799sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6); S.ambient()800Integer vectors that sum to 6801sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=12); S.ambient()802Integer vectors that sum to 6 with constraints: max_part=12803804.. todo::805806Integer vectors should accept ``max_part`` as a single argument, and the following should change::807808sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=12); S.ambient()809Integer vectors810811"""812if self._sum is not None:813if self._max_part <= -1:814return IntegerVectors(n=self._sum)815else:816return IntegerVectors(n=self._sum, max_part=self._max_part)817else:818# Fix me once max_part should be accepted as a single819# argument for integer vectors820return IntegerVectors(max_part=self._max_part)821822def lift(self, elt):823r"""824Lift the element ``elt`` inside the ambient space from which ``self`` is a quotient.825826EXAMPLES::827828sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=1)829sage: v = S.lift([1,0,1,0]); v830[1, 0, 1, 0]831sage: v in IntegerVectors(max_part=1)832True833sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=6)834sage: v = S.lift(S.list()[5]); v835[4, 1, 1, 0]836sage: v in IntegerVectors(n=6)837True838"""839# TODO: For now, Sage integer vectors are just python list.840# Once Integer vectors will have an element class, update this841# code properly842return list(elt)843844def retract(self, elt):845r"""846Return the canonical representative of the orbit of the847integer ``elt`` under the action of the permutation group848defining ``self``.849850If the element ``elt`` is already maximal in its orbits for851the lexicographic order, ``elt`` is thus the good852representative for its orbit.853854EXAMPLES::855856sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=2, max_part=1)857sage: S.retract([1,1,0,0])858[1, 1, 0, 0]859sage: S.retract([1,0,1,0])860[1, 0, 1, 0]861sage: S.retract([1,0,0,1])862[1, 1, 0, 0]863sage: S.retract([0,1,1,0])864[1, 1, 0, 0]865sage: S.retract([0,1,0,1])866[1, 0, 1, 0]867sage: S.retract([0,0,1,1])868[1, 1, 0, 0]869"""870# TODO: Once Sage integer vector will have a data structure871# based on ClonableIntArray, remove the conversion intarray872assert len(elt) == self.n, "%s is a quotient set of %s"%(self, self.ambient())873if self._sum is not None:874assert sum(elt) == self._sum, "%s is a quotient set of %s"%(self, self.ambient())875if self._max_part >= 0:876assert max(elt) <= self._max_part, "%s is a quotient set of %s"%(self, self.ambient())877intarray = self.element_class(self, elt, check=False)878return self.element_class(self, canonical_representative_of_orbit_of(self._sgs, intarray), check=False)879880def an_element(self):881r"""882Returns an element of ``self`` or raises an EmptySetError when883``self`` is empty.884885EXAMPLES::886887sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=0, max_part=1); S.an_element()888[0, 0, 0, 0]889sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=1, max_part=1); S.an_element()890[1, 0, 0, 0]891sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=2, max_part=1); S.an_element()892[1, 1, 0, 0]893sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=3, max_part=1); S.an_element()894[1, 1, 1, 0]895sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=4, max_part=1); S.an_element()896[1, 1, 1, 1]897sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=5, max_part=1); S.an_element()898Traceback (most recent call last):899...900EmptySetError901"""902if self._max_part < 0:903return self([self._sum]+(self.n-1)*[0], check=False)904else:905try:906v = iter(self)907return v.next()908except StopIteration:909from sage.categories.sets_cat import EmptySetError910raise EmptySetError911912def orbit(self, v):913r"""914Returns the orbit of the vector ``v`` under the action of the915permutation group defining ``self``. The result is a set.916917INPUT:918919- ``v`` - an element of ``self`` or any list of length the920degree of the permutation group.921922EXAMPLES:923924We convert the result in a list in increasing lexicographic925order, to get a reproducible doctest::926927sage: from sage.combinat.enumeration_mod_permgroup import lex_cmp928sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4)929sage: I.orbit([1,1,1,1])930set([[1, 1, 1, 1]])931sage: sorted(I.orbit([3,0,0,1]), cmp=lex_cmp)932[[0, 0, 1, 3], [0, 1, 3, 0], [1, 3, 0, 0], [3, 0, 0, 1]]933"""934assert isinstance(v, (list, ClonableIntArray)), '%s shoud be a Python list or an element of %s'%(v, self)935try:936if v.parent() is self:937return orbit(self._sgs, v)938except:939return orbit(self._sgs, self.element_class(self, v, check=False))940941class Element(ClonableIntArray):942r"""943Element class for the set of integer vectors with constraints enumerated944modulo the action of a permutation group. These vectors are clonable lists945of integers which must check conditions comming form the parent as in946the method :meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_with_constraints.Element.check`.947948TESTS::949950sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 4)951sage: v = I.element_class(I, [3,1,0,0]); v952[3, 1, 0, 0]953sage: TestSuite(v).run()954sage: v = I.element_class(I, [3,2,0,0])955Traceback (most recent call last):956...957AssertionError: [3, 2, 0, 0] should be a integer vector of sum 4958"""959def check(self):960r"""961Checks that ``self`` meets the constraints of being an element of ``self.parent()``.962963EXAMPLES::964965sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 4)966sage: v = I.an_element()967sage: v.check()968sage: w = I([0,4,0,0], check=False); w969[0, 4, 0, 0]970sage: w.check()971Traceback (most recent call last):972...973AssertionError974"""975if self.parent()._sum is not None:976assert sum(self) == self.parent()._sum, '%s should be a integer vector of sum %s'%(self, self.parent()._sum)977if self.parent()._max_part >= 0:978assert max(self) <= self.parent()._max_part, 'Entries of %s must be inferiors to %s'%(self, self.parent()._max_part)979assert self.parent().is_canonical(self)980981982