Ticket #10534: trac_10534-generation_of_subsets_and_set_partitions.2.patch

File trac_10534-generation_of_subsets_and_set_partitions.2.patch, 81.0 KB (added by vdelecroix, 5 years ago)
  • sage/combinat/choose_nk.py

    # HG changeset patch
    # User Vincent Delecroix <20100.delecroix at gmail.com>
    # Date 1342784184 -32400
    # Node ID 187e3e021f86c278c05f1cd50ae65702a531eab8
    # Parent  73c2ed49a5560ede77d71a8227a00c64a82e0a08
    trac 10534: Optimizations for the generation of subwords, subsets and set partitions
    
    Make links to itertools + doc improvements for choose_nk.py, split_nk.py, subword.py, subset.py, set_partition_ordered.py and set_partition.py
    
     * call to itertools.combinations for enumeration
     * call to functions in sage.rings.arith for cardinality
     * random generation for the various submultisets
     * corrected error for Subwords([1,2,3],0).last()
     * add a default implementation for _an_element_ in CombinatorialClass
     * return Integer value (and not Python int, or Sage Rational) for cardinality method
     * test coverage up to 100%
    
    diff --git a/sage/combinat/choose_nk.py b/sage/combinat/choose_nk.py
    a b  
    11"""
    22Low-level Combinations
     3
     4AUTHORS:
     5
     6- Mike Hansen (2007): initial implementation
     7
     8- Vincent Delecroix (2011): call itertools for faster iteration, bug
     9  corrections, doctests.
    310"""
    411#*****************************************************************************
    512#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     
    1926from sage.rings.arith import binomial
    2027from sage.misc.misc import uniq
    2128from combinat import CombinatorialClass
     29import itertools
     30from sage.rings.integer import Integer
    2231
    2332class ChooseNK(CombinatorialClass):
    2433    """
    2534    Low level combinatorial class of all possible choices of k elements out of
    26     range(n) without repetitions.
     35    range(n) without repetitions. The elements of the output are tuples of
     36    Python int (and not Sage Integer).
     37
    2738
    2839    This is a low-level combinatorial class, with a simplistic interface by
    29     design. It aim at speed. It's element are returned as plain list of python
     40    design. It aims at speed. It's element are returned as plain list of python
    3041    int.
    3142
    3243    EXAMPLES::
     
    3445        sage: from sage.combinat.choose_nk import ChooseNK       
    3546        sage: c = ChooseNK(4,2)
    3647        sage: c.first()
    37         [0, 1]
     48        (0, 1)
    3849        sage: c.list()
    39         [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
     50        [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
    4051        sage: type(c.list()[1])
    41         <type 'list'>
     52        <type 'tuple'>
    4253        sage: type(c.list()[1][1])
    4354        <type 'int'>
    4455    """
     
    6677            False
    6778            sage: [0,1,3] in c52
    6879            False
     80
     81        TESTS::
     82
     83            sage: from sage.combinat.choose_nk import ChooseNK
     84            sage: c52 = ChooseNK(5,2)
     85            sage: [0,2] in c52   # trac 10534
     86            True
     87            sage: [0,5] in c52
     88            False
    6989        """
    7090        try:
    71             x = list(x)
     91            x = map(int,x)
    7292        except TypeError:
    7393            return False
    7494
    75         r = range(len(x))
    76         return all(i in r for i in x) and len(uniq(x)) == self._k
     95        # test if the length is the good one
     96        r = len(x)
     97        if r != self._k:
     98            return False
    7799
     100        # test if there is any repetition
     101        x = set(x)
     102        if len(x) != r:
     103            return False
     104
     105        # test if x is a subset of {0,1,...,n-1}
     106        if x.difference(xrange(self._n)):
     107            return False
     108
     109        return True
    78110       
    79111    def cardinality(self):
    80112        """
     
    97129        EXAMPLES::
    98130       
    99131            sage: from sage.combinat.choose_nk import ChooseNK
    100             sage: [c for c in ChooseNK(5,2)]
    101             [[0, 1],
    102              [0, 2],
    103              [0, 3],
    104              [0, 4],
    105              [1, 2],
    106              [1, 3],
    107              [1, 4],
    108              [2, 3],
    109              [2, 4],
    110              [3, 4]]
     132            sage: list(ChooseNK(5,2))
     133            [(0, 1),
     134             (0, 2),
     135             (0, 3),
     136             (0, 4),
     137             (1, 2),
     138             (1, 3),
     139             (1, 4),
     140             (2, 3),
     141             (2, 4),
     142             (3, 4)]
    111143        """
    112         k = self._k
    113         n = self._n
    114         dif = 1
    115         if k == 0:
    116             yield []
    117             return
    118 
    119         if n < 1+(k-1)*dif:
    120             return
    121         else:
    122             subword = [ i*dif for i in range(k) ]
    123 
    124         yield subword[:]
    125         finished = False
    126 
    127         while not finished:   
    128             #Find the biggest element that can be increased
    129             if subword[-1] < n-1:
    130                 subword[-1] += 1
    131                 yield subword[:]
    132                 continue
    133 
    134             finished = True
    135             for i in reversed(range(k-1)):
    136                 if subword[i]+dif < subword[i+1]:
    137                     subword[i] += 1
    138                     #Reset the bigger elements
    139                     for j in range(1,k-i):
    140                         subword[i+j] = subword[i]+j*dif
    141                     yield subword[:]
    142                     finished = False
    143                     break
    144 
    145         return
    146        
     144        return itertools.combinations(range(self._n), self._k)
    147145
    148146    def random_element(self):
    149147        """
     
    153151       
    154152            sage: from sage.combinat.choose_nk import ChooseNK
    155153            sage: ChooseNK(5,2).random_element()
    156             [0, 2]
     154            (0, 2)
    157155        """
    158         r = rnd.sample(xrange(self._n),self._k)
    159         r.sort()
    160         return r
     156        return tuple(rnd.sample(range(self._n),self._k))
     157
     158        # r.sort()
     159        # removed in trac 10534
     160        # it is not needed to sort because sage.misc.prandom.sample returns
     161        # ordered subword
    161162
    162163    def unrank(self, r):
    163164        """
     
    169170            True
    170171        """
    171172        if r < 0 or r >= self.cardinality():
    172             raise ValueError, "rank must be between 0 and %s (inclusive)"%(self.cardinality()-1)
     173            raise ValueError, "rank must be between 0 and %d (inclusive)"%(self.cardinality()-1)
     174
    173175        return from_rank(r, self._n, self._k)
    174176
    175177    def rank(self, x):
     
    181183            sage: range(c52.cardinality()) == map(c52.rank, c52)
    182184            True
    183185        """       
    184         if len(x) != self._k:
    185             return
     186        if x not in self:
     187            raise ValueError, "%s not in Choose(%d,%d)" %(str(x),self._n,self._k)
    186188
    187189        return rank(x, self._n)
    188190
    189191
    190 def rank(comb, n):
     192def rank(comb, n, check=True):
    191193    """
    192194    Returns the rank of comb in the subsets of range(n) of size k.
    193195   
     
    197199    EXAMPLES::
    198200   
    199201        sage: import sage.combinat.choose_nk as choose_nk
    200         sage: choose_nk.rank([], 3)
     202        sage: choose_nk.rank((), 3)
    201203        0
    202         sage: choose_nk.rank([0], 3)
     204        sage: choose_nk.rank((0,), 3)
    203205        0
    204         sage: choose_nk.rank([1], 3)
     206        sage: choose_nk.rank((1,), 3)
    205207        1
    206         sage: choose_nk.rank([2], 3)
     208        sage: choose_nk.rank((2,), 3)
    207209        2
    208         sage: choose_nk.rank([0,1], 3)
     210        sage: choose_nk.rank((0,1), 3)
    209211        0
    210         sage: choose_nk.rank([0,2], 3)
     212        sage: choose_nk.rank((0,2), 3)
    211213        1
    212         sage: choose_nk.rank([1,2], 3)
     214        sage: choose_nk.rank((1,2), 3)
    213215        2
    214         sage: choose_nk.rank([0,1,2], 3)
     216        sage: choose_nk.rank((0,1,2), 3)
    215217        0
     218
     219        sage: choose_nk.rank((0,1,2,3), 3)
     220        Traceback (most recent call last):
     221        ...
     222        ValueError: len(comb) must be <= n
     223        sage: choose_nk.rank((0,0), 2)
     224        Traceback (most recent call last):
     225        ...
     226        ValueError: comb must be a subword of (0,1,...,n)
    216227    """
     228    k = len(comb)
     229    if check:
     230        if k > n:
     231            raise ValueError, "len(comb) must be <= n"
     232        comb = map(int, comb)
     233        for i in xrange(k-1):
     234            if comb[i+1] <= comb[i]:
     235                raise ValueError, "comb must be a subword of (0,1,...,n)"
    217236
    218     k = len(comb)
    219     if k > n:
    220         raise ValueError, "len(comb) must be <= n"
    221    
    222237    #Generate the combinadic from the
    223238    #combination
    224     w = [0]*k
    225     for i in range(k):
    226         w[i] = (n-1) - comb[i]
     239    #w = [n-1-comb[i] for i in xrange(k)]
    227240
    228241    #Calculate the integer that is the dual of
    229242    #the lexicographic index of the combination
    230243    r = k
    231244    t = 0
    232     for i in range(k):
    233         t += binomial(w[i],r)
     245    for i in xrange(k):
     246        t += binomial(n-1-comb[i],r)
    234247        r -= 1
    235248
    236249    return binomial(n,k)-t-1
    237250
    238 
    239 
    240251def _comb_largest(a,b,x):
    241252    """
    242253    Returns the largest w < a such that binomial(w,b) <= x.
     
    268279   
    269280        sage: import sage.combinat.choose_nk as choose_nk
    270281        sage: choose_nk.from_rank(0,3,0)
    271         []
     282        ()
    272283        sage: choose_nk.from_rank(0,3,1)
    273         [0]
     284        (0,)
    274285        sage: choose_nk.from_rank(1,3,1)
    275         [1]
     286        (1,)
    276287        sage: choose_nk.from_rank(2,3,1)
    277         [2]
     288        (2,)
    278289        sage: choose_nk.from_rank(0,3,2)
    279         [0, 1]
     290        (0, 1)
    280291        sage: choose_nk.from_rank(1,3,2)
    281         [0, 2]
     292        (0, 2)
    282293        sage: choose_nk.from_rank(2,3,2)
    283         [1, 2]
     294        (1, 2)
    284295        sage: choose_nk.from_rank(0,3,3)
    285         [0, 1, 2]
     296        (0, 1, 2)
    286297    """
    287298    if k < 0:
    288299        raise ValueError, "k must be > 0"
     
    294305    x = binomial(n,k) - 1 - r # x is the 'dual' of m
    295306    comb = [None]*k
    296307
    297     for i in range(k):
     308    for i in xrange(k):
    298309        comb[i] = _comb_largest(a,b,x)
    299310        x = x - binomial(comb[i], b)
    300311        a = comb[i]
    301312        b = b -1
    302313
    303     for i in range(k):
     314    for i in xrange(k):
    304315        comb[i] = (n-1)-comb[i]
    305316
    306     return comb
     317    return tuple(comb)
    307318
  • sage/combinat/combination.py

    diff --git a/sage/combinat/combination.py b/sage/combinat/combination.py
    a b  
    11r"""
    22Combinations
     3
     4AUTHORS:
     5
     6- Mike Hansen (2007): initial implementation
     7
     8- Vincent Delecroix (2011): cleaning, bug corrections, doctests
     9
    310"""
    411#*****************************************************************************
    512#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     
    8087         [1, 2, 3],
    8188         [2, 2, 3],
    8289         [1, 2, 2, 3]]
     90
     91    TESTS:
     92
     93    We check that the code works even for non mutable objects::
     94
     95        sage: l = [vector((0,0)), vector((0,1))]
     96        sage: Combinations(l).list()
     97        [[], [(0, 0)], [(0, 1)], [(0, 0), (0, 1)]]
    8398    """
    8499
    85    
    86100
    87101    #Check to see if everything in mset is unique
    88102    if isinstance(mset, (int, Integer)):
    89103        mset = range(mset)
    90104    else:
    91105        mset = list(mset)
    92        
     106
    93107    d = {}
    94108    for i in mset:
    95109        d[mset.index(i)] = 1
    96        
     110
    97111    if len(d) == len(mset):
    98112        if k is None:
    99113            return Combinations_set(mset)
  • sage/combinat/generator.py

    diff --git a/sage/combinat/generator.py b/sage/combinat/generator.py
    a b  
    11"""
    22Generators
     3
     4This module is higly deprecated since itertools exists!
    35"""
    46#*****************************************************************************
    57#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     
    1618#                  http://www.gnu.org/licenses/
    1719#*****************************************************************************
    1820
     21import itertools
     22
     23#TODO: change to itertools.chain
    1924def concat(gens):
    2025    r"""
    2126    Returns a generator that is the concatenation of the generators in
     
    3136        for element in gen:
    3237            yield element
    3338
    34 
     39#TODO: change to itertools.map
    3540def map(f, gen):
    3641    """
    3742    Returns a generator that returns f(g) for g in gen.
     
    4550    for element in gen:
    4651        yield f(element)
    4752
     53#TODO: change to itertools.repeat
    4854def element(element, n = 1):
    4955    """
    5056    Returns a generator that yield a single element n times.
     
    5965    for i in range(n):
    6066        yield element
    6167
     68#TODO: change to itertools.ifilter
    6269def select(f, gen):
    6370    """
    6471    Returns a generator for all the elements g of gen such that f(g) is
  • sage/combinat/set_partition.py

    diff --git a/sage/combinat/set_partition.py b/sage/combinat/set_partition.py
    a b  
    4848- Mike Hansen
    4949
    5050- MuPAD-Combinat developers (for algorithms and design inspiration).
     51
     52- Vincent Delecroix (2010-12-25): cleaning (bugs in __contains__, improved
     53  constructor, more doc, more tests)
    5154"""
    5255#*****************************************************************************
    5356#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     
    6568#*****************************************************************************
    6669
    6770from sage.sets.set import Set, is_Set, Set_object_enumerated
    68 import sage.combinat.partition as partition
    69 import sage.rings.integer
     71from sage.structure.parent import Parent
     72from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     73from cartesian_product import CartesianProduct
     74
    7075import __builtin__
    7176import itertools
    72 from cartesian_product import CartesianProduct
    73 import sage.combinat.subset as subset
    74 import sage.combinat.set_partition_ordered as set_partition_ordered
    7577import copy
     78
     79import sage.rings.integer
     80import sage.rings.arith as arith
     81from sage.functions.other import factorial
     82
     83import partition
     84import subset
     85from subword import Subwords
     86import set_partition_ordered
     87
    7688from combinat import CombinatorialClass, bell_number, stirling_number2
    7789from misc import IterableFunctionCall
    7890
     91#
     92# Constructor
     93#
    7994
    8095def SetPartitions(s, part=None):
    8196    """
     
    93108    You may specify a second argument k. If k is an integer,
    94109    SetPartitions returns the class of set partitions into k parts; if
    95110    it is an integer partition, SetPartitions returns the class of set
    96     partitions whose block sizes correspond to that integer partition.
     111    partitions whose block sizes correspond to that integer partition,
     112    if it is a list of integer partitions then SetPartitions returns
     113    the class of set partitions whose block sizes is one of the integer
     114    partitions.
    97115   
    98116    The Bell number `B_n`, named in honor of Eric Temple Bell,
    99117    is the number of different partitions of a set with n elements.
    100118   
    101     EXAMPLES::
     119    EXAMPLES:
    102120   
    103         sage: S = [1,2,3,4]
    104         sage: SetPartitions(S,2)
    105         Set partitions of [1, 2, 3, 4] with 2 parts
    106         sage: SetPartitions([1,2,3,4], [3,1]).list()
     121    Set partitions of [1,2,3,4]::
     122
     123        sage: s = SetPartitions(4)
     124        sage: s
     125        Set partitions of [1, 2, 3, 4]
     126        sage: s.cardinality()
     127        15
     128
     129    Set partitions of [1,2,3,4] with a given number of parts. The sum of all the
     130    possibilities sum up to the cardinality above::
     131
     132        sage: s1 = SetPartitions(4,1)
     133        sage: s1.cardinality()
     134        1
     135        sage: s2 = SetPartitions(4,2)
     136        sage: s2.cardinality()
     137        7
     138        sage: s3 = SetPartitions(4,3)
     139        sage: s3.cardinality()
     140        6
     141        sage: s4 = SetPartitions(4,4)
     142        sage: s4.cardinality()
     143        1
     144        sage: 1 + 7 + 6 + 1 == 15
     145        True
     146
     147    Set partitions of [1,2,3,4] with given parts::
     148
     149        sage: s22 = SetPartitions(4,[2,2])
     150        sage: s22
     151        Set partitions of [1, 2, 3, 4] with size [2, 2]
     152        sage: s22.list()
     153        [{{1, 2}, {3, 4}}, {{1, 3}, {2, 4}}, {{2, 3}, {1, 4}}]
     154        sage: s31 = SetPartitions(4,[3,1])
     155        sage: s31
     156        Set partitions of [1, 2, 3, 4] with size [3, 1]
     157        sage: s31.list()
    107158        [{{2, 3, 4}, {1}}, {{1, 3, 4}, {2}}, {{3}, {1, 2, 4}}, {{4}, {1, 2, 3}}]
    108         sage: SetPartitions(7, [3,3,1]).cardinality()
    109         70
     159
     160        sage: s = SetPartitions(4,2)
     161        sage: s22.cardinality() + s31.cardinality() == s.cardinality()
     162        True
    110163   
     164    You can specify several parts (in which case the list correspond to the
     165    union)::
     166
     167        sage: s = SetPartitions(5,[[5], [3,2]])
     168        sage: s
     169        Set partitions of [1, 2, 3, 4, 5] with sizes in {[5], [3, 2]}
     170        sage: s1 = SetPartitions(5,[5])
     171        sage: s2 = SetPartitions(5,[3,2])
     172        sage: all(sp in s for sp in s1)
     173        True
     174        sage: all(sp in s for sp in s2)
     175        True
     176        sage: s1.cardinality() + s2.cardinality() == s.cardinality()
     177        True
     178
    111179    In strings, repeated letters are considered distinct::
    112180   
    113181        sage: SetPartitions('aabcd').cardinality()
     
    121189    """
    122190    if isinstance(s, (int, sage.rings.integer.Integer)):
    123191        set = range(1, s+1)
    124     elif isinstance(s, str):
    125         set = [x for x in s]
    126192    else:
    127         set = s
     193        set = list(s)
    128194   
    129195    if part is not None:
    130196        if isinstance(part, (int, sage.rings.integer.Integer)):
     
    132198                raise ValueError, "part must be <= len(set)"
    133199            else:
    134200                return SetPartitions_setn(set,part)
     201
     202        pp = partition.Partitions(len(set))
     203
     204        if part in pp:
     205            return SetPartitions_setpart(set, partition.Partition(part))
     206
     207        if isinstance(part, (list,tuple)) and all(sp in pp for sp in part):
     208            return SetPartitions_setparts(set, Set(map(partition.Partition,part)))
     209
    135210        else:
    136             if part not in partition.Partitions(len(set)):
    137                 raise ValueError, "part must be a partition of %s"%len(set)
    138             else:
    139                 return SetPartitions_setparts(set, [partition.Partition(part)])
     211            raise ValueError, "part must be an integer, a partition or a list of partitions"%len(set)
     212
    140213    else:
    141214        return SetPartitions_set(set)
    142215
     216#
     217# Classes
     218#
    143219
    144 
    145 class SetPartitions_setparts(CombinatorialClass):
    146     Element = Set_object_enumerated
    147     def __init__(self, set, parts):
     220class SetPartitions_setpart(CombinatorialClass):
     221    r"""
     222    Unordered set partition with specified part sizes
     223    """
     224    element_class = Set_object_enumerated
     225    def __init__(self, set, part):
    148226        """
    149227        TESTS::
    150228       
    151229            sage: S = SetPartitions(4, [2,2])
    152230            sage: S == loads(dumps(S))
    153231            True
     232            sage: TestSuite(S).run()
    154233        """
     234        Parent.__init__(self, category=FiniteEnumeratedSets())
    155235        self.set = set
    156         self.parts = parts
     236        self.part = part
    157237
    158     def __repr__(self):
     238    def _repr_(self):
    159239        """
    160240        TESTS::
    161241       
    162242            sage: repr(SetPartitions(4, [2,2]))
    163             'Set partitions of [1, 2, 3, 4] with sizes in [[2, 2]]'
     243            'Set partitions of [1, 2, 3, 4] with size [2, 2]'
     244        """
     245        return "Set partitions of %s with size %s"%(self.set, self.part)
     246
     247    def __contains__(self, x):
     248        """
     249        TESTS::
     250       
     251            sage: S221 = SetPartitions(5, [2,2,1])
     252            sage: S32  = SetPartitions(5, [3,2])
     253            sage: all(sp in S221 for sp in S221)
     254            True
     255            sage: all(sp in S32 for sp in S32)
     256            True
     257            sage: any(sp in S221 for sp in S32)
     258            False
     259            sage: any(sp in S32 for sp in S221)
     260            False
     261        """
     262        part = map(len,x)
     263
     264        #Make sure that the number of elements match up
     265        if sum(part) != len(self.set):
     266            return False
     267
     268        u = set([])
     269        for s in x:
     270            u.update(s)
     271
     272        #Make sure that the union of all the
     273        #sets is the original set
     274        if u != set(self.set):
     275            return False
     276
     277        #Make sure that the part of x equals self.part
     278        if sorted(part,reverse=True) != self.part:
     279            return False
     280
     281        return True
     282
     283    def cardinality(self):
     284        """
     285        Returns the number of set partitions of a set with specified part.
     286       
     287        Let `p = [1^{e_1}, 2^{e_2}, \ldots, n^{e_n}]` be a partition of `n` in
     288        exponential notation. Then the number of partitions of `[1,2,\ldots,n]`
     289        with part sizes `p` is
     290       
     291        .. math::
     292       
     293        \frac{n!}{\prod i!^{e_i} e_i!}
     294
     295        The above number is related to multinomial coefficients which counts the
     296        number of ordered set partitions.
     297       
     298         .. math::
     299
     300        \frac{n!}{\prod i!^{e_i}}
     301       
     302        EXAMPLES::
     303
     304            sage: SetPartitions(4,[2,2]).cardinality()
     305            3
     306            sage: SetPartitions(3,[3]).cardinality()
     307            1
     308            sage: SetPartitions(12,[3,3,3,3]).cardinality()
     309            15400
     310            sage: multinomial([3,3,3,3]) / factorial(4)
     311            15400
     312        """
     313        n = arith.multinomial(list(self.part))
     314        for j in self.part.to_exp_dict().values():
     315            n //= factorial(j)
     316        return n
     317   
     318    def __iter__(self):
     319        """
     320        Returns an iterator of the partitions in self
     321
     322        TESTS::
     323       
     324            sage: S = SetPartitions(3,[2,1])
     325            sage: for p in S: print p
     326            {{2, 3}, {1}}
     327            {{1, 3}, {2}}
     328            {{1, 2}, {3}}
     329
     330            sage: S311 = SetPartitions(5,[3,1,1])
     331            sage: len(list(S311.__iter__())) == S311.cardinality()
     332            True
     333        """
     334        return (Set(map(Set,p)) for p in self._fast_iterator())
     335
     336    def _fast_iterator(self):
     337        r"""
     338        Iterator over the element of self but returns list of tuples instead of
     339        list of Sets
     340
     341        This iterator is faster in principle than the standard one obtained with
     342        the method __iter__.
     343
     344        EXAMPLES::
     345
     346            sage: S = SetPartitions(3,[2,1])
     347            sage: list(S._fast_iterator())
     348            [((1,), (2, 3)), ((2,), (1, 3)), ((3,), (1, 2))]
     349            sage: S311 = SetPartitions(5,[3,1,1])
     350            sage: len(list(S311._fast_iterator())) == S311.cardinality()
     351            True
     352
     353            sage: S = SetPartitions(5,[3,1,1])
     354            sage: for p in S._fast_iterator(): print p
     355            ((1,), (2,), (3, 4, 5))
     356            ((1,), (3,), (2, 4, 5))
     357            ((1,), (4,), (2, 3, 5))
     358            ((1,), (5,), (2, 3, 4))
     359            ((2,), (3,), (1, 4, 5))
     360            ((2,), (4,), (1, 3, 5))
     361            ((2,), (5,), (1, 3, 4))
     362            ((3,), (4,), (1, 2, 5))
     363            ((3,), (5,), (1, 2, 4))
     364            ((4,), (5,), (1, 2, 3))
     365        """
     366        exp = []
     367        block_sizes = []
     368        for i,j in enumerate(self.part.to_exp()):
     369            if j:
     370                exp.append((i+1,j))
     371                block_sizes.append((i+1)*j)
     372        m = len(exp)
     373
     374        for b in set_partition_ordered.OrderedSetPartitions(tuple(self.set),block_sizes)._fast_iterator():
     375            lb = tuple(_blocks(b[i],exp[i][0],exp[i][1]) for i in xrange(m))
     376            for x in itertools.imap(_union,itertools.product(*lb)):
     377                yield x
     378
     379class SetPartitions_setparts(CombinatorialClass):
     380    r"""
     381    Unordered set partition with specified parts
     382    """
     383    element_class = Set_object_enumerated
     384    def __init__(self, set, parts):
     385        """
     386        TESTS::
     387       
     388            sage: S = SetPartitions(4, [[3,1],[2,2]])
     389            sage: S == loads(dumps(S))
     390            True
     391            sage: TestSuite(S).run()
     392        """
     393        Parent.__init__(self, category=FiniteEnumeratedSets())
     394        self.set = set
     395        self.parts = parts
     396
     397    def cardinality(self):
     398        r"""
     399        Return the cardinality of self
     400
     401        EXAMPLES::
     402
     403            sage: S = SetPartitions(4,2)
     404            sage: len(S.list()) == S.cardinality()
     405            True
     406        """
     407        return sum(SetPartitions_setpart(self.set,p).cardinality() for p in self.parts)
     408
     409    def _repr_(self):
     410        """
     411        TESTS::
     412       
     413            sage: repr(SetPartitions(4, [[4],[2,2]]))
     414            'Set partitions of [1, 2, 3, 4] with sizes in {[4], [2, 2]}'
    164415        """
    165416        return "Set partitions of %s with sizes in %s"%(self.set, self.parts)
    166417
     
    172423            sage: all([sp in S for sp in S])
    173424            True
    174425        """
    175         #x must be a set
    176         if not is_Set(x):
    177             return False
     426        return any(x in SetPartitions_setpart(self.set,p) for p in self.parts)
    178427
    179         #Make sure that the number of elements match up
    180         if sum(map(len, x)) != len(self.set):
    181             return False
     428    def _fast_iterator(self):
     429        r"""
     430        Fast iterator which returns list of tuples instead of list of sets
     431        (faster iteration)
    182432
    183         #Check to make sure each element of x
    184         #is a set
    185         u = Set([])
    186         for s in x:
    187             if not is_Set(s):
    188                 return False
    189             u = u.union(s)
     433        EXAMPLES::
    190434
    191         #Make sure that the union of all the
    192         #sets is the original set
    193         if u != Set(self.set):
    194             return False
     435            sage: S = SetPartitions(5,[2,2,1])
     436            sage: for p in S._fast_iterator(): print p
     437            ((1,), (2, 3), (4, 5))
     438            ((1,), (2, 4), (3, 5))
     439            ((1,), (2, 5), (3, 4))
     440            ((2,), (1, 3), (4, 5))
     441            ((2,), (1, 4), (3, 5))
     442            ((2,), (1, 5), (3, 4))
     443            ((3,), (1, 2), (4, 5))
     444            ((3,), (1, 4), (2, 5))
     445            ((3,), (1, 5), (2, 4))
     446            ((4,), (1, 2), (3, 5))
     447            ((4,), (1, 3), (2, 5))
     448            ((4,), (1, 5), (2, 3))
     449            ((5,), (1, 2), (3, 4))
     450            ((5,), (1, 3), (2, 4))
     451            ((5,), (1, 4), (2, 3))
     452        """
     453        for p in self.parts:
     454            for sp in SetPartitions_setpart(self.set,p)._fast_iterator():
     455                yield sp
    195456
    196         return True
    197 
    198     def cardinality(self):
    199         """
    200         Returns the number of set partitions of set. This number is given
    201         by the n-th Bell number where n is the number of elements in the
    202         set.
    203        
    204         If a partition or partition length is specified, then count will
    205         generate all of the set partitions.
    206        
    207         EXAMPLES::
    208        
    209             sage: SetPartitions([1,2,3,4]).cardinality()
    210             15
    211             sage: SetPartitions(3).cardinality()
    212             5
    213             sage: SetPartitions(3,2).cardinality()
    214             3
    215             sage: SetPartitions([]).cardinality()
    216             1
    217         """
    218         return len(self.list())
    219 
    220 
    221     def _iterator_part(self, part):
    222         """
    223         Returns an iterator for the set partitions with block sizes
    224         corresponding to the partition part.
    225        
    226         INPUT:
    227        
    228        
    229         -  ``part`` - a Partition object
    230        
    231        
    232         EXAMPLES::
    233        
    234             sage: S = SetPartitions(3)
    235             sage: it = S._iterator_part(Partition([1,1,1]))
    236             sage: list(sorted(map(list, it.next())))
    237             [[1], [2], [3]]
    238             sage: S21 = SetPartitions(3,Partition([2,1]))
    239             sage: len(list(S._iterator_part(Partition([2,1])))) == S21.cardinality()
    240             True
    241         """
    242         set = self.set
    243 
    244         nonzero = []
    245         expo = [0]+part.to_exp()
    246 
    247         for i in range(len(expo)):
    248             if expo[i] != 0:
    249                 nonzero.append([i, expo[i]])
    250 
    251         taillesblocs = map(lambda x: (x[0])*(x[1]), nonzero)
    252 
    253         blocs = set_partition_ordered.OrderedSetPartitions(copy.copy(set), taillesblocs).list()
    254 
    255         for b in blocs:
    256             lb = [ IterableFunctionCall(_listbloc, nonzero[i][0], nonzero[i][1], b[i]) for i in range(len(nonzero)) ]
    257             for x in itertools.imap(lambda x: _union(x), CartesianProduct( *lb )):
    258                 yield x
    259 
    260 
    261    
    262457    def __iter__(self):
    263458        """
    264459        An iterator for all the set partitions of the set.
    265        
    266         EXAMPLES::
     460
     461        TESTS::
    267462       
    268463            sage: SetPartitions(3).list()
    269464            [{{1, 2, 3}}, {{2, 3}, {1}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{2}, {3}, {1}}]
    270465        """
    271         for p in self.parts:
    272             for sp in self._iterator_part(p):
    273                 yield sp
    274 
     466        return (Set(map(Set,p)) for p in self._fast_iterator())
    275467
    276468class SetPartitions_setn(SetPartitions_setparts):
     469    r"""
     470    Set partitions with specified number of parts
     471    """
    277472    def __init__(self, set, n):
    278473        """
    279474        TESTS::
     
    281476            sage: S = SetPartitions(5, 3)
    282477            sage: S == loads(dumps(S))
    283478            True
     479            sage: TestSuite(S).run()
    284480        """
    285481        self.n = n
    286482        SetPartitions_setparts.__init__(self, set, partition.Partitions(len(set), length=n).list())
    287483
    288     def __repr__(self):
     484    def _repr_(self):
    289485        """
    290486        TESTS::
    291487       
     
    294490        """
    295491        return "Set partitions of %s with %s parts"%(self.set,self.n)
    296492
     493    def __contains__(self,x):
     494        r"""
     495        TESTS::
     496
     497            sage: S = SetPartitions(5,2)
     498            sage: S32 = SetPartitions(5,[3,2])
     499            sage: all(sp in S for sp in S32)
     500            True
     501            sage: S221 = SetPartitions(5,[2,2,1])
     502            sage: any(sp in S for sp in S221)
     503            False
     504        """
     505        return (x in SetPartitions_set(self.set)) and (len(x) == self.n)
     506
    297507    def cardinality(self):
    298508        """
    299509        The Stirling number of the second kind is the number of partitions
     
    307517            25
    308518        """
    309519        return stirling_number2(len(self.set), self.n)
    310    
     520
    311521class SetPartitions_set(SetPartitions_setparts):
     522    r"""
     523    Set partitions of a set
     524    """
    312525    def __init__(self, set):
    313526        """
    314527        TESTS::
     
    316529            sage: S = SetPartitions([1,2,3])
    317530            sage: S == loads(dumps(S))
    318531            True
     532            sage: TestSuite(S).run()
    319533        """
    320534        SetPartitions_setparts.__init__(self, set, partition.Partitions(len(set)))
    321535
    322     def __repr__(self):
     536    def _repr_(self):
    323537        """
    324538        TESTS::
    325539       
     
    328542        """
    329543        return "Set partitions of %s"%(self.set)
    330544
     545    def __contains__(self, x):
     546        """
     547        TESTS::
     548       
     549            sage: S = SetPartitions(5)
     550            sage: S32 = SetPartitions(5, [3,2])
     551            sage: all(sp in S for sp in S32)
     552            True
     553            sage: S311 = SetPartitions(5,[3,1,1])
     554            sage: all(sp in S for sp in S32)
     555            True
     556            sage: S31 = SetPartitions(4,[3,1])
     557            sage: any(sp in S for sp in S31)
     558            False
     559        """
     560        part = map(len,x)
     561
     562        #Make sure that the number of elements match up
     563        if sum(part) != len(self.set):
     564            return False
     565
     566        #Check to make sure each element of x
     567        #is a set
     568        u = set([])
     569        for s in x:
     570            u = u.union(s)
     571
     572        #Make sure that the union of all the
     573        #sets is the original set
     574        if u != set(self.set):
     575            return False
     576
     577        return True
     578
    331579    def cardinality(self):
    332580        """
    333581        EXAMPLES::
     
    339587        """
    340588        return bell_number(len(self.set))
    341589
     590#
     591# Functions
     592#
    342593
     594def _blocks(t, m, r):
     595    r"""
     596    Recrusive function that returns the list of partitions of the tuple t into r
     597    parts of size m
    343598
    344 def _listbloc(n, nbrepets, listint=None):
     599    EXAMPLES::
     600
     601        sage: from sage.combinat.set_partition import _blocks
     602        sage: for b in _blocks((0,1,2,3,4,5), 3, 2): print b
     603        [(0, 1, 2), (3, 4, 5)]
     604        [(0, 1, 3), (2, 4, 5)]
     605        [(0, 1, 4), (2, 3, 5)]
     606        [(0, 1, 5), (2, 3, 4)]
     607        [(0, 2, 3), (1, 4, 5)]
     608        [(0, 2, 4), (1, 3, 5)]
     609        [(0, 2, 5), (1, 3, 4)]
     610        [(0, 3, 4), (1, 2, 5)]
     611        [(0, 3, 5), (1, 2, 4)]
     612        [(0, 4, 5), (1, 2, 3)]
    345613    """
    346     listbloc decomposes a set of n\*nbrepets integers (the list
    347     listint) in nbrepets parts.
     614    if r == 1:
     615        yield [t]
     616    elif m == 1:
     617        yield [(i,) for i in t]
     618    else:
     619        smallest = (t[0],)
     620        new_t = t[1:]
    348621   
    349     It is used in the algorithm to generate all set partitions.
    350    
    351     Not to be called by the user.
    352    
    353     EXAMPLES::
    354    
    355         sage: list(sage.combinat.set_partition._listbloc(2,1))
    356         [{{1, 2}}]
    357         sage: l = [Set([Set([3, 4]), Set([1, 2])]), Set([Set([2, 4]), Set([1, 3])]), Set([Set([2, 3]), Set([1, 4])])]
    358         sage: list(sage.combinat.set_partition._listbloc(2,2,[1,2,3,4])) == l
    359         True
    360     """
    361     if isinstance(listint, (int, sage.rings.integer.Integer)) or listint is None:
    362         listint = Set(range(1,n+1))
    363 
    364 
    365     if nbrepets == 1:
    366         yield Set([listint])
    367         return
    368    
    369     l = __builtin__.list(listint)
    370     l.sort()
    371     smallest = Set(l[:1])
    372     new_listint = Set(l[1:])
    373    
    374     f = lambda u, v: u.union(_set_union([smallest,v]))
    375    
    376     for ssens in subset.Subsets(new_listint, n-1):
    377         for z in _listbloc(n, nbrepets-1, new_listint-ssens):
    378             yield f(z,ssens)
    379 
    380 
     622        for block1 in Subwords(new_t,m-1): # generate the first by Subwords
     623            block1 = smallest + block1
     624            b1 = set(block1)
     625            comp = tuple(itertools.ifilter(lambda x: x not in b1,new_t))
     626            for z in _blocks(comp,m,r-1):  # generate the rest recursively
     627                yield [block1] + z
    381628
    382629def _union(s):
     630    r"""
     631    Return the tuple which is the union of the tuples in s
     632
     633    EXAMPLES
     634
     635        sage: from sage.combinat.set_partition import _union
     636        sage: _union([(0,1),('two','three','four'),(5,6)])
     637        (0, 1, 'two', 'three', 'four', 5, 6)
    383638    """
    384     TESTS::
    385    
    386         sage: s = Set([ Set([1,2]), Set([3,4]) ])
    387         sage: sage.combinat.set_partition._union(s)
    388         {1, 2, 3, 4}
    389     """
    390     result = Set([])
     639    result = []
    391640    for ss in s:
    392         result = result.union(ss)
    393     return result
     641        result.extend(ss)
     642    return tuple(result)
    394643
    395644def _set_union(s):
    396645    """
     
    440689
    441690    return res
    442691
    443    
    444692def standard_form(sp):
    445693    """
    446694    Returns the set partition as a list of lists.
     
    448696    EXAMPLES::
    449697   
    450698        sage: map(sage.combinat.set_partition.standard_form, SetPartitions(4, [2,2]))
    451         [[[3, 4], [1, 2]], [[2, 4], [1, 3]], [[2, 3], [1, 4]]]
     699        [[[1, 2], [3, 4]], [[1, 3], [2, 4]], [[2, 3], [1, 4]]]
    452700    """
    453701
    454702    return [__builtin__.list(x) for x in sp]
    455703
    456 
    457 def less(s, t):
     704def less(s, t, check=True):
    458705    """
    459706    Returns True if s < t otherwise it returns False.
    460707   
     
    476723        sage: sage.combinat.set_partition.less(z[0], z[0])
    477724        False
    478725    """
    479 
    480     if _union(s) != _union(t):
    481         raise ValueError, "cannot compare partitions of different sets"
     726    if check:
     727        if sorted(_union(s)) != sorted(_union(t)):
     728            raise ValueError, "cannot compare partitions of different sets"
    482729
    483730    if s == t:
    484731        return False
  • sage/combinat/set_partition_ordered.py

    diff --git a/sage/combinat/set_partition_ordered.py b/sage/combinat/set_partition_ordered.py
    a b  
    7676#                  http://www.gnu.org/licenses/
    7777#*****************************************************************************
    7878
     79import itertools
     80from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     81from sage.structure.parent import Parent
     82from sage.sets.set import Set_object_enumerated
     83
     84import itertools
     85import __builtin__
     86
    7987from sage.rings.arith import factorial
    8088import sage.combinat.composition as composition
    8189from sage.combinat.words.word import Word
     
    8492from sage.combinat.combinat import stirling_number2
    8593from sage.sets.set import Set, is_Set
    8694from combinat import CombinatorialClass
     95
    8796from sage.misc.misc import prod
    8897
    8998
     
    162171       
    163172
    164173class OrderedSetPartitions_s(CombinatorialClass):
     174    r"""
     175    Ordered set partitions of a set
     176    """
     177    element_class = __builtin__.list
    165178    def __init__(self, s):
    166179        """
    167180        TESTS::
     
    169182            sage: OS = OrderedSetPartitions([1,2,3,4])
    170183            sage: OS == loads(dumps(OS))
    171184            True
     185            sage: TestSuite(OS).run()
    172186        """
     187        Parent.__init__(self, category=FiniteEnumeratedSets())
    173188        self.s = s
    174189
    175     def __repr__(self):
     190    def _repr_(self):
    176191        """
    177192        TESTS::
    178193       
     
    190205            True
    191206        """
    192207        #x must be a list
    193         if not isinstance(x, list):
     208        if not isinstance(x, __builtin__.list):
    194209            return False
    195210
    196211        #The total number of elements in the list
     
    232247            sage: OrderedSetPartitions(5).cardinality()
    233248            541
    234249        """
    235         set = self.s
    236         return sum([factorial(k)*stirling_number2(len(set),k) for k in range(len(set)+1)])
     250        k = len(self.s)
     251        return sum(factorial(i)*stirling_number2(k,i) for i in range(k+1))
    237252
     253    def _fast_iterator(self):
     254        r"""
     255        Iterator over tuples instead of Set (faster iteration)
    238256
     257        EXAMPLES::
     258
     259            sage: S=OrderedSetPartitions([1,2,3])
     260            sage: for p in S._fast_iterator(): print p
     261            [(1,), (2,), (3,)]
     262            [(1,), (3,), (2,)]
     263            [(2,), (1,), (3,)]
     264            [(3,), (1,), (2,)]
     265            [(2,), (3,), (1,)]
     266            [(3,), (2,), (1,)]
     267            [(1,), (2, 3)]
     268            [(2,), (1, 3)]
     269            [(3,), (1, 2)]
     270            [(1, 2), (3,)]
     271            [(1, 3), (2,)]
     272            [(2, 3), (1,)]
     273            [(1, 2, 3)]
     274        """
     275        for x in composition.Compositions(len(self.s)):
     276            for z in OrderedSetPartitions(self.s,x)._fast_iterator():
     277                yield z
    239278
    240279    def __iter__(self):
    241         """
     280        r"""
    242281        EXAMPLES::
    243282       
    244283            sage: [ p for p in OrderedSetPartitions([1,2,3]) ]
     
    256295             [{2, 3}, {1}],
    257296             [{1, 2, 3}]]
    258297        """
    259         for x in composition.Compositions(len(self.s)):
    260             for z in OrderedSetPartitions(self.s,x):
    261                 yield z
     298        return itertools.imap(lambda x: map(Set,x), self._fast_iterator())
    262299
    263 
    264 class OrderedSetPartitions_sn(CombinatorialClass):
     300class OrderedSetPartitions_sn(OrderedSetPartitions_s):
     301    r"""
     302    Ordered partitions of a set with given number of atoms
     303    """
    265304    def __init__(self, s, n):
    266305        """
    267306        TESTS::
     
    269308            sage: OS = OrderedSetPartitions([1,2,3,4], 2)
    270309            sage: OS == loads(dumps(OS))
    271310            True
     311            sage: TestSuite(OS).run()
    272312        """
     313        Parent.__init__(self, category=FiniteEnumeratedSets())
    273314        self.s = s
    274315        self.n = n
    275316
     
    285326            sage: len(filter(lambda x: x in OS, OrderedSetPartitions([1,2,3,4])))
    286327            14
    287328        """
    288         return x in OrderedSetPartitions_s(self.s) and len(x) == self.n
     329        return OrderedSetPartitions_s.__contains__(self,x) and len(x) == self.n
    289330
    290     def __repr__(self):
     331    def _repr_(self):
    291332        """
    292333        TESTS::
    293334       
     
    305346            sage: OrderedSetPartitions(4,1).cardinality()
    306347            1
    307348        """
    308         set = self.s
    309349        n   = self.n
    310         return factorial(n)*stirling_number2(len(set),n)
     350        return factorial(n)*stirling_number2(len(self.s),n)
     351
     352    def _fast_iterator(self):
     353        r"""
     354        Iterator over tuples instead of Set (faster iteration)
     355
     356        EXAMPLES::
     357
     358            sage: S=OrderedSetPartitions([1,2,3],2)
     359            sage: for p in S._fast_iterator(): print p
     360            [(1, 2), (3,)]
     361            [(1, 3), (2,)]
     362            [(2, 3), (1,)]
     363            [(1,), (2, 3)]
     364            [(2,), (1, 3)]
     365            [(3,), (1, 2)]
     366
     367        """
     368        for x in composition.Compositions(len(self.s),length=self.n):
     369            for z in OrderedSetPartitions_scomp(self.s,x)._fast_iterator():
     370                yield z
    311371
    312372    def __iter__(self):
    313373        """
     
    329389             [{3}, {1, 2, 4}],
    330390             [{4}, {1, 2, 3}]]
    331391        """
    332         for x in composition.Compositions(len(self.s),length=self.n):
    333             for z in OrderedSetPartitions_scomp(self.s,x):
    334                 yield z
     392        return itertools.imap(lambda x: map(Set,x), self._fast_iterator())
    335393
    336 class OrderedSetPartitions_scomp(CombinatorialClass):
     394class OrderedSetPartitions_scomp(OrderedSetPartitions_s):
     395    r"""
     396    Ordered partitions of a set with given composition
     397    """
    337398    def __init__(self, s, comp):
    338399        """
    339400        TESTS::
     
    341402            sage: OS = OrderedSetPartitions([1,2,3,4], [2,1,1])
    342403            sage: OS == loads(dumps(OS))
    343404            True
     405            sage: TestSuite(OS).run()
    344406        """
     407        Parent.__init__(self, category=FiniteEnumeratedSets())
    345408        self.s = s
    346409        self.c = composition.Composition(comp)
    347410
     
    366429            sage: len(filter(lambda x: x in OS, OrderedSetPartitions([1,2,3,4])))
    367430            12
    368431        """
    369         return x in OrderedSetPartitions_s(self.s) and map(len, x) == self.c
     432        return OrderedSetPartitions_s.__contains__(self,x) and map(len, x) == self.c
    370433   
    371434    def cardinality(self):
    372435        """
     
    383446            sage: OrderedSetPartitions(5, [2,0,3]).cardinality()
    384447            10
    385448        """
    386         return factorial(len(self.s))/prod([factorial(i) for i in self.c])
     449        return factorial(len(self.s))//prod([factorial(i) for i in self.c])
    387450   
    388451    def __iter__(self):
    389452        """
     
    403466             [{2, 4}, {3}, {1}],
    404467             [{3, 4}, {2}, {1}]]
    405468        """
     469        return (map(Set,x) for x in self._fast_iterator())
     470
     471    def _fast_iterator(self):
     472        r"""
     473        Iterator which returns tuple instead of sets
     474       
     475        EXAMPLES::
     476
     477            sage: S = OrderedSetPartitions(5,[2,2,1])
     478            sage: for s in S._fast_iterator(): print s
     479            [(1, 2), (3, 4), (5,)]
     480            [(1, 2), (3, 5), (4,)]
     481            [(1, 2), (4, 5), (3,)]
     482            [(1, 3), (2, 4), (5,)]
     483            [(1, 3), (2, 5), (4,)]
     484            [(1, 4), (2, 3), (5,)]
     485            [(1, 5), (2, 3), (4,)]
     486            [(1, 4), (2, 5), (3,)]
     487            [(1, 5), (2, 4), (3,)]
     488            [(1, 3), (4, 5), (2,)]
     489            [(1, 4), (3, 5), (2,)]
     490            [(1, 5), (3, 4), (2,)]
     491            [(2, 3), (1, 4), (5,)]
     492            [(2, 3), (1, 5), (4,)]
     493            [(2, 4), (1, 3), (5,)]
     494            [(2, 5), (1, 3), (4,)]
     495            [(2, 4), (1, 5), (3,)]
     496            [(2, 5), (1, 4), (3,)]
     497            [(3, 4), (1, 2), (5,)]
     498            [(3, 5), (1, 2), (4,)]
     499            [(4, 5), (1, 2), (3,)]
     500            [(3, 4), (1, 5), (2,)]
     501            [(3, 5), (1, 4), (2,)]
     502            [(4, 5), (1, 3), (2,)]
     503            [(2, 3), (4, 5), (1,)]
     504            [(2, 4), (3, 5), (1,)]
     505            [(2, 5), (3, 4), (1,)]
     506            [(3, 4), (2, 5), (1,)]
     507            [(3, 5), (2, 4), (1,)]
     508            [(4, 5), (2, 3), (1,)]
     509        """
    406510        comp = self.c
    407511        lset = [x for x in self.s]
    408         l = len(self.c)
    409         dcomp = [-1] + comp.descents(final_descent=True)
     512        r = range(1,len(lset))
     513        l = len(comp)
     514        dcomp = [0] + [i+1 for i in comp.descents(final_descent=True)]
    410515
    411516        p = []
    412         for j in range(l):
    413             p += [j+1]*comp[j]
     517        for j in xrange(l):
     518            p.extend([j+1]*comp[j])
    414519
    415520        for x in permutation.Permutations(p):
    416             res = permutation.Permutation_class(range(1,len(lset))) * Word(x).standard_permutation().inverse()
    417             res =[lset[x-1] for x in res]
    418             yield [ Set( res[dcomp[i]+1:dcomp[i+1]+1] ) for i in range(l)]
    419            
     521            res =[lset[y-1] for y in Word(x,length=len(p),datatype='list').standard_permutation().inverse()]
     522            yield [tuple(res[dcomp[i]:dcomp[i+1]]) for i in xrange(l)]
    420523
    421 
    422 
  • sage/combinat/split_nk.py

    diff --git a/sage/combinat/split_nk.py b/sage/combinat/split_nk.py
    a b  
    11"""
    22Low-level splits
     3
     4Authors:
     5
     6- Mike Hansen (2007): original version
     7
     8- Vincent Delecroix (2011): improvements
    39"""
    410#*****************************************************************************
    511#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     
    3238        sage: S = SplitNK(5,2); S
    3339        Splits of {0, ..., 4} into a set of size 2 and one of size 3
    3440        sage: S.first()
    35         [[0, 1], [2, 3, 4]]
     41        [(0, 1), (2, 3, 4)]
    3642        sage: S.last()
    37         [[3, 4], [0, 1, 2]]
     43        [(3, 4), (0, 1, 2)]
    3844        sage: S.list()
    39         [[[0, 1], [2, 3, 4]],
    40          [[0, 2], [1, 3, 4]],
    41          [[0, 3], [1, 2, 4]],
    42          [[0, 4], [1, 2, 3]],
    43          [[1, 2], [0, 3, 4]],
    44          [[1, 3], [0, 2, 4]],
    45          [[1, 4], [0, 2, 3]],
    46          [[2, 3], [0, 1, 4]],
    47          [[2, 4], [0, 1, 3]],
    48          [[3, 4], [0, 1, 2]]]
     45        [[(0, 1), (2, 3, 4)],
     46         [(0, 2), (1, 3, 4)],
     47         [(0, 3), (1, 2, 4)],
     48         [(0, 4), (1, 2, 3)],
     49         [(1, 2), (0, 3, 4)],
     50         [(1, 3), (0, 2, 4)],
     51         [(1, 4), (0, 2, 3)],
     52         [(2, 3), (0, 1, 4)],
     53         [(2, 4), (0, 1, 3)],
     54         [(3, 4), (0, 1, 2)]]
    4955    """
    5056    return SplitNK_nk(n,k)
    5157
     
    6268        self._n = n
    6369        self._k = k
    6470
    65     def __repr__(self):
     71    def _repr_(self):
    6672        """
    6773        TESTS::
    6874       
    6975            sage: from sage.combinat.split_nk import SplitNK
    70             sage: repr(SplitNK(5,2))
     76            sage: repr(SplitNK(5,2)) #indirect doctest
    7177            'Splits of {0, ..., 4} into a set of size 2 and one of size 3'
    7278        """
    7379        return "Splits of {0, ..., %s} into a set of size %s and one of size %s"%(self._n-1, self._k, self._n-self._k)
     
    9399        EXAMPLES::
    94100       
    95101            sage: from sage.combinat.split_nk import SplitNK
    96             sage: [c for c in SplitNK(5,2)]
    97             [[[0, 1], [2, 3, 4]],
    98              [[0, 2], [1, 3, 4]],
    99              [[0, 3], [1, 2, 4]],
    100              [[0, 4], [1, 2, 3]],
    101              [[1, 2], [0, 3, 4]],
    102              [[1, 3], [0, 2, 4]],
    103              [[1, 4], [0, 2, 3]],
    104              [[2, 3], [0, 1, 4]],
    105              [[2, 4], [0, 1, 3]],
    106              [[3, 4], [0, 1, 2]]]
     102            sage: for c in SplitNK(5,2): print c
     103            [(0, 1), (2, 3, 4)]
     104            [(0, 2), (1, 3, 4)]
     105            [(0, 3), (1, 2, 4)]
     106            [(0, 4), (1, 2, 3)]
     107            [(1, 2), (0, 3, 4)]
     108            [(1, 3), (0, 2, 4)]
     109            [(1, 4), (0, 2, 3)]
     110            [(2, 3), (0, 1, 4)]
     111            [(2, 4), (0, 1, 3)]
     112            [(3, 4), (0, 1, 2)]
    107113        """
    108114        range_n = range(self._n)
    109115        for kset in choose_nk.ChooseNK(self._n,self._k):
    110             yield [ kset, filter(lambda x: x not in kset, range_n) ]
    111 
     116            yield [ kset, tuple(filter(lambda x: x not in kset, range_n))]
    112117
    113118    def random_element(self):
    114119        """
     
    119124       
    120125            sage: from sage.combinat.split_nk import SplitNK
    121126            sage: SplitNK(5,2).random_element()
    122             [[0, 2], [1, 3, 4]]
     127            [(0, 2), (1, 3, 4)]
    123128        """
    124129        r = rnd.sample(xrange(self._n),self._n)
    125130        r0 = r[:self._k]
    126131        r1 = r[self._k:]
    127132        r0.sort()
    128133        r1.sort()
    129         return [ r0, r1 ]
     134        return [ tuple(r0), tuple(r1) ]
  • sage/combinat/subset.py

    diff --git a/sage/combinat/subset.py b/sage/combinat/subset.py
    a b  
    11r"""
    22Subsets
    33
    4 The combinatorial class of the subsets of a finite set. The set can
    5 be given as a list or a Set or else as an integer `n` which encodes the set
    6 `\{1,2,...,n\}`. See :class:`Subsets` for more information and examples.
     4The set of subsets of a finite set. The set can be given as a list or a Set
     5or else as an integer `n` which encodes the set `\{1,2,...,n\}`.
     6See :class:`Subsets` for more information and examples.
    77
    88AUTHORS:
    99
     
    1111
    1212- Florent Hivert (2009/02/06): doc improvements + new methods
    1313
     14- Vincent Delecroix (2011/03/10): use iterator from itertools, implement basic
     15  random uniform generation
    1416"""
    1517#*****************************************************************************
    1618#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
     
    3436import sage.combinat.choose_nk as choose_nk
    3537import sage.misc.prandom as rnd
    3638import __builtin__
     39import copy
    3740import itertools
    38 from combinat import CombinatorialClass
     41from sage.structure.parent import Parent
     42from sage.structure.element import Element
    3943from sage.sets.set import Set_object_enumerated
    4044from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
    41 
     45from combinat import CombinatorialClass
    4246
    4347def Subsets(s, k=None, submultiset=False):
    4448    """
    45     Returns the combinatorial class of the subsets of the finite set
    46     s. The set can be given as a list, Set or any iterable convertible
    47     to a set. It can alternatively be given a non-negative integer `n`
    48     which encode the set `\{1,2,\dots,n\}` (i.e. the Sage
    49     ``range(1,s+1)``).
     49    Returns the combinatorial class of the subsets of the finite set ``s``. The
     50    set can be given as a list, Set or any iterable convertible to a set. It can
     51    alternatively be given a non-negative integer `n` which encode the set
     52    `\{1,2,\dots,n\}` (i.e. the Sage ``range(1,s+1)``).
    5053
    51     A second optional parameter k can be given. In this case, Subsets returns
    52     the combinatorial class of subsets of s of size k.
     54    A second optional parameter ``k`` can be given. In this case, Subsets
     55    returns the combinatorial class of subsets of ``s`` of size ``k``.
    5356
    5457    Finally the option ``submultiset`` allows one to deal with sets with
    5558    repeated elements usually called multisets.
     
    9699        [['a', 'a'], ['a', 'b'], ['b', 'b']]
    97100    """
    98101    if k is not None:
    99         k=Integer(k)
     102        k = Integer(k)
    100103
    101104    if isinstance(s, (int, Integer)):
    102105        if s < 0:
     
    117120        else:
    118121            return Subsets_sk(s, k)
    119122
    120 
    121 
    122 
    123 
    124123class Subsets_s(CombinatorialClass):
     124    element_class = Set_object_enumerated
    125125    def __init__(self, s):
    126126        """
    127127        TESTS::
     
    141141            sage: S = sage.sets.set.Set_object_enumerated([1,2])
    142142            sage: TestSuite(S).run()         # todo: not implemented
    143143        """
    144         CombinatorialClass.__init__(self, category=FiniteEnumeratedSets())
     144        Parent.__init__(self, category=FiniteEnumeratedSets())
    145145        self.s = Set(s)
    146146
    147     def __repr__(self):
     147    def _repr_(self):
    148148        """
    149149        TESTS::
    150150
    151             sage: repr(Subsets([1,2,3]))
     151            sage: repr(Subsets([1,2,3])) #indirect doctest
    152152            'Subsets of {1, 2, 3}'
    153153        """
    154154        return "Subsets of %s"%self.s
     
    186186            sage: Subsets(3).cardinality()
    187187            8
    188188        """
    189         return Integer(2**len(self.s))
     189        return Integer(2**self.s.cardinality())
    190190
    191191    def first(self):
    192192        """
     
    216216        """
    217217        return self.s
    218218
    219 
    220219    def __iter__(self):
    221220        """
    222221        Iterates through the subsets of s.
     
    231230            [{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}]
    232231
    233232        """
    234         lset = __builtin__.list(self.s)
    235         #We use the iterator for the subwords of range(len(self.s))
    236         ind_set = lambda index_list: Set([lset[i] for i in index_list])
    237         it = itertools.imap(ind_set, subword.Subwords(range(len(lset))))
    238         for sub in it:
    239             yield sub
     233        for k in xrange(self.s.cardinality()+1):
     234            for ss in Subsets_sk(self.s, k):
     235                yield ss
    240236
    241237    def random_element(self):
    242238        """
     
    251247            {5}
    252248        """
    253249        lset = __builtin__.list(self.s)
    254         n = len(self.s)
    255250        return Set(filter(lambda x: rnd.randint(0,1), lset))
    256251
    257252    def rank(self, sub):
     
    311306                else:
    312307                    return Set([lset[i] for i in choose_nk.from_rank(r, n, k)])
    313308
     309    def _element_constructor(self,X):
     310        return Set(X)
     311
    314312    def _an_element_(self):
    315313        """
    316314        Returns an example of subset.
     
    326324        """
    327325        return self.unrank(self.cardinality() // 2)
    328326
    329     def _element_constructor_(self, x):
    330         """
    331         TESTS::
    332 
    333             sage: S3 = Subsets(3); S3([1,2])
    334             {1, 2}
    335             sage: S3([0,1,2])
    336             Traceback (most recent call last):
    337             ...
    338             ValueError: [0, 1, 2] not in Subsets of {1, 2, 3}
    339         """
    340         return Set(x)
    341 
    342     element_class = Set_object_enumerated
    343 
    344 
    345 class Subsets_sk(CombinatorialClass):
     327#TODO: remove inheritance
     328class Subsets_sk(Subsets_s):
    346329    def __init__(self, s, k):
    347330        """
    348331        TESTS::
     
    359342            sage: S = Subsets(3,2)
    360343            sage: TestSuite(S).run(skip=["_test_elements"])
    361344        """
    362         CombinatorialClass.__init__(self, category=FiniteEnumeratedSets())
    363         self.s = Set(s)
    364         self.k = k
     345        Subsets_s.__init__(self,s)
     346        self._k = Integer(k)
     347        if self._k < 0:
     348            raise ValueError, "the integer k (=%d) should be non-negative" %k
    365349
    366     def __repr__(self):
     350    def _repr_(self):
    367351        """
    368352        TESTS::
    369353
    370             sage: repr(Subsets(3,2))
     354            sage: repr(Subsets(3,2)) #indirect doctest
    371355            'Subsets of {1, 2, 3} of size 2'
    372356        """
    373         return "Subsets of %s of size %s"%(self.s, self.k)
     357        return Subsets_s._repr_(self) + " of size %s"%(self._k)
    374358
    375359    def __contains__(self, value):
    376360        """
     
    383367            sage: Set([]) in S
    384368            False
    385369        """
    386         value = Set(value)
    387         if len(value) != self.k:
    388             return False
    389         for v in value:
    390             if not v in self.s:
    391                 return False
    392         return True
     370        return len(value) == self._k and Subsets_s.__contains__(self,value)
    393371
    394372    def cardinality(self):
    395373        """
     
    412390            sage: Subsets(3,4).cardinality()
    413391            0
    414392        """
    415         if self.k not in range(len(self.s)+1):
    416             return 0
    417         else:
    418             return binomial(len(self.s),self.k)
    419 
     393        if self._k > self.s.cardinality():
     394            return Integer(0)
     395        return binomial(self.s.cardinality(),self._k)
    420396
    421397    def first(self):
    422398        """
     
    432408            {1, 2}
    433409            sage: Subsets(3,4).first()
    434410        """
    435         if self.k not in range(len(self.s)+1):
     411        if self._k < 0 or self._k > len(self.s):
    436412            return None
    437413        else:
    438             return Set(__builtin__.list(self.s)[:self.k])
    439 
    440 
     414            return Set(__builtin__.list(self.s)[:self._k])
    441415
    442416    def last(self):
    443417        """
     
    453427            {2, 3}
    454428            sage: Subsets(3,4).last()
    455429        """
    456         if self.k not in range(len(self.s)+1):
     430        if self._k not in range(len(self.s)+1):
    457431            return None
    458432        else:
    459             return Set(__builtin__.list(self.s)[-self.k:])
     433            return Set(__builtin__.list(self.s)[-self._k:])
    460434
     435    def _fast_iterator(self):
     436        r"""
     437        Iterate through the subsets of size k if s.
    461438
     439        Beware that this function yield tuples and not sets. If you need sets
     440        use __iter__
    462441
     442        EXAMPLES::
     443
     444            sage: list(Subsets(range(3), 2)._fast_iterator())
     445            [(0, 1), (0, 2), (1, 2)]
     446        """
     447        return itertools.combinations(self.s,self._k)
    463448
    464449    def __iter__(self):
    465450        """
     
    467452
    468453        EXAMPLES::
    469454
    470             sage: [sub for sub in Subsets(Set([1,2,3]), 2)]
     455            sage: Subsets(Set([1,2,3]), 2).list()
    471456            [{1, 2}, {1, 3}, {2, 3}]
    472             sage: [sub for sub in Subsets([1,2,3,3], 2)]
     457            sage: Subsets([1,2,3,3], 2).list()
    473458            [{1, 2}, {1, 3}, {2, 3}]
    474             sage: [sub for sub in Subsets(3,2)]
     459            sage: Subsets(3,2).list()
    475460            [{1, 2}, {1, 3}, {2, 3}]
     461            sage: Subsets(3,3).list()
     462            [{1, 2, 3}]
    476463        """
    477         if self.k not in range(len(self.s)+1):
    478             return
    479 
    480         lset = __builtin__.list(self.s)
    481         #We use the iterator for the subwords of range(len(self.s))
    482         ind_set = lambda index_list: Set([lset[i] for i in index_list])
    483         for sub in choose_nk.ChooseNK(len(lset),self.k):
    484             yield ind_set(sub)
    485 
    486 
     464        return itertools.imap(Set_object_enumerated, self._fast_iterator())
    487465
    488466    def random_element(self):
    489467        """
     
    500478        lset = __builtin__.list(self.s)
    501479        n = len(self.s)
    502480
    503         if self.k not in range(len(self.s)+1):
     481        if self._k not in range(len(self.s)+1):
    504482            return None
    505483        else:
    506             return Set([lset[i] for i in choose_nk.ChooseNK(n, self.k).random_element()])
     484            return Set([lset[i] for i in choose_nk.ChooseNK(n, self._k).random_element()])
    507485
    508486    def rank(self, sub):
    509487        """
     
    531509        n = len(self.s)
    532510        r = 0
    533511
    534         if self.k not in range(len(self.s)+1):
     512        if self._k not in range(len(self.s)+1):
    535513            return None
    536         elif self.k != len(subset):
     514        elif self._k != len(subset):
    537515            return None
    538516        else:
    539517            return choose_nk.rank(index_list,n)
    540518
    541 
    542519    def unrank(self, r):
    543520        """
    544521        Returns the subset of s that has rank k.
     
    554531        lset = __builtin__.list(self.s)
    555532        n = len(lset)
    556533
    557         if self.k not in range(len(self.s)+1):
     534        if self._k not in range(len(self.s)+1):
    558535            return None
    559536        elif r >= self.cardinality() or r < 0:
    560537            return None
    561538        else:
    562             return Set([lset[i] for i in choose_nk.from_rank(r, n, self.k)])
     539            return Set([lset[i] for i in choose_nk.from_rank(r, n, self._k)])
    563540
    564541    def _an_element_(self):
    565542        """
     
    589566        """
    590567        return Set(x)
    591568
    592     element_class = Set_object_enumerated
    593569
     570#TODO: MultiSet data structure in Sage
     571
     572def dict_to_list(d):
     573    r"""
     574    Return a list whose elements are the elements of i of d repeated with
     575    multiplicity d[i].
     576
     577    EXAMPLES::
     578
     579        sage: from sage.combinat.subset import dict_to_list
     580        sage: dict_to_list({'a':1, 'b':3})
     581        ['a', 'b', 'b', 'b']
     582    """
     583    l = []
     584    for i,j in d.iteritems():
     585        l.extend([i]*j)
     586    return l
     587
     588def list_to_dict(l):
     589    r"""
     590    Return a dictionnary whose keys are the elements of l and values are the
     591    multiplicity they appear in l.
     592
     593    EXAMPLES::
     594
     595        sage: from sage.combinat.subset import list_to_dict
     596        sage: list_to_dict(['a', 'b', 'b', 'b'])
     597        {'a': 1, 'b': 3}
     598    """
     599    d = {}
     600    for elt in l:
     601        if elt not in d:
     602            d[elt] = 0
     603        d[elt] += 1
     604    return d
    594605
    595606class SubMultiset_s(CombinatorialClass):
    596607    """
     
    599610    EXAMPLES::
    600611
    601612        sage: S = Subsets([1,2,2,3], submultiset=True)
    602         sage: S._s
    603         [1, 2, 2, 3]
    604613
    605     The positions of the unique elements in s are stored in::
     614    The positions of the unique elements in s are stored in the attribute
     615    ``._d``::
    606616
    607         sage: S._indices
    608         [0, 1, 3]
    609 
    610     and their multiplicities in::
    611 
    612         sage: S._multiplicities
    613         [1, 2, 1]
    614         sage: Subsets([1,2,3,3], submultiset=True).cardinality()
    615         12
    616         sage: TestSuite(S).run()
     617        sage: S._d
     618        {1: 1, 2: 2, 3: 1}
    617619    """
    618620    def __init__(self, s):
    619621        """
     
    624626            sage: S = Subsets([1,2,2,3], submultiset=True)
    625627            sage: Subsets([1,2,3,3], submultiset=True).cardinality()
    626628            12
     629            sage: TestSuite(S).run()
    627630        """
    628631        CombinatorialClass.__init__(self, category=FiniteEnumeratedSets())
    629632
    630         s = sorted(list(s))
    631         indices = list(sorted(Set([s.index(a) for a in s])))
    632         multiplicities = [len([a for a in s if a == s[i]])
    633                           for i in indices]
     633        self._d = s
     634        if not isinstance(s, dict):
     635            self._d = list_to_dict(s)
    634636
    635         self._s = s
    636         self._indices = indices
    637         self._multiplicities = multiplicities
    638 
    639     def __repr__(self):
     637    def _repr_(self):
    640638        """
    641639        TESTS::
    642640
    643641            sage: S = Subsets([1, 2, 2, 3], submultiset=True); S
    644642            SubMultiset of [1, 2, 2, 3]
    645643        """
    646         return "SubMultiset of %s"%self._s
     644        return "SubMultiset of %s"%dict_to_list(self._d)
    647645
    648646    def __contains__(self, s):
    649647        """
     
    663661            sage: [4] in S
    664662            False
    665663        """
    666         return sorted(s) in subword.Subwords(self._s)
     664        dd = {}
     665        for elt in s:
     666            if elt in dd:
     667                dd[elt] += 1
     668                if dd[elt] > self._d[elt]:
     669                    return False
     670            elif elt not in self._d:
     671                return False
     672            else:
     673                dd[elt] = 1
     674        return True
     675
     676    def cardinality(self):
     677        r"""
     678        Return the cardinality of self
     679       
     680        EXAMPLES::
     681
     682            sage: S = Subsets([1,1,2,3],submultiset=True)
     683            sage: S.cardinality()
     684            12
     685            sage: len(S.list())
     686            12
     687
     688            sage: S = Subsets([1,1,2,2,3],submultiset=True)
     689            sage: S.cardinality()
     690            18
     691            sage: len(S.list())
     692            18
     693
     694            sage: S = Subsets([1,1,1,2,2,3],submultiset=True)
     695            sage: S.cardinality()
     696            24
     697            sage: len(S.list())
     698            24
     699        """
     700        from sage.all import prod
     701        return Integer(prod(k+1 for k in self._d.values()))
     702
     703    def random_element(self):
     704        r"""
     705        Return a random element of self with uniform law
     706
     707        EXAMPLES::
     708
     709            sage: S = Subsets([1,1,2,3], submultiset=True)
     710            sage: S.random_element()
     711            [2]
     712        """
     713        l = []
     714        for i in self._d:
     715            l.extend([i]*rnd.randint(0,self._d[i]))
     716        return l
     717
     718    def generating_serie(self,variable='x'):
     719        r"""
     720        Return the serie (here a polynom) associated to the counting of the
     721        element of self weighted by the number of element they contain.
     722
     723        EXAMPLES::
     724
     725            sage: Subsets([1,1],submultiset=True).generating_serie()
     726            x^2 + x + 1
     727            sage: Subsets([1,1,2,3],submultiset=True).generating_serie()
     728            x^4 + 3*x^3 + 4*x^2 + 3*x + 1
     729            sage: Subsets([1,1,1,2,2,3,3,4],submultiset=True).generating_serie()
     730            x^8 + 4*x^7 + 9*x^6 + 14*x^5 + 16*x^4 + 14*x^3 + 9*x^2 + 4*x + 1
     731
     732            sage: S = Subsets([1,1,1,2,2,3,3,4],submultiset=True)
     733            sage: S.cardinality()
     734            72
     735            sage: sum(S.generating_serie())
     736            72
     737        """
     738        from sage.rings.integer_ring import ZZ
     739        from sage.all import prod
     740        R = ZZ[variable]
     741        return prod(R([1]*(n+1)) for n in self._d.values())
    667742
    668743    def __iter__(self):
    669744        """
    670         Iterates through the subsets of the multiset ``self._s``.  Note
     745        Iterates through the subsets of the multiset ``self.s``.  Note
    671746        that each subset is represented by a list of its elements rather than
    672747        a set since we can have multiplicities (no multiset data structure yet
    673748        in sage).
     
    690765             [1, 2, 2, 3]]
    691766
    692767        """
    693         for k in range(len(self._s)+1):
    694             for s in SubMultiset_sk(self._s, k):
     768        for k in range(sum(self._d.values())+1):
     769            for s in SubMultiset_sk(self._d, k):
    695770                yield s
    696771
    697 
    698772class SubMultiset_sk(SubMultiset_s):
    699773    """
    700774    The combinatorial class of the subsets of size k of a multiset s.  Note
     
    704778
    705779    EXAMPLES::
    706780
    707         sage: S = Subsets([1,2,3,3],2, submultiset=True)
     781        sage: S = Subsets([1,2,3,3],2,submultiset=True)
    708782        sage: S._k
    709783        2
    710784        sage: S.cardinality()
     
    715789        [3, 3]
    716790        sage: [sub for sub in S]
    717791        [[1, 2], [1, 3], [2, 3], [3, 3]]
    718         sage: TestSuite(S).run()
    719792        """
    720793    def __init__(self, s, k):
    721794        """
    722         TEST::
     795        TESTS::
    723796
    724             sage: S = Subsets([1,2,3,3],2, submultiset=True)
     797            sage: S = Subsets([1,2,3,3],2,submultiset=True)
    725798            sage: [sub for sub in S]
    726799            [[1, 2], [1, 3], [2, 3], [3, 3]]
     800            sage: TestSuite(S).run()
    727801        """
    728802        SubMultiset_s.__init__(self, s)
     803        self._l = dict_to_list(self._d)
    729804        self._k = k
    730805
    731     def __repr__(self):
     806    def generating_serie(self,variable='x'):
     807        r"""
     808        Return the serie (this case a polynom) associated to the counting of the
     809        element of self weighted by the number of element they contains
     810
     811        EXAMPLES::
     812
     813            sage: x = ZZ['x'].gen()
     814            sage: l = [1,1,1,1,2,2,3]
     815            sage: for k in xrange(len(l)):
     816            ...      S = Subsets(l,k,submultiset=True)
     817            ...      print S.generating_serie(x) == S.cardinality()*x**k
     818            True
     819            True
     820            True
     821            True
     822            True
     823            True
     824            True
     825        """
     826        from sage.all import ZZ
     827        x = ZZ[variable].gen()
     828        P = SubMultiset_s.generating_serie(self)
     829        return P[self._k] * (x**self._k)
     830
     831    def cardinality(self):
     832        r"""
     833        Return the cardinality of self
     834
     835        EXAMPLES::
     836
     837            sage: S = Subsets([1,2,2,3,3,3],4,submultiset=True)
     838            sage: S.cardinality()
     839            5
     840            sage: len(list(S))
     841            5
     842
     843            sage: S = Subsets([1,2,2,3,3,3],3,submultiset=True)
     844            sage: S.cardinality()
     845            6
     846            sage: len(list(S))
     847            6
     848        """
     849        return Integer(sum(1 for _ in self))
     850
     851    def _repr_(self):
    732852        """
    733853        TESTS::
    734854
    735             sage: S = Subsets([1, 2, 2, 3], 3, submultiset=True); S
    736             SubMultiset of [1, 2, 2, 3] of size 3
     855            sage: S = Subsets([1, 2, 2, 3], 3, submultiset=True)
     856            sage: repr(S) #indirect doctest
     857            'SubMultiset of [1, 2, 2, 3] of size 3'
    737858        """
    738         return "SubMultiset of %s of size %s"%(self._s, self._k)
     859        return "%s of size %s"%(SubMultiset_s._repr_(self), self._k)
    739860
    740861    def __contains__(self, s):
    741862        """
     
    757878            sage: [3, 3] in S
    758879            False
    759880        """
    760         return sorted(s) in subword.Subwords(self._s, self._k)
     881        return len(s) == self._k and SubMultiset_s.__contains__(self, s)
     882
     883    def random_element(self):
     884        r"""
     885        Return a random submultiset of given length
     886
     887        EXAMPLES::
     888
     889            sage: Subsets(7,3).random_element()
     890            {1, 4, 7}
     891            sage: Subsets(7,5).random_element()
     892            {1, 3, 4, 5, 7}
     893        """
     894        return rnd.sample(self._l, self._k)
    761895
    762896    def __iter__(self):
    763897        """
    764898        Iterates through the subsets of size ``self._k`` of the multiset
    765         ``self._s``. Note that each subset is represented by a list of the
     899        ``self.s``. Note that each subset is represented by a list of the
    766900        elements rather than a set since we can have multiplicities (no
    767901        multiset data structure yet in sage).
    768902
     
    773907            [[1, 2], [1, 3], [2, 2], [2, 3]]
    774908        """
    775909        from sage.combinat.integer_vector import IntegerVectors
    776         for iv in IntegerVectors(self._k, len(self._indices), outer=self._multiplicities):
    777             yield sum([ [self._s[self._indices[i]]]*iv[i] for i in range(len(iv))], [])
     910        elts = self._d.keys()
     911        for iv in IntegerVectors(self._k, len(self._d), outer=self._d.values()):
     912            yield sum([ [elts[i]]*iv[i] for i in range(len(iv))], [])
    778913
  • sage/combinat/subword.py

    diff --git a/sage/combinat/subword.py b/sage/combinat/subword.py
    a b  
    11r"""
    22Subwords
    33
    4 A subword of a word $w$ is a word obtained by deleting the letters at some
     4A subword of a word `w` is a word obtained by deleting the letters at some
    55(non necessarily adjacent) positions in `w`. It is not to be confused with the
    66notion of factor where one keeps adjacent positions in `w`. Sometimes it is
    77useful to allow repeated uses of the same letter of `w` in a "generalized"
     
    2121
    2222- "nnu" is a subword with repetitions of "bonjour";
    2323 
    24 A word can be given either as a string or as a list.
     24A word can be given either as a string, as a list or as a tuple.
     25
     26
     27As repetition can occur in the initial word, the subwords of a given words is
     28not a set in general but an enumerated multiset!
     29
     30TODO:
     31
     32- implement subwords with repetitions
     33
     34- implement the category of EnumeratedMultiset and inheritate from when needed
     35  (ie the initial word has repeated letters)
    2536
    2637AUTHORS:
    2738
     
    2940
    3041- Florent Hivert (2009/02/06): doc improvements + new methods + bug fixes
    3142
    32 
     43- Vincent Delecroix (2011/10/03): link to itertools for faster generation,
     44  documentation, random generation, improvements
    3345"""
    3446
    3547#*****************************************************************************
     
    4759#                  http://www.gnu.org/licenses/
    4860#*****************************************************************************
    4961
    50 import sage.combinat.combination as combination
    51 from sage.rings.arith import factorial
     62from sage.structure.parent import Parent
     63
     64import sage.rings.arith as arith
     65import sage.misc.prandom as prandom
     66from sage.rings.integer import Integer
    5267import itertools
    53 from combinat import CombinatorialClass
    54 
     68import choose_nk
     69from combinat import CombinatorialClass
    5570
    5671def Subwords(w, k=None):
    5772    """
    58     Returns the combinatorial class of subwords of w. The word w can be given
    59     by either a string or a list.
     73    Returns the set of subwords of w. The word w can be given by either a
     74    string, a list or a tuple.
    6075   
    6176    If k is specified, then it returns the combinatorial class of
    6277    subwords of w of length k.
     
    7186        ['a', 'b', 'c']
    7287        sage: S.list()
    7388        [[], ['a'], ['b'], ['c'], ['a', 'b'], ['a', 'c'], ['b', 'c'], ['a', 'b', 'c']]
    74    
    75     ::
     89
     90    The same example using string::
     91
     92        sage: S = Subwords('abc'); S
     93        Subwords of abc
     94        sage: S.first()
     95        ''
     96        sage: S.last()
     97        'abc'
     98        sage: S.list()
     99        ['', 'a', 'b', 'c', 'ab', 'ac', 'bc', 'abc']
     100
     101    The same example using tuple::
     102
     103        sage: S = Subwords((1,2,3)); S
     104        Subwords of (1, 2, 3)
     105        sage: S.first()
     106        ()
     107        sage: S.last()
     108        (1, 2, 3)
     109        sage: S.list()
     110        [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
     111
     112    Using word with specified length::
    76113   
    77114        sage: S = Subwords(['a','b','c'], 2); S
    78115        Subwords of ['a', 'b', 'c'] of length 2
    79116        sage: S.list()
    80117        [['a', 'b'], ['a', 'c'], ['b', 'c']]
    81118    """
    82     if k == None:
    83         return Subwords_w(w)
     119    datatype = type(w)  # 'datatype' is the type of w
     120    if datatype not in [str,list,tuple]:
     121        raise ValueError, "datatype should be str, list or tuple"
     122
     123    build = datatype    # 'build' is a method to build an element with the same
     124                        # type as the one of w.
     125    if datatype == str:
     126        build = lambda x: ''.join(x)
     127 
     128    if k is None:
     129        return Subwords_w(w,build)
    84130    else:
    85         if k not in range(0, len(w)+1):
    86             raise ValueError, "k must be between 0 and %s"%len(w)
     131        if not isinstance(k, (int,Integer)):
     132            raise ValueError, "k should be an integer"
     133        if k < 0 or k > len(w):
     134            return FiniteEnumeratedSet([])
    87135        else:
    88             return Subwords_wk(w,k)
     136            return Subwords_wk(w,k,build)
    89137
    90138
    91139class Subwords_w(CombinatorialClass):
    92     def __init__(self, w):
     140    r"""
     141    Subwords of a given word
     142    """
     143    def __init__(self, w, build):
    93144        """
    94145        TESTS::
    95146       
    96147            sage: S = Subwords([1,2,3])
    97148            sage: S == loads(dumps(S))
    98149            True
     150            sage: TestSuite(S).run()
    99151        """
    100         self.w = w
    101        
    102     def __repr__(self):
     152        CombinatorialClass.__init__(self)
     153        self._w = w   # the word
     154        self._build = build  # how to build an element with same type as w
     155
     156    def __reduce__(self):
     157        r"""
     158        Pickle (how to construct back the object)
     159
     160        TESTS::
     161
     162            sage: S = Subwords((1,2,3))
     163            sage: S == loads(dumps(S))
     164            True
     165            sage: S = Subwords('123')
     166            sage: S == loads(dumps(S))
     167            True
     168            sage: S = Subwords(('a',(1,2,3),('a','b'),'ir'))
     169            sage: S == loads(dumps(S))
     170            True
     171        """
     172        return (Subwords, (self._w,))
     173
     174    def _repr_(self):
    103175        """
    104176        TESTS::
    105177       
    106             sage: repr(Subwords([1,2,3]))
     178            sage: repr(Subwords([1,2,3])) # indirect doctest
    107179            'Subwords of [1, 2, 3]'
    108180        """
    109         return "Subwords of %s"%self.w
     181        return "Subwords of %s" %str(self._w)
    110182
    111183    def __contains__(self, w):
    112184        """
     
    116188            True
    117189            sage: [2,3,3,4] in Subwords([1,2,3,4,3,4,4])
    118190            True
    119             sage: [5, 5, 3] in Subwords([1, 3, 3, 5, 4, 5, 3, 5])
     191            sage: [5,5,3] in Subwords([1,3,3,5,4,5,3,5])
    120192            True
    121             sage: [3, 5, 5, 3] in Subwords([1, 3, 3, 5, 4, 5, 3, 5])
     193            sage: [3,5,5,3] in Subwords([1,3,3,5,4,5,3,5])
    122194            True
    123             sage: [3, 5, 5, 3, 4] in Subwords([1, 3, 3, 5, 4, 5, 3, 5])
     195            sage: [3,5,5,3,4] in Subwords([1,3,3,5,4,5,3,5])
    124196            False
    125197            sage: [2,3,3,4] in Subwords([1,2,3,4,3,4,4])
    126198            True
    127199            sage: [2,3,3,1] in Subwords([1,2,3,4,3,4,4])
    128200            False
    129201        """
    130         if smallest_positions(self.w, w) != False:
     202        if smallest_positions(self._w, w) != False:
    131203            return True
    132204        return False   
    133205       
     
    138210            sage: Subwords([1,2,3]).cardinality()
    139211            8
    140212        """
    141         return 2**len(self.w)
     213        return Integer(2)**len(self._w)
    142214
    143215    def first(self):
    144216        """
     
    146218       
    147219            sage: Subwords([1,2,3]).first()
    148220            []
     221            sage: Subwords((1,2,3)).first()
     222            ()
     223            sage: Subwords('123').first()
     224            ''
    149225        """
    150         return []
     226        return self._build([])
    151227
    152228    def last(self):
    153229        """
     
    155231       
    156232            sage: Subwords([1,2,3]).last()
    157233            [1, 2, 3]
     234            sage: Subwords((1,2,3)).last()
     235            (1, 2, 3)
     236            sage: Subwords('123').last()
     237            '123'
    158238        """
    159         return self.w
     239        return self._w
     240
     241    def random_element(self):
     242        r"""
     243        Return a random subword with uniform law
     244
     245        EXAMPLES::
     246
     247            sage: S1 = Subwords([1,2,3,2,1,3])
     248            sage: S2 = Subwords([4,6,6,6,7,4,5,5])
     249            sage: for i in xrange(100):
     250            ...     w = S1.random_element()
     251            ...     if w in S2:
     252            ...         assert(w == [])
     253            sage: for i in xrange(100):
     254            ...     w = S2.random_element()
     255            ...     if w in S1:
     256            ...         assert(w == [])
     257        """
     258        return self._build(elt for elt in self._w if prandom.randint(0,1))
    160259
    161260    def __iter__(self):
    162261        r"""
    163262        EXAMPLES::
    164263       
    165             sage: [sw for sw in Subwords([1,2,3])]
     264            sage: Subwords([1,2,3]).list()
    166265            [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
     266            sage: Subwords((1,2,3)).list()
     267            [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
     268            sage: Subwords('123').list()
     269            ['', '1', '2', '3', '12', '13', '23', '123']
    167270        """
    168         #Case 1: recursively build a generator for all the subwords
    169         #        length k and concatenate them together
    170         w = self.w
    171         return itertools.chain(*[Subwords_wk(w,i) for i in range(0,len(w)+1)])
     271        return itertools.chain(*[Subwords_wk(self._w,i,self._build) for i in range(0,len(self._w)+1)])
    172272
    173 
    174 class Subwords_wk(CombinatorialClass):
    175     def __init__(self, w, k):
     273class Subwords_wk(Subwords_w):
     274    r"""
     275    Subwords with fixed length of a given word
     276    """
     277    def __init__(self, w, k, build):
    176278        """
    177279        TESTS::
    178280       
    179281            sage: S = Subwords([1,2,3],2)
    180282            sage: S == loads(dumps(S))
    181283            True
     284            sage: TestSuite(S).run()
    182285        """
    183         self.w = w
    184         self.k = k
     286        Subwords_w.__init__(self,w,build)
     287        self._k = k
    185288
    186     def __repr__(self):
     289    def __reduce__(self):
     290        r"""
     291        Pickle (how to construct back the object)
     292
     293        TESTS::
     294
     295            sage: S = Subwords('abc',2)
     296            sage: S == loads(dumps(S))
     297            True
     298            sage: S = Subwords(('a',1,'45',(1,2)))
     299            sage: S == loads(dumps(S))
     300            True
     301        """
     302        return (Subwords,(self._w,self._k))
     303
     304    def _repr_(self):
    187305        """
    188306        TESTS::
    189307       
    190             sage: repr(Subwords([1,2,3],2))
     308            sage: repr(Subwords([1,2,3],2))  # indirect doctest
    191309            'Subwords of [1, 2, 3] of length 2'
    192310        """
    193         return "Subwords of %s of length %s"%(self.w, self.k)
     311        return "%s of length %s" %(Subwords_w._repr_(self), self._k)
    194312
    195313    def __contains__(self, w):
    196314        """
     
    202320            True
    203321            sage: [2,3,3,4] in Subwords([1,2,3,4,3,4,4],3)
    204322            False
    205             sage: [5, 5, 3] in Subwords([1, 3, 3, 5, 4, 5, 3, 5],3)
     323            sage: [5,5,3] in Subwords([1,3,3,5,4,5,3,5],3)
    206324            True
    207             sage: [5, 5, 3] in Subwords([1, 3, 3, 5, 4, 5, 3, 5],4)
     325            sage: [5,5,3] in Subwords([1,3,3,5,4,5,3,5],4)
    208326            False
    209327        """
    210         if len(w) != self.k:
    211             return False
    212         if smallest_positions(self.w, w) != False:
    213             return True
    214         return False   
     328        return len(w) == self._k and Subwords_w.__contains__(self,w)
    215329   
    216330    def cardinality(self):
    217331        r"""
     
    222336            sage: Subwords([1,2,3], 2).cardinality()
    223337            3
    224338        """
    225         w = self.w
    226         k = self.k
    227         return factorial(len(w))/(factorial(k)*factorial(len(w)-k))
    228 
     339        return arith.binomial(len(self._w),self._k)
    229340
    230341    def first(self):
    231342        r"""
     
    235346            [1, 2]
    236347            sage: Subwords([1,2,3],0).first()
    237348            []
     349            sage: Subwords((1,2,3),2).first()
     350            (1, 2)
     351            sage: Subwords((1,2,3),0).first()
     352            ()
     353            sage: Subwords('123',2).first()
     354            '12'
     355            sage: Subwords('123',0).first()
     356            ''
    238357        """
    239         return self.w[:self.k]
     358        return self._w[:self._k]
    240359
    241360    def last(self):
    242361        r"""
     
    244363       
    245364            sage: Subwords([1,2,3],2).last()
    246365            [2, 3]
     366            sage: Subwords([1,2,3],0).last()
     367            []
     368            sage: Subwords((1,2,3),2).last()
     369            (2, 3)
     370            sage: Subwords((1,2,3),0).last()
     371            ()
     372            sage: Subwords('123',2).last()
     373            '23'
     374            sage: Subwords('123',0).last()
     375            ''
     376
     377        TESTS::
     378
     379            sage: Subwords('123', 0).last()  # trac 10534
     380            ''
    247381        """
     382        if self._k:
     383            return self._w[-self._k:]
     384        return self.first()
    248385
    249         return self.w[-self.k:]
     386    def random_element(self):
     387        r"""
     388        Return a random subword of given length with uniform law
    250389
     390        EXAMPLES::
    251391
     392            sage: S1 = Subwords([1,2,3,2,1],3)
     393            sage: S2 = Subwords([4,4,5,5,4,5,4,4],3)
     394            sage: for i in xrange(100):
     395            ...     w = S1.random_element()
     396            ...     if w in S2:
     397            ...         assert(w == [])
     398            sage: for i in xrange(100):
     399            ...     w = S2.random_element()
     400            ...     if w in S1:
     401            ...         assert(w == [])
     402        """
     403        sample = prandom.sample(self._w, self._k)
     404        if type(self._w) == list:
     405            return sample
     406        return self._build(sample)
    252407
    253408    def __iter__(self):
    254409        """
    255410        EXAMPLES::
    256411       
    257             sage: [sw for sw in Subwords([1,2,3],2)]
     412            sage: Subwords([1,2,3],2).list()
    258413            [[1, 2], [1, 3], [2, 3]]
    259             sage: [sw for sw in Subwords([1,2,3],0)]
     414            sage: Subwords([1,2,3],0).list()
    260415            [[]]
     416            sage: Subwords((1,2,3),2).list()
     417            [(1, 2), (1, 3), (2, 3)]
     418            sage: Subwords((1,2,3),0).list()
     419            [()]
     420            sage: Subwords('abc',2).list()
     421            ['ab', 'ac', 'bc']
     422            sage: Subwords('abc',0).list()
     423            ['']
    261424        """
    262         w = self.w
    263         k = self.k
    264         #Case 1: k == 0
    265         if k == 0:
    266             return itertools.repeat([],1)
    267 
    268         #Case 2: build a generator for the subwords of length k
    269         gen = iter(combination.Combinations(range(len(w)), k))
    270         return itertools.imap(lambda subword: [w[x] for x in subword], gen)
    271    
     425        if self._k > len(self._w):
     426            return iter([])
     427        iterator = itertools.combinations(self._w, self._k)
     428        if type(self._w) == tuple:
     429            return iterator
     430        else:
     431            return itertools.imap(self._build, iterator)
    272432
    273433def smallest_positions(word, subword, pos = 0):
    274434    """
     
    315475    """
    316476    pos -= 1
    317477    res = [None] * len(subword)
    318     for i in range(len(subword)):
    319         for j in range(pos+1, len(word)+1):
     478    for i in xrange(len(subword)):
     479        for j in xrange(pos+1, len(word)+1):
    320480            if j == len(word):
    321481                return False
    322482            if word[j] == subword[i]: