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 , 6 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 1 1 """ 2 2 Low-level Combinations 3 4 AUTHORS: 5 6 - Mike Hansen (2007): initial implementation 7 8 - Vincent Delecroix (2011): call itertools for faster iteration, bug 9 corrections, doctests. 3 10 """ 4 11 #***************************************************************************** 5 12 # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, … … 19 26 from sage.rings.arith import binomial 20 27 from sage.misc.misc import uniq 21 28 from combinat import CombinatorialClass 29 import itertools 30 from sage.rings.integer import Integer 22 31 23 32 class ChooseNK(CombinatorialClass): 24 33 """ 25 34 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 27 38 28 39 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 python40 design. It aims at speed. It's element are returned as plain list of python 30 41 int. 31 42 32 43 EXAMPLES:: … … 34 45 sage: from sage.combinat.choose_nk import ChooseNK 35 46 sage: c = ChooseNK(4,2) 36 47 sage: c.first() 37 [0, 1]48 (0, 1) 38 49 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)] 40 51 sage: type(c.list()[1]) 41 <type ' list'>52 <type 'tuple'> 42 53 sage: type(c.list()[1][1]) 43 54 <type 'int'> 44 55 """ … … 66 77 False 67 78 sage: [0,1,3] in c52 68 79 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 69 89 """ 70 90 try: 71 x = list(x)91 x = map(int,x) 72 92 except TypeError: 73 93 return False 74 94 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 77 99 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 78 110 79 111 def cardinality(self): 80 112 """ … … 97 129 EXAMPLES:: 98 130 99 131 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)] 111 143 """ 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) 147 145 148 146 def random_element(self): 149 147 """ … … 153 151 154 152 sage: from sage.combinat.choose_nk import ChooseNK 155 153 sage: ChooseNK(5,2).random_element() 156 [0, 2]154 (0, 2) 157 155 """ 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 161 162 162 163 def unrank(self, r): 163 164 """ … … 169 170 True 170 171 """ 171 172 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 173 175 return from_rank(r, self._n, self._k) 174 176 175 177 def rank(self, x): … … 181 183 sage: range(c52.cardinality()) == map(c52.rank, c52) 182 184 True 183 185 """ 184 if len(x) != self._k:185 r eturn186 if x not in self: 187 raise ValueError, "%s not in Choose(%d,%d)" %(str(x),self._n,self._k) 186 188 187 189 return rank(x, self._n) 188 190 189 191 190 def rank(comb, n ):192 def rank(comb, n, check=True): 191 193 """ 192 194 Returns the rank of comb in the subsets of range(n) of size k. 193 195 … … 197 199 EXAMPLES:: 198 200 199 201 sage: import sage.combinat.choose_nk as choose_nk 200 sage: choose_nk.rank( [], 3)202 sage: choose_nk.rank((), 3) 201 203 0 202 sage: choose_nk.rank( [0], 3)204 sage: choose_nk.rank((0,), 3) 203 205 0 204 sage: choose_nk.rank( [1], 3)206 sage: choose_nk.rank((1,), 3) 205 207 1 206 sage: choose_nk.rank( [2], 3)208 sage: choose_nk.rank((2,), 3) 207 209 2 208 sage: choose_nk.rank( [0,1], 3)210 sage: choose_nk.rank((0,1), 3) 209 211 0 210 sage: choose_nk.rank( [0,2], 3)212 sage: choose_nk.rank((0,2), 3) 211 213 1 212 sage: choose_nk.rank( [1,2], 3)214 sage: choose_nk.rank((1,2), 3) 213 215 2 214 sage: choose_nk.rank( [0,1,2], 3)216 sage: choose_nk.rank((0,1,2), 3) 215 217 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) 216 227 """ 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)" 217 236 218 k = len(comb)219 if k > n:220 raise ValueError, "len(comb) must be <= n"221 222 237 #Generate the combinadic from the 223 238 #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)] 227 240 228 241 #Calculate the integer that is the dual of 229 242 #the lexicographic index of the combination 230 243 r = k 231 244 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) 234 247 r -= 1 235 248 236 249 return binomial(n,k)-t-1 237 250 238 239 240 251 def _comb_largest(a,b,x): 241 252 """ 242 253 Returns the largest w < a such that binomial(w,b) <= x. … … 268 279 269 280 sage: import sage.combinat.choose_nk as choose_nk 270 281 sage: choose_nk.from_rank(0,3,0) 271 []282 () 272 283 sage: choose_nk.from_rank(0,3,1) 273 [0]284 (0,) 274 285 sage: choose_nk.from_rank(1,3,1) 275 [1]286 (1,) 276 287 sage: choose_nk.from_rank(2,3,1) 277 [2]288 (2,) 278 289 sage: choose_nk.from_rank(0,3,2) 279 [0, 1]290 (0, 1) 280 291 sage: choose_nk.from_rank(1,3,2) 281 [0, 2]292 (0, 2) 282 293 sage: choose_nk.from_rank(2,3,2) 283 [1, 2]294 (1, 2) 284 295 sage: choose_nk.from_rank(0,3,3) 285 [0, 1, 2]296 (0, 1, 2) 286 297 """ 287 298 if k < 0: 288 299 raise ValueError, "k must be > 0" … … 294 305 x = binomial(n,k) - 1 - r # x is the 'dual' of m 295 306 comb = [None]*k 296 307 297 for i in range(k):308 for i in xrange(k): 298 309 comb[i] = _comb_largest(a,b,x) 299 310 x = x - binomial(comb[i], b) 300 311 a = comb[i] 301 312 b = b -1 302 313 303 for i in range(k):314 for i in xrange(k): 304 315 comb[i] = (n-1)-comb[i] 305 316 306 return comb317 return tuple(comb) 307 318 -
sage/combinat/combination.py
diff --git a/sage/combinat/combination.py b/sage/combinat/combination.py
a b 1 1 r""" 2 2 Combinations 3 4 AUTHORS: 5 6 - Mike Hansen (2007): initial implementation 7 8 - Vincent Delecroix (2011): cleaning, bug corrections, doctests 9 3 10 """ 4 11 #***************************************************************************** 5 12 # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, … … 80 87 [1, 2, 3], 81 88 [2, 2, 3], 82 89 [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)]] 83 98 """ 84 99 85 86 100 87 101 #Check to see if everything in mset is unique 88 102 if isinstance(mset, (int, Integer)): 89 103 mset = range(mset) 90 104 else: 91 105 mset = list(mset) 92 106 93 107 d = {} 94 108 for i in mset: 95 109 d[mset.index(i)] = 1 96 110 97 111 if len(d) == len(mset): 98 112 if k is None: 99 113 return Combinations_set(mset) -
sage/combinat/generator.py
diff --git a/sage/combinat/generator.py b/sage/combinat/generator.py
a b 1 1 """ 2 2 Generators 3 4 This module is higly deprecated since itertools exists! 3 5 """ 4 6 #***************************************************************************** 5 7 # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, … … 16 18 # http://www.gnu.org/licenses/ 17 19 #***************************************************************************** 18 20 21 import itertools 22 23 #TODO: change to itertools.chain 19 24 def concat(gens): 20 25 r""" 21 26 Returns a generator that is the concatenation of the generators in … … 31 36 for element in gen: 32 37 yield element 33 38 34 39 #TODO: change to itertools.map 35 40 def map(f, gen): 36 41 """ 37 42 Returns a generator that returns f(g) for g in gen. … … 45 50 for element in gen: 46 51 yield f(element) 47 52 53 #TODO: change to itertools.repeat 48 54 def element(element, n = 1): 49 55 """ 50 56 Returns a generator that yield a single element n times. … … 59 65 for i in range(n): 60 66 yield element 61 67 68 #TODO: change to itertools.ifilter 62 69 def select(f, gen): 63 70 """ 64 71 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 48 48 - Mike Hansen 49 49 50 50 - 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) 51 54 """ 52 55 #***************************************************************************** 53 56 # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, … … 65 68 #***************************************************************************** 66 69 67 70 from sage.sets.set import Set, is_Set, Set_object_enumerated 68 import sage.combinat.partition as partition 69 import sage.rings.integer 71 from sage.structure.parent import Parent 72 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets 73 from cartesian_product import CartesianProduct 74 70 75 import __builtin__ 71 76 import itertools 72 from cartesian_product import CartesianProduct73 import sage.combinat.subset as subset74 import sage.combinat.set_partition_ordered as set_partition_ordered75 77 import copy 78 79 import sage.rings.integer 80 import sage.rings.arith as arith 81 from sage.functions.other import factorial 82 83 import partition 84 import subset 85 from subword import Subwords 86 import set_partition_ordered 87 76 88 from combinat import CombinatorialClass, bell_number, stirling_number2 77 89 from misc import IterableFunctionCall 78 90 91 # 92 # Constructor 93 # 79 94 80 95 def SetPartitions(s, part=None): 81 96 """ … … 93 108 You may specify a second argument k. If k is an integer, 94 109 SetPartitions returns the class of set partitions into k parts; if 95 110 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. 97 115 98 116 The Bell number `B_n`, named in honor of Eric Temple Bell, 99 117 is the number of different partitions of a set with n elements. 100 118 101 EXAMPLES: :119 EXAMPLES: 102 120 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() 107 158 [{{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 110 163 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 111 179 In strings, repeated letters are considered distinct:: 112 180 113 181 sage: SetPartitions('aabcd').cardinality() … … 121 189 """ 122 190 if isinstance(s, (int, sage.rings.integer.Integer)): 123 191 set = range(1, s+1) 124 elif isinstance(s, str):125 set = [x for x in s]126 192 else: 127 set = s193 set = list(s) 128 194 129 195 if part is not None: 130 196 if isinstance(part, (int, sage.rings.integer.Integer)): … … 132 198 raise ValueError, "part must be <= len(set)" 133 199 else: 134 200 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 135 210 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 140 213 else: 141 214 return SetPartitions_set(set) 142 215 216 # 217 # Classes 218 # 143 219 144 145 class SetPartitions_setparts(CombinatorialClass): 146 Element = Set_object_enumerated 147 def __init__(self, set, parts): 220 class 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): 148 226 """ 149 227 TESTS:: 150 228 151 229 sage: S = SetPartitions(4, [2,2]) 152 230 sage: S == loads(dumps(S)) 153 231 True 232 sage: TestSuite(S).run() 154 233 """ 234 Parent.__init__(self, category=FiniteEnumeratedSets()) 155 235 self.set = set 156 self.part s = parts236 self.part = part 157 237 158 def _ _repr__(self):238 def _repr_(self): 159 239 """ 160 240 TESTS:: 161 241 162 242 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 379 class 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]}' 164 415 """ 165 416 return "Set partitions of %s with sizes in %s"%(self.set, self.parts) 166 417 … … 172 423 sage: all([sp in S for sp in S]) 173 424 True 174 425 """ 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) 178 427 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) 182 432 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:: 190 434 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 195 456 196 return True197 198 def cardinality(self):199 """200 Returns the number of set partitions of set. This number is given201 by the n-th Bell number where n is the number of elements in the202 set.203 204 If a partition or partition length is specified, then count will205 generate all of the set partitions.206 207 EXAMPLES::208 209 sage: SetPartitions([1,2,3,4]).cardinality()210 15211 sage: SetPartitions(3).cardinality()212 5213 sage: SetPartitions(3,2).cardinality()214 3215 sage: SetPartitions([]).cardinality()216 1217 """218 return len(self.list())219 220 221 def _iterator_part(self, part):222 """223 Returns an iterator for the set partitions with block sizes224 corresponding to the partition part.225 226 INPUT:227 228 229 - ``part`` - a Partition object230 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 True241 """242 set = self.set243 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 x259 260 261 262 457 def __iter__(self): 263 458 """ 264 459 An iterator for all the set partitions of the set. 265 266 EXAMPLES::460 461 TESTS:: 267 462 268 463 sage: SetPartitions(3).list() 269 464 [{{1, 2, 3}}, {{2, 3}, {1}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{2}, {3}, {1}}] 270 465 """ 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()) 275 467 276 468 class SetPartitions_setn(SetPartitions_setparts): 469 r""" 470 Set partitions with specified number of parts 471 """ 277 472 def __init__(self, set, n): 278 473 """ 279 474 TESTS:: … … 281 476 sage: S = SetPartitions(5, 3) 282 477 sage: S == loads(dumps(S)) 283 478 True 479 sage: TestSuite(S).run() 284 480 """ 285 481 self.n = n 286 482 SetPartitions_setparts.__init__(self, set, partition.Partitions(len(set), length=n).list()) 287 483 288 def _ _repr__(self):484 def _repr_(self): 289 485 """ 290 486 TESTS:: 291 487 … … 294 490 """ 295 491 return "Set partitions of %s with %s parts"%(self.set,self.n) 296 492 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 297 507 def cardinality(self): 298 508 """ 299 509 The Stirling number of the second kind is the number of partitions … … 307 517 25 308 518 """ 309 519 return stirling_number2(len(self.set), self.n) 310 520 311 521 class SetPartitions_set(SetPartitions_setparts): 522 r""" 523 Set partitions of a set 524 """ 312 525 def __init__(self, set): 313 526 """ 314 527 TESTS:: … … 316 529 sage: S = SetPartitions([1,2,3]) 317 530 sage: S == loads(dumps(S)) 318 531 True 532 sage: TestSuite(S).run() 319 533 """ 320 534 SetPartitions_setparts.__init__(self, set, partition.Partitions(len(set))) 321 535 322 def _ _repr__(self):536 def _repr_(self): 323 537 """ 324 538 TESTS:: 325 539 … … 328 542 """ 329 543 return "Set partitions of %s"%(self.set) 330 544 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 331 579 def cardinality(self): 332 580 """ 333 581 EXAMPLES:: … … 339 587 """ 340 588 return bell_number(len(self.set)) 341 589 590 # 591 # Functions 592 # 342 593 594 def _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 343 598 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)] 345 613 """ 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:] 348 621 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 381 628 382 629 def _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) 383 638 """ 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 = [] 391 640 for ss in s: 392 result = result.union(ss)393 return result641 result.extend(ss) 642 return tuple(result) 394 643 395 644 def _set_union(s): 396 645 """ … … 440 689 441 690 return res 442 691 443 444 692 def standard_form(sp): 445 693 """ 446 694 Returns the set partition as a list of lists. … … 448 696 EXAMPLES:: 449 697 450 698 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]]] 452 700 """ 453 701 454 702 return [__builtin__.list(x) for x in sp] 455 703 456 457 def less(s, t): 704 def less(s, t, check=True): 458 705 """ 459 706 Returns True if s < t otherwise it returns False. 460 707 … … 476 723 sage: sage.combinat.set_partition.less(z[0], z[0]) 477 724 False 478 725 """ 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" 482 729 483 730 if s == t: 484 731 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 76 76 # http://www.gnu.org/licenses/ 77 77 #***************************************************************************** 78 78 79 import itertools 80 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets 81 from sage.structure.parent import Parent 82 from sage.sets.set import Set_object_enumerated 83 84 import itertools 85 import __builtin__ 86 79 87 from sage.rings.arith import factorial 80 88 import sage.combinat.composition as composition 81 89 from sage.combinat.words.word import Word … … 84 92 from sage.combinat.combinat import stirling_number2 85 93 from sage.sets.set import Set, is_Set 86 94 from combinat import CombinatorialClass 95 87 96 from sage.misc.misc import prod 88 97 89 98 … … 162 171 163 172 164 173 class OrderedSetPartitions_s(CombinatorialClass): 174 r""" 175 Ordered set partitions of a set 176 """ 177 element_class = __builtin__.list 165 178 def __init__(self, s): 166 179 """ 167 180 TESTS:: … … 169 182 sage: OS = OrderedSetPartitions([1,2,3,4]) 170 183 sage: OS == loads(dumps(OS)) 171 184 True 185 sage: TestSuite(OS).run() 172 186 """ 187 Parent.__init__(self, category=FiniteEnumeratedSets()) 173 188 self.s = s 174 189 175 def _ _repr__(self):190 def _repr_(self): 176 191 """ 177 192 TESTS:: 178 193 … … 190 205 True 191 206 """ 192 207 #x must be a list 193 if not isinstance(x, list):208 if not isinstance(x, __builtin__.list): 194 209 return False 195 210 196 211 #The total number of elements in the list … … 232 247 sage: OrderedSetPartitions(5).cardinality() 233 248 541 234 249 """ 235 set = self.s236 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)) 237 252 253 def _fast_iterator(self): 254 r""" 255 Iterator over tuples instead of Set (faster iteration) 238 256 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 239 278 240 279 def __iter__(self): 241 """280 r""" 242 281 EXAMPLES:: 243 282 244 283 sage: [ p for p in OrderedSetPartitions([1,2,3]) ] … … 256 295 [{2, 3}, {1}], 257 296 [{1, 2, 3}]] 258 297 """ 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()) 262 299 263 264 class OrderedSetPartitions_sn(CombinatorialClass): 300 class OrderedSetPartitions_sn(OrderedSetPartitions_s): 301 r""" 302 Ordered partitions of a set with given number of atoms 303 """ 265 304 def __init__(self, s, n): 266 305 """ 267 306 TESTS:: … … 269 308 sage: OS = OrderedSetPartitions([1,2,3,4], 2) 270 309 sage: OS == loads(dumps(OS)) 271 310 True 311 sage: TestSuite(OS).run() 272 312 """ 313 Parent.__init__(self, category=FiniteEnumeratedSets()) 273 314 self.s = s 274 315 self.n = n 275 316 … … 285 326 sage: len(filter(lambda x: x in OS, OrderedSetPartitions([1,2,3,4]))) 286 327 14 287 328 """ 288 return x in OrderedSetPartitions_s(self.s) and len(x) == self.n329 return OrderedSetPartitions_s.__contains__(self,x) and len(x) == self.n 289 330 290 def _ _repr__(self):331 def _repr_(self): 291 332 """ 292 333 TESTS:: 293 334 … … 305 346 sage: OrderedSetPartitions(4,1).cardinality() 306 347 1 307 348 """ 308 set = self.s309 349 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 311 371 312 372 def __iter__(self): 313 373 """ … … 329 389 [{3}, {1, 2, 4}], 330 390 [{4}, {1, 2, 3}]] 331 391 """ 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()) 335 393 336 class OrderedSetPartitions_scomp(CombinatorialClass): 394 class OrderedSetPartitions_scomp(OrderedSetPartitions_s): 395 r""" 396 Ordered partitions of a set with given composition 397 """ 337 398 def __init__(self, s, comp): 338 399 """ 339 400 TESTS:: … … 341 402 sage: OS = OrderedSetPartitions([1,2,3,4], [2,1,1]) 342 403 sage: OS == loads(dumps(OS)) 343 404 True 405 sage: TestSuite(OS).run() 344 406 """ 407 Parent.__init__(self, category=FiniteEnumeratedSets()) 345 408 self.s = s 346 409 self.c = composition.Composition(comp) 347 410 … … 366 429 sage: len(filter(lambda x: x in OS, OrderedSetPartitions([1,2,3,4]))) 367 430 12 368 431 """ 369 return x in OrderedSetPartitions_s(self.s) and map(len, x) == self.c432 return OrderedSetPartitions_s.__contains__(self,x) and map(len, x) == self.c 370 433 371 434 def cardinality(self): 372 435 """ … … 383 446 sage: OrderedSetPartitions(5, [2,0,3]).cardinality() 384 447 10 385 448 """ 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]) 387 450 388 451 def __iter__(self): 389 452 """ … … 403 466 [{2, 4}, {3}, {1}], 404 467 [{3, 4}, {2}, {1}]] 405 468 """ 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 """ 406 510 comp = self.c 407 511 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)] 410 515 411 516 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]) 414 519 415 520 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)] 420 523 421 422 -
sage/combinat/split_nk.py
diff --git a/sage/combinat/split_nk.py b/sage/combinat/split_nk.py
a b 1 1 """ 2 2 Low-level splits 3 4 Authors: 5 6 - Mike Hansen (2007): original version 7 8 - Vincent Delecroix (2011): improvements 3 9 """ 4 10 #***************************************************************************** 5 11 # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, … … 32 38 sage: S = SplitNK(5,2); S 33 39 Splits of {0, ..., 4} into a set of size 2 and one of size 3 34 40 sage: S.first() 35 [ [0, 1], [2, 3, 4]]41 [(0, 1), (2, 3, 4)] 36 42 sage: S.last() 37 [ [3, 4], [0, 1, 2]]43 [(3, 4), (0, 1, 2)] 38 44 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)]] 49 55 """ 50 56 return SplitNK_nk(n,k) 51 57 … … 62 68 self._n = n 63 69 self._k = k 64 70 65 def _ _repr__(self):71 def _repr_(self): 66 72 """ 67 73 TESTS:: 68 74 69 75 sage: from sage.combinat.split_nk import SplitNK 70 sage: repr(SplitNK(5,2)) 76 sage: repr(SplitNK(5,2)) #indirect doctest 71 77 'Splits of {0, ..., 4} into a set of size 2 and one of size 3' 72 78 """ 73 79 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) … … 93 99 EXAMPLES:: 94 100 95 101 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)] 107 113 """ 108 114 range_n = range(self._n) 109 115 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))] 112 117 113 118 def random_element(self): 114 119 """ … … 119 124 120 125 sage: from sage.combinat.split_nk import SplitNK 121 126 sage: SplitNK(5,2).random_element() 122 [ [0, 2], [1, 3, 4]]127 [(0, 2), (1, 3, 4)] 123 128 """ 124 129 r = rnd.sample(xrange(self._n),self._n) 125 130 r0 = r[:self._k] 126 131 r1 = r[self._k:] 127 132 r0.sort() 128 133 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 1 1 r""" 2 2 Subsets 3 3 4 The combinatorial class of the subsets of a finite set. The set can5 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.4 The set of subsets of a finite set. The set can be given as a list or a Set 5 or else as an integer `n` which encodes the set `\{1,2,...,n\}`. 6 See :class:`Subsets` for more information and examples. 7 7 8 8 AUTHORS: 9 9 … … 11 11 12 12 - Florent Hivert (2009/02/06): doc improvements + new methods 13 13 14 - Vincent Delecroix (2011/03/10): use iterator from itertools, implement basic 15 random uniform generation 14 16 """ 15 17 #***************************************************************************** 16 18 # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, … … 34 36 import sage.combinat.choose_nk as choose_nk 35 37 import sage.misc.prandom as rnd 36 38 import __builtin__ 39 import copy 37 40 import itertools 38 from combinat import CombinatorialClass 41 from sage.structure.parent import Parent 42 from sage.structure.element import Element 39 43 from sage.sets.set import Set_object_enumerated 40 44 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets 41 45 from combinat import CombinatorialClass 42 46 43 47 def Subsets(s, k=None, submultiset=False): 44 48 """ 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)``). 50 53 51 A second optional parameter k can be given. In this case, Subsets returns52 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``. 53 56 54 57 Finally the option ``submultiset`` allows one to deal with sets with 55 58 repeated elements usually called multisets. … … 96 99 [['a', 'a'], ['a', 'b'], ['b', 'b']] 97 100 """ 98 101 if k is not None: 99 k =Integer(k)102 k = Integer(k) 100 103 101 104 if isinstance(s, (int, Integer)): 102 105 if s < 0: … … 117 120 else: 118 121 return Subsets_sk(s, k) 119 122 120 121 122 123 124 123 class Subsets_s(CombinatorialClass): 124 element_class = Set_object_enumerated 125 125 def __init__(self, s): 126 126 """ 127 127 TESTS:: … … 141 141 sage: S = sage.sets.set.Set_object_enumerated([1,2]) 142 142 sage: TestSuite(S).run() # todo: not implemented 143 143 """ 144 CombinatorialClass.__init__(self, category=FiniteEnumeratedSets())144 Parent.__init__(self, category=FiniteEnumeratedSets()) 145 145 self.s = Set(s) 146 146 147 def _ _repr__(self):147 def _repr_(self): 148 148 """ 149 149 TESTS:: 150 150 151 sage: repr(Subsets([1,2,3])) 151 sage: repr(Subsets([1,2,3])) #indirect doctest 152 152 'Subsets of {1, 2, 3}' 153 153 """ 154 154 return "Subsets of %s"%self.s … … 186 186 sage: Subsets(3).cardinality() 187 187 8 188 188 """ 189 return Integer(2** len(self.s))189 return Integer(2**self.s.cardinality()) 190 190 191 191 def first(self): 192 192 """ … … 216 216 """ 217 217 return self.s 218 218 219 220 219 def __iter__(self): 221 220 """ 222 221 Iterates through the subsets of s. … … 231 230 [{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}] 232 231 233 232 """ 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 240 236 241 237 def random_element(self): 242 238 """ … … 251 247 {5} 252 248 """ 253 249 lset = __builtin__.list(self.s) 254 n = len(self.s)255 250 return Set(filter(lambda x: rnd.randint(0,1), lset)) 256 251 257 252 def rank(self, sub): … … 311 306 else: 312 307 return Set([lset[i] for i in choose_nk.from_rank(r, n, k)]) 313 308 309 def _element_constructor(self,X): 310 return Set(X) 311 314 312 def _an_element_(self): 315 313 """ 316 314 Returns an example of subset. … … 326 324 """ 327 325 return self.unrank(self.cardinality() // 2) 328 326 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 328 class Subsets_sk(Subsets_s): 346 329 def __init__(self, s, k): 347 330 """ 348 331 TESTS:: … … 359 342 sage: S = Subsets(3,2) 360 343 sage: TestSuite(S).run(skip=["_test_elements"]) 361 344 """ 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 365 349 366 def _ _repr__(self):350 def _repr_(self): 367 351 """ 368 352 TESTS:: 369 353 370 sage: repr(Subsets(3,2)) 354 sage: repr(Subsets(3,2)) #indirect doctest 371 355 'Subsets of {1, 2, 3} of size 2' 372 356 """ 373 return "Subsets of %s of size %s"%(self.s, self.k)357 return Subsets_s._repr_(self) + " of size %s"%(self._k) 374 358 375 359 def __contains__(self, value): 376 360 """ … … 383 367 sage: Set([]) in S 384 368 False 385 369 """ 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) 393 371 394 372 def cardinality(self): 395 373 """ … … 412 390 sage: Subsets(3,4).cardinality() 413 391 0 414 392 """ 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) 420 396 421 397 def first(self): 422 398 """ … … 432 408 {1, 2} 433 409 sage: Subsets(3,4).first() 434 410 """ 435 if self. k not in range(len(self.s)+1):411 if self._k < 0 or self._k > len(self.s): 436 412 return None 437 413 else: 438 return Set(__builtin__.list(self.s)[:self.k]) 439 440 414 return Set(__builtin__.list(self.s)[:self._k]) 441 415 442 416 def last(self): 443 417 """ … … 453 427 {2, 3} 454 428 sage: Subsets(3,4).last() 455 429 """ 456 if self. k not in range(len(self.s)+1):430 if self._k not in range(len(self.s)+1): 457 431 return None 458 432 else: 459 return Set(__builtin__.list(self.s)[-self. k:])433 return Set(__builtin__.list(self.s)[-self._k:]) 460 434 435 def _fast_iterator(self): 436 r""" 437 Iterate through the subsets of size k if s. 461 438 439 Beware that this function yield tuples and not sets. If you need sets 440 use __iter__ 462 441 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) 463 448 464 449 def __iter__(self): 465 450 """ … … 467 452 468 453 EXAMPLES:: 469 454 470 sage: [sub for sub in Subsets(Set([1,2,3]), 2)]455 sage: Subsets(Set([1,2,3]), 2).list() 471 456 [{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() 473 458 [{1, 2}, {1, 3}, {2, 3}] 474 sage: [sub for sub in Subsets(3,2)]459 sage: Subsets(3,2).list() 475 460 [{1, 2}, {1, 3}, {2, 3}] 461 sage: Subsets(3,3).list() 462 [{1, 2, 3}] 476 463 """ 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()) 487 465 488 466 def random_element(self): 489 467 """ … … 500 478 lset = __builtin__.list(self.s) 501 479 n = len(self.s) 502 480 503 if self. k not in range(len(self.s)+1):481 if self._k not in range(len(self.s)+1): 504 482 return None 505 483 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()]) 507 485 508 486 def rank(self, sub): 509 487 """ … … 531 509 n = len(self.s) 532 510 r = 0 533 511 534 if self. k not in range(len(self.s)+1):512 if self._k not in range(len(self.s)+1): 535 513 return None 536 elif self. k != len(subset):514 elif self._k != len(subset): 537 515 return None 538 516 else: 539 517 return choose_nk.rank(index_list,n) 540 518 541 542 519 def unrank(self, r): 543 520 """ 544 521 Returns the subset of s that has rank k. … … 554 531 lset = __builtin__.list(self.s) 555 532 n = len(lset) 556 533 557 if self. k not in range(len(self.s)+1):534 if self._k not in range(len(self.s)+1): 558 535 return None 559 536 elif r >= self.cardinality() or r < 0: 560 537 return None 561 538 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)]) 563 540 564 541 def _an_element_(self): 565 542 """ … … 589 566 """ 590 567 return Set(x) 591 568 592 element_class = Set_object_enumerated593 569 570 #TODO: MultiSet data structure in Sage 571 572 def 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 588 def 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 594 605 595 606 class SubMultiset_s(CombinatorialClass): 596 607 """ … … 599 610 EXAMPLES:: 600 611 601 612 sage: S = Subsets([1,2,2,3], submultiset=True) 602 sage: S._s603 [1, 2, 2, 3]604 613 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``:: 606 616 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} 617 619 """ 618 620 def __init__(self, s): 619 621 """ … … 624 626 sage: S = Subsets([1,2,2,3], submultiset=True) 625 627 sage: Subsets([1,2,3,3], submultiset=True).cardinality() 626 628 12 629 sage: TestSuite(S).run() 627 630 """ 628 631 CombinatorialClass.__init__(self, category=FiniteEnumeratedSets()) 629 632 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) 634 636 635 self._s = s 636 self._indices = indices 637 self._multiplicities = multiplicities 638 639 def __repr__(self): 637 def _repr_(self): 640 638 """ 641 639 TESTS:: 642 640 643 641 sage: S = Subsets([1, 2, 2, 3], submultiset=True); S 644 642 SubMultiset of [1, 2, 2, 3] 645 643 """ 646 return "SubMultiset of %s"% self._s644 return "SubMultiset of %s"%dict_to_list(self._d) 647 645 648 646 def __contains__(self, s): 649 647 """ … … 663 661 sage: [4] in S 664 662 False 665 663 """ 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()) 667 742 668 743 def __iter__(self): 669 744 """ 670 Iterates through the subsets of the multiset ``self. _s``. Note745 Iterates through the subsets of the multiset ``self.s``. Note 671 746 that each subset is represented by a list of its elements rather than 672 747 a set since we can have multiplicities (no multiset data structure yet 673 748 in sage). … … 690 765 [1, 2, 2, 3]] 691 766 692 767 """ 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): 695 770 yield s 696 771 697 698 772 class SubMultiset_sk(SubMultiset_s): 699 773 """ 700 774 The combinatorial class of the subsets of size k of a multiset s. Note … … 704 778 705 779 EXAMPLES:: 706 780 707 sage: S = Subsets([1,2,3,3],2, 781 sage: S = Subsets([1,2,3,3],2,submultiset=True) 708 782 sage: S._k 709 783 2 710 784 sage: S.cardinality() … … 715 789 [3, 3] 716 790 sage: [sub for sub in S] 717 791 [[1, 2], [1, 3], [2, 3], [3, 3]] 718 sage: TestSuite(S).run()719 792 """ 720 793 def __init__(self, s, k): 721 794 """ 722 TEST ::795 TESTS:: 723 796 724 sage: S = Subsets([1,2,3,3],2, 797 sage: S = Subsets([1,2,3,3],2,submultiset=True) 725 798 sage: [sub for sub in S] 726 799 [[1, 2], [1, 3], [2, 3], [3, 3]] 800 sage: TestSuite(S).run() 727 801 """ 728 802 SubMultiset_s.__init__(self, s) 803 self._l = dict_to_list(self._d) 729 804 self._k = k 730 805 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): 732 852 """ 733 853 TESTS:: 734 854 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' 737 858 """ 738 return " SubMultiset of %s of size %s"%(self._s, self._k)859 return "%s of size %s"%(SubMultiset_s._repr_(self), self._k) 739 860 740 861 def __contains__(self, s): 741 862 """ … … 757 878 sage: [3, 3] in S 758 879 False 759 880 """ 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) 761 895 762 896 def __iter__(self): 763 897 """ 764 898 Iterates through the subsets of size ``self._k`` of the multiset 765 ``self. _s``. Note that each subset is represented by a list of the899 ``self.s``. Note that each subset is represented by a list of the 766 900 elements rather than a set since we can have multiplicities (no 767 901 multiset data structure yet in sage). 768 902 … … 773 907 [[1, 2], [1, 3], [2, 2], [2, 3]] 774 908 """ 775 909 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))], []) 778 913 -
sage/combinat/subword.py
diff --git a/sage/combinat/subword.py b/sage/combinat/subword.py
a b 1 1 r""" 2 2 Subwords 3 3 4 A subword of a word $w$is a word obtained by deleting the letters at some4 A subword of a word `w` is a word obtained by deleting the letters at some 5 5 (non necessarily adjacent) positions in `w`. It is not to be confused with the 6 6 notion of factor where one keeps adjacent positions in `w`. Sometimes it is 7 7 useful to allow repeated uses of the same letter of `w` in a "generalized" … … 21 21 22 22 - "nnu" is a subword with repetitions of "bonjour"; 23 23 24 A word can be given either as a string or as a list. 24 A word can be given either as a string, as a list or as a tuple. 25 26 27 As repetition can occur in the initial word, the subwords of a given words is 28 not a set in general but an enumerated multiset! 29 30 TODO: 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) 25 36 26 37 AUTHORS: 27 38 … … 29 40 30 41 - Florent Hivert (2009/02/06): doc improvements + new methods + bug fixes 31 42 32 43 - Vincent Delecroix (2011/10/03): link to itertools for faster generation, 44 documentation, random generation, improvements 33 45 """ 34 46 35 47 #***************************************************************************** … … 47 59 # http://www.gnu.org/licenses/ 48 60 #***************************************************************************** 49 61 50 import sage.combinat.combination as combination 51 from sage.rings.arith import factorial 62 from sage.structure.parent import Parent 63 64 import sage.rings.arith as arith 65 import sage.misc.prandom as prandom 66 from sage.rings.integer import Integer 52 67 import itertools 53 from combinat import CombinatorialClass 54 68 import choose_nk 69 from combinat import CombinatorialClass 55 70 56 71 def Subwords(w, k=None): 57 72 """ 58 Returns the combinatorial class of subwords of w. The word w can be given59 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. 60 75 61 76 If k is specified, then it returns the combinatorial class of 62 77 subwords of w of length k. … … 71 86 ['a', 'b', 'c'] 72 87 sage: S.list() 73 88 [[], ['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:: 76 113 77 114 sage: S = Subwords(['a','b','c'], 2); S 78 115 Subwords of ['a', 'b', 'c'] of length 2 79 116 sage: S.list() 80 117 [['a', 'b'], ['a', 'c'], ['b', 'c']] 81 118 """ 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) 84 130 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([]) 87 135 else: 88 return Subwords_wk(w,k )136 return Subwords_wk(w,k,build) 89 137 90 138 91 139 class Subwords_w(CombinatorialClass): 92 def __init__(self, w): 140 r""" 141 Subwords of a given word 142 """ 143 def __init__(self, w, build): 93 144 """ 94 145 TESTS:: 95 146 96 147 sage: S = Subwords([1,2,3]) 97 148 sage: S == loads(dumps(S)) 98 149 True 150 sage: TestSuite(S).run() 99 151 """ 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): 103 175 """ 104 176 TESTS:: 105 177 106 sage: repr(Subwords([1,2,3])) 178 sage: repr(Subwords([1,2,3])) # indirect doctest 107 179 'Subwords of [1, 2, 3]' 108 180 """ 109 return "Subwords of %s" %self.w181 return "Subwords of %s" %str(self._w) 110 182 111 183 def __contains__(self, w): 112 184 """ … … 116 188 True 117 189 sage: [2,3,3,4] in Subwords([1,2,3,4,3,4,4]) 118 190 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]) 120 192 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]) 122 194 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]) 124 196 False 125 197 sage: [2,3,3,4] in Subwords([1,2,3,4,3,4,4]) 126 198 True 127 199 sage: [2,3,3,1] in Subwords([1,2,3,4,3,4,4]) 128 200 False 129 201 """ 130 if smallest_positions(self. w, w) != False:202 if smallest_positions(self._w, w) != False: 131 203 return True 132 204 return False 133 205 … … 138 210 sage: Subwords([1,2,3]).cardinality() 139 211 8 140 212 """ 141 return 2**len(self.w)213 return Integer(2)**len(self._w) 142 214 143 215 def first(self): 144 216 """ … … 146 218 147 219 sage: Subwords([1,2,3]).first() 148 220 [] 221 sage: Subwords((1,2,3)).first() 222 () 223 sage: Subwords('123').first() 224 '' 149 225 """ 150 return []226 return self._build([]) 151 227 152 228 def last(self): 153 229 """ … … 155 231 156 232 sage: Subwords([1,2,3]).last() 157 233 [1, 2, 3] 234 sage: Subwords((1,2,3)).last() 235 (1, 2, 3) 236 sage: Subwords('123').last() 237 '123' 158 238 """ 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)) 160 259 161 260 def __iter__(self): 162 261 r""" 163 262 EXAMPLES:: 164 263 165 sage: [sw for sw in Subwords([1,2,3])]264 sage: Subwords([1,2,3]).list() 166 265 [[], [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'] 167 270 """ 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)]) 172 272 173 174 class Subwords_wk(CombinatorialClass): 175 def __init__(self, w, k): 273 class Subwords_wk(Subwords_w): 274 r""" 275 Subwords with fixed length of a given word 276 """ 277 def __init__(self, w, k, build): 176 278 """ 177 279 TESTS:: 178 280 179 281 sage: S = Subwords([1,2,3],2) 180 282 sage: S == loads(dumps(S)) 181 283 True 284 sage: TestSuite(S).run() 182 285 """ 183 self.w = w184 self. k = k286 Subwords_w.__init__(self,w,build) 287 self._k = k 185 288 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): 187 305 """ 188 306 TESTS:: 189 307 190 sage: repr(Subwords([1,2,3],2)) 308 sage: repr(Subwords([1,2,3],2)) # indirect doctest 191 309 'Subwords of [1, 2, 3] of length 2' 192 310 """ 193 return " Subwords of %s of length %s"%(self.w, self.k)311 return "%s of length %s" %(Subwords_w._repr_(self), self._k) 194 312 195 313 def __contains__(self, w): 196 314 """ … … 202 320 True 203 321 sage: [2,3,3,4] in Subwords([1,2,3,4,3,4,4],3) 204 322 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) 206 324 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) 208 326 False 209 327 """ 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) 215 329 216 330 def cardinality(self): 217 331 r""" … … 222 336 sage: Subwords([1,2,3], 2).cardinality() 223 337 3 224 338 """ 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) 229 340 230 341 def first(self): 231 342 r""" … … 235 346 [1, 2] 236 347 sage: Subwords([1,2,3],0).first() 237 348 [] 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 '' 238 357 """ 239 return self. w[:self.k]358 return self._w[:self._k] 240 359 241 360 def last(self): 242 361 r""" … … 244 363 245 364 sage: Subwords([1,2,3],2).last() 246 365 [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 '' 247 381 """ 382 if self._k: 383 return self._w[-self._k:] 384 return self.first() 248 385 249 return self.w[-self.k:] 386 def random_element(self): 387 r""" 388 Return a random subword of given length with uniform law 250 389 390 EXAMPLES:: 251 391 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) 252 407 253 408 def __iter__(self): 254 409 """ 255 410 EXAMPLES:: 256 411 257 sage: [sw for sw in Subwords([1,2,3],2)]412 sage: Subwords([1,2,3],2).list() 258 413 [[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() 260 415 [[]] 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 [''] 261 424 """ 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) 272 432 273 433 def smallest_positions(word, subword, pos = 0): 274 434 """ … … 315 475 """ 316 476 pos -= 1 317 477 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): 320 480 if j == len(word): 321 481 return False 322 482 if word[j] == subword[i]: