Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/integer_list.py
4036 views
1
r"""
2
Tools for generating lists of integers in lexicographic order
3
4
IMPORTANT NOTE (2009/02):
5
The internal functions in this file will be deprecated soon.
6
Please only use them through IntegerListsLex.
7
8
"""
9
#*****************************************************************************
10
# Copyright (C) 2007 Mike Hansen <[email protected]>,
11
#
12
# Distributed under the terms of the GNU General Public License (GPL)
13
#
14
# This code is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
# General Public License for more details.
18
#
19
# The full text of the GPL is available at:
20
#
21
# http://www.gnu.org/licenses/
22
#*****************************************************************************
23
24
import generator
25
from sage.rings.arith import binomial
26
from sage.rings.infinity import infinity
27
from sage.rings.integer_ring import ZZ
28
from sage.misc.lazy_attribute import lazy_attribute
29
import __builtin__
30
31
def first(n, min_length, max_length, floor, ceiling, min_slope, max_slope):
32
"""
33
Returns the lexicographically smallest valid composition of n
34
satisfying the conditions.
35
36
.. warning::
37
38
INTERNAL FUNCTION! DO NOT USE DIRECTLY!
39
40
Preconditions:
41
42
- minslope < maxslope
43
44
- floor and ceiling need to satisfy the slope constraints,
45
e.g. be obtained fromcomp2floor or comp2ceil
46
47
- floor must be below ceiling to ensure
48
the existence a valid composition
49
50
TESTS::
51
52
sage: import sage.combinat.integer_list as integer_list
53
sage: f = lambda l: lambda i: l[i-1]
54
sage: f([0,1,2,3,4,5])(1)
55
0
56
sage: integer_list.first(12, 4, 4, f([0,0,0,0]), f([4,4,4,4]), -1, 1)
57
[4, 3, 3, 2]
58
sage: integer_list.first(36, 9, 9, f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -1, 1)
59
[7, 6, 5, 5, 4, 3, 3, 2, 1]
60
sage: integer_list.first(25, 9, 9, f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 1)
61
[7, 6, 5, 4, 2, 1, 0, 0, 0]
62
sage: integer_list.first(36, 9, 9, f([3,3,3,2,1,4,2,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 1)
63
[7, 6, 5, 5, 5, 4, 3, 1, 0]
64
"""
65
66
#Increase minl until n <= sum([ceiling(i) for i in range(min_length)])
67
#This may run forever!
68
N = sum([ceiling(i) for i in range(1,min_length+1)])
69
while N < n:
70
min_length += 1
71
if min_length > max_length:
72
return None
73
74
if ceiling(min_length) == 0 and max_slope <= 0:
75
return None
76
77
N += ceiling(min_length)
78
79
80
#This is the place where it's required that floor(i)
81
#respects the floor conditions.
82
n -= sum([floor(i) for i in range(1, min_length+1)])
83
if n < 0:
84
return None
85
86
#Now we know that we can build the composition inside
87
#the "tube" [1 ... min_length] * [floor, ceiling]
88
89
if min_slope == -infinity:
90
#Easy case: min_slope == -infinity
91
result = []
92
i = min_length
93
for i in range(1,min_length+1):
94
if n <= ceiling(i) - floor(i):
95
result.append(floor(i) + n)
96
break
97
else:
98
result.append(ceiling(i))
99
n -= ceiling(i) - floor(i)
100
101
result += [floor(j) for j in range(i+1,min_length+1)]
102
return result
103
104
else:
105
if n == 0 and min_length == 0:
106
return []
107
108
low_x = 1
109
low_y = floor(1)
110
high_x = 1
111
high_y = floor(1)
112
113
while n > 0:
114
#invariant after each iteration of the loop:
115
#[low_x, low_y] is the coordinate of the rightmost point of the
116
#current diagonal s.t. floor(low_x) < low_y
117
low_y += 1
118
while low_x < min_length and low_y+min_slope > floor(low_x + 1):
119
low_x += 1
120
low_y += min_slope
121
122
high_y += 1
123
while high_y > ceiling(high_x):
124
high_x += 1
125
high_y += min_slope
126
127
n -= low_x - high_x + 1
128
129
#print "lx, ly, hw, hy, n", low_x, low_y, high_x, high_y, n
130
#print (high_x-1 - 1 + 1) + (n + 1 - 0 + 1) + ( low_x-high_x - n + 1) + (min_length - (low_x + 1) +1)
131
result = []
132
result += [ ceiling(j) for j in range(1,high_x)]
133
result += [ high_y + min_slope*i - 1 for i in range(0, -n) ]
134
result += [ high_y + min_slope*i for i in range(-n, low_x-high_x+1)]
135
result += [ floor(j) for j in range(low_x+1,min_length+1) ]
136
137
return result
138
139
140
def lower_regular(comp, min_slope, max_slope):
141
"""
142
Returns the uppest regular composition below comp
143
144
TESTS::
145
146
sage: import sage.combinat.integer_list as integer_list
147
sage: integer_list.lower_regular([4,2,6], -1, 1)
148
[3, 2, 3]
149
sage: integer_list.lower_regular([4,2,6], -1, infinity)
150
[3, 2, 6]
151
sage: integer_list.lower_regular([1,4,2], -1, 1)
152
[1, 2, 2]
153
sage: integer_list.lower_regular([4,2,6,3,7], -2, 1)
154
[4, 2, 3, 3, 4]
155
sage: integer_list.lower_regular([4,2,infinity,3,7], -2, 1)
156
[4, 2, 3, 3, 4]
157
sage: integer_list.lower_regular([1, infinity, 2], -1, 1)
158
[1, 2, 2]
159
sage: integer_list.lower_regular([infinity, 4, 2], -1, 1)
160
[4, 3, 2]
161
"""
162
163
new_comp = comp[:]
164
for i in range(1, len(new_comp)):
165
new_comp[i] = min(new_comp[i], new_comp[i-1] + max_slope)
166
167
for i in reversed(range(len(new_comp)-1)):
168
new_comp[i] = min( new_comp[i], new_comp[i+1] - min_slope)
169
170
return new_comp
171
172
def rightmost_pivot(comp, min_length, max_length, floor, ceiling, min_slope, max_slope):
173
"""
174
TESTS::
175
176
sage: import sage.combinat.integer_list as integer_list
177
sage: f = lambda l: lambda i: l[i-1]
178
sage: integer_list.rightmost_pivot([7,6,5,5,4,3,3,2,1], 9, 9, f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -1, 0)
179
[7, 2]
180
sage: integer_list.rightmost_pivot([7,6,5,5,4,3,3,2,1], 9, 9,f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 0)
181
[7, 1]
182
sage: integer_list.rightmost_pivot([7,6,5,5,4,3,3,2,1], 9, 9,f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 4)
183
[8, 1]
184
sage: integer_list.rightmost_pivot([7,6,5,5,4,3,3,2,1], 9, 9,f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 1)
185
[8, 1]
186
sage: integer_list.rightmost_pivot([7,6,5,5,5,5,5,4,4], 9, 9,f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 1)
187
sage: integer_list.rightmost_pivot([3,3,3,2,1,1,0,0,0], 9, 9,f([3,3,3,2,1,1,0,0,0]), f([7,6,5,5,5,5,5,4,4]), -2, 1)
188
sage: g = lambda x: lambda i: x
189
sage: integer_list.rightmost_pivot([1],1,1,g(0),g(2),-10, 10)
190
sage: integer_list.rightmost_pivot([1,2],2,2,g(0),g(2),-10, 10)
191
sage: integer_list.rightmost_pivot([1,2],2,2,g(1),g(2), -10, 10)
192
sage: integer_list.rightmost_pivot([1,2],2,3,g(1),g(2), -10, 10)
193
[2, 1]
194
sage: integer_list.rightmost_pivot([2,2],2,3,g(2),g(2),-10, 10)
195
sage: integer_list.rightmost_pivot([2,3],2,3,g(2),g(2),-10,+10)
196
sage: integer_list.rightmost_pivot([3,2],2,3,g(2),g(2),-10,+10)
197
sage: integer_list.rightmost_pivot([3,3],2,3,g(2),g(2),-10,+10)
198
[1, 2]
199
sage: integer_list.rightmost_pivot([6],1,3,g(0),g(6),-1,0)
200
[1, 0]
201
sage: integer_list.rightmost_pivot([6],1,3,g(0),g(6),-2,0)
202
[1, 0]
203
sage: integer_list.rightmost_pivot([7,9,8,7],1,5,g(0),g(10),-1,10)
204
[2, 6]
205
sage: integer_list.rightmost_pivot([7,9,8,7],1,5,g(5),g(10),-10,10)
206
[3, 5]
207
sage: integer_list.rightmost_pivot([7,9,8,7],1,5,g(5),g(10),-1,10)
208
[2, 6]
209
sage: integer_list.rightmost_pivot([7,9,8,7],1,5,g(4),g(10),-2,10)
210
[3, 7]
211
sage: integer_list.rightmost_pivot([9,8,7],1,4,g(4),g(10),-2,0)
212
[1, 4]
213
sage: integer_list.rightmost_pivot([1,3],1,5,lambda i: i,g(10),-10,10)
214
sage: integer_list.rightmost_pivot([1,4],1,5,lambda i: i,g(10),-10,10)
215
sage: integer_list.rightmost_pivot([2,4],1,5,lambda i: i,g(10),-10,10)
216
[1, 1]
217
"""
218
219
y = len(comp) + 1
220
while y <= max_length:
221
if ceiling(y) > 0:
222
break
223
if max_slope <= 0:
224
y = max_length + 1
225
break
226
y += 1
227
228
x = len(comp)
229
if x == 0:
230
return None
231
232
ceilingx_x = comp[x-1]-1
233
floorx_x = floor(x)
234
if x > 1:
235
floorx_x = max(floorx_x, comp[x-2]+min_slope)
236
237
F = comp[x-1] - floorx_x
238
G = ceilingx_x - comp[x-1] #this is -1
239
240
highX = x
241
lowX = x
242
243
while not (ceilingx_x >= floorx_x and
244
(G >= 0 or
245
( y < max_length +1 and
246
F - max(floor(y), floorx_x + (y-x)*min_slope) >= 0 and
247
G + min(ceiling(y), ceilingx_x + (y-x)*max_slope) >= 0 ))):
248
249
if x == 1:
250
return None
251
252
x -= 1
253
254
oldfloorx_x = floorx_x
255
ceilingx_x = comp[x-1] - 1
256
floorx_x = floor(x)
257
if x > 1:
258
floorx_x = max(floorx_x, comp[x-2]+min_slope)
259
260
min_slope_lowX = min_slope*(lowX - x)
261
max_slope_highX = max_slope*(highX - x)
262
263
264
#Update G
265
if max_slope == infinity:
266
#In this case, we have
267
# -- ceiling_x(i) = ceiling(i) for i > x
268
# --G >= 0 or G = -1
269
G += ceiling(x+1)-comp[x]
270
else:
271
G += (highX - x)*( (comp[x-1]+max_slope) - comp[x]) - 1
272
temp = (ceilingx_x + max_slope_highX) - ceiling(highX)
273
while highX > x and ( temp >= 0 ):
274
G -= temp
275
highX -= 1
276
max_slope_highX = max_slope*(highX-x)
277
temp = (ceilingx_x + max_slope_highX) - ceiling(highX)
278
279
if G >= 0 and comp[x-1] > floorx_x:
280
#By case 1, x is at the rightmost pivot position
281
break
282
283
#Update F
284
if y < max_length+1:
285
F += comp[x-1] - floorx_x
286
if min_slope != -infinity:
287
F += (lowX - x) * (oldfloorx_x - (floorx_x + min_slope))
288
temp = floor(lowX) - (floorx_x + min_slope_lowX)
289
while lowX > x and temp >= 0:
290
F -= temp
291
lowX -= 1
292
min_slope_lowX = min_slope*(lowX-x)
293
temp = floor(lowX) - (floorx_x + min_slope_lowX)
294
295
return [x, floorx_x]
296
297
def next(comp, min_length, max_length, floor, ceiling, min_slope, max_slope):
298
"""
299
Returns the next integer list after comp that satisfies the
300
constraints.
301
302
.. warning::
303
304
INTERNAL FUNCTION! DO NOT USE DIRECTLY!
305
306
EXAMPLES::
307
308
sage: from sage.combinat.integer_list import next
309
sage: IV = IntegerVectors(2,3,min_slope=0)
310
sage: params = IV._parameters()
311
sage: next([0,1,1], *params)
312
[0, 0, 2]
313
"""
314
x = rightmost_pivot( comp, min_length, max_length, floor, ceiling, min_slope, max_slope)
315
if x == None:
316
return None
317
[x, low] = x
318
high = comp[x-1]-1
319
320
## // Build wrappers around floor and ceiling to take into
321
## // account the new constraints on the value of compo[x].
322
## //
323
## // Efficiency note: they are not wrapped more than once, since
324
## // the method Next calls first, but not the converse.
325
326
if min_slope == -infinity:
327
new_floor = lambda i: floor(x+(i-1))
328
else:
329
new_floor = lambda i: max(floor(x+(i-1)), low+(i-1)*min_slope)
330
331
if max_slope == infinity:
332
new_ceiling = lambda i: comp[x-1] - 1 if i == 1 else ceiling(x+(i-1))
333
else:
334
new_ceiling = lambda i: min(ceiling(x+(i-1)), high+(i-1)*max_slope)
335
336
337
res = []
338
res += comp[:x-1]
339
res += first(sum(comp[x-1:]), max(min_length-x+1, 0), max_length-x+1,
340
new_floor, new_ceiling, min_slope, max_slope)
341
return res
342
343
344
345
def iterator(n, min_length, max_length, floor, ceiling, min_slope, max_slope):
346
"""
347
.. warning::
348
349
INTERNAL FUNCTION! DO NOT USE DIRECTLY!
350
351
EXAMPLES::
352
353
sage: from sage.combinat.integer_list import iterator
354
sage: IV = IntegerVectors(2,3,min_slope=0)
355
sage: params = IV._parameters()
356
sage: list(iterator(2,*params))
357
[[0, 1, 1], [0, 0, 2]]
358
"""
359
#from sage.misc.misc import deprecation
360
#deprecation("sage.combinat.integer_list.iterator is deprecated. Please use IntegerListsLex(...)")
361
succ = lambda x: next(x, min_length, max_length, floor, ceiling, min_slope, max_slope)
362
363
#Handle the case where n is a list of integers
364
if isinstance(n, __builtin__.list):
365
iterators = [iterator(i, min_length, max_length, floor, ceiling, min_slope, max_slope) for i in range(n[0], min(n[1]+1,upper_bound(min_length, max_length, floor, ceiling, min_slope, max_slope)))]
366
367
return generator.concat(iterators)
368
else:
369
f = first(n, min_length, max_length, floor, ceiling, min_slope, max_slope)
370
if f == None:
371
return generator.element(None, 0)
372
return generator.successor(f, succ)
373
374
def list(n, min_length, max_length, floor, ceiling, min_slope, max_slope):
375
"""
376
.. warning::
377
378
THIS FUNCTION IS DEPRECATED!
379
380
Please use IntegersListsLex(...) instead
381
382
EXAMPLES::
383
384
sage: import sage.combinat.integer_list as integer_list
385
sage: g = lambda x: lambda i: x
386
sage: integer_list.list(0,0,infinity,g(1),g(infinity),0,infinity)
387
[[]]
388
sage: integer_list.list(0,0,infinity,g(1),g(infinity),0,0)
389
[[]]
390
sage: integer_list.list(0, 0, 0, g(1), g(infinity), 0, 0)
391
[[]]
392
sage: integer_list.list(0, 0, 0, g(0), g(infinity), 0, 0)
393
[[]]
394
sage: integer_list.list(0, 0, infinity, g(1), g(infinity), 0, infinity)
395
[[]]
396
sage: integer_list.list(1, 0, infinity, g(1), g(infinity), 0, infinity)
397
[[1]]
398
sage: integer_list.list(0, 1, infinity, g(1), g(infinity), 0, infinity)
399
[]
400
sage: integer_list.list(0, 1, infinity, g(0), g(infinity), 0, infinity)
401
[[0]]
402
sage: integer_list.list(3, 0, 2, g(0), g(infinity), -infinity, infinity)
403
[[3], [2, 1], [1, 2], [0, 3]]
404
sage: partitions = (0, infinity, g(0), g(infinity), -infinity, 0)
405
sage: partitions_min_2 = (0, infinity, g(2), g(infinity), -infinity, 0)
406
sage: compositions = (0, infinity, g(1), g(infinity), -infinity, infinity)
407
sage: integer_vectors = lambda l: (l, l, g(0), g(infinity), -infinity, infinity)
408
sage: lower_monomials = lambda c: (len(c), len(c), g(0), lambda i: c[i], -infinity, infinity)
409
sage: upper_monomials = lambda c: (len(c), len(c), g(0), lambda i: c[i], -infinity, infinity)
410
sage: constraints = (0, infinity, g(1), g(infinity), -1, 0)
411
sage: integer_list.list(6, *partitions)
412
[[6],
413
[5, 1],
414
[4, 2],
415
[4, 1, 1],
416
[3, 3],
417
[3, 2, 1],
418
[3, 1, 1, 1],
419
[2, 2, 2],
420
[2, 2, 1, 1],
421
[2, 1, 1, 1, 1],
422
[1, 1, 1, 1, 1, 1]]
423
sage: integer_list.list(6, *constraints)
424
[[6],
425
[3, 3],
426
[3, 2, 1],
427
[2, 2, 2],
428
[2, 2, 1, 1],
429
[2, 1, 1, 1, 1],
430
[1, 1, 1, 1, 1, 1]]
431
sage: integer_list.list(1, *partitions_min_2)
432
[]
433
sage: integer_list.list(2, *partitions_min_2)
434
[[2]]
435
sage: integer_list.list(3, *partitions_min_2)
436
[[3]]
437
sage: integer_list.list(4, *partitions_min_2)
438
[[4], [2, 2]]
439
sage: integer_list.list(5, *partitions_min_2)
440
[[5], [3, 2]]
441
sage: integer_list.list(6, *partitions_min_2)
442
[[6], [4, 2], [3, 3], [2, 2, 2]]
443
sage: integer_list.list(7, *partitions_min_2)
444
[[7], [5, 2], [4, 3], [3, 2, 2]]
445
sage: integer_list.list(9, *partitions_min_2)
446
[[9], [7, 2], [6, 3], [5, 4], [5, 2, 2], [4, 3, 2], [3, 3, 3], [3, 2, 2, 2]]
447
sage: integer_list.list(10, *partitions_min_2)
448
[[10],
449
[8, 2],
450
[7, 3],
451
[6, 4],
452
[6, 2, 2],
453
[5, 5],
454
[5, 3, 2],
455
[4, 4, 2],
456
[4, 3, 3],
457
[4, 2, 2, 2],
458
[3, 3, 2, 2],
459
[2, 2, 2, 2, 2]]
460
sage: integer_list.list(4, *compositions)
461
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 3], [1, 2, 1], [1, 1, 2], [1, 1, 1, 1]]
462
"""
463
#deprecation("sage.combinat.integer_list.list(...) is deprecated. Please use list(IntegerListsLex(...))")
464
return __builtin__.list(iterator(n, min_length, max_length, floor, ceiling, min_slope, max_slope))
465
466
def upper_regular(comp, min_slope, max_slope):
467
"""
468
Returns the uppest regular composition above comp.
469
470
TESTS::
471
472
sage: import sage.combinat.integer_list as integer_list
473
sage: integer_list.upper_regular([4,2,6],-1,1)
474
[4, 5, 6]
475
sage: integer_list.upper_regular([4,2,6],-2, 1)
476
[4, 5, 6]
477
sage: integer_list.upper_regular([4,2,6,3,7],-2, 1)
478
[4, 5, 6, 6, 7]
479
sage: integer_list.upper_regular([4,2,6,1], -2, 1)
480
[4, 5, 6, 4]
481
"""
482
483
new_comp = comp[:]
484
for i in range(1, len(new_comp)):
485
new_comp[i] = max(new_comp[i], new_comp[i-1] + min_slope)
486
487
for i in reversed(range(len(new_comp)-1)):
488
new_comp[i] = max( new_comp[i], new_comp[i+1] - max_slope)
489
490
return new_comp
491
492
def comp2floor(f, min_slope, max_slope):
493
"""
494
Given a composition, returns the lowest regular function N->N above
495
it.
496
497
EXAMPLES::
498
499
sage: from sage.combinat.integer_list import comp2floor
500
sage: f = comp2floor([2, 1, 1],-1,0)
501
sage: [f(i) for i in range(10)]
502
[2, 1, 1, 1, 2, 3, 4, 5, 6, 7]
503
"""
504
if len(f) == 0: return lambda i: 0
505
floor = upper_regular(f, min_slope, max_slope)
506
return lambda i: floor[i] if i < len(floor) else max(0, floor[-1]-(i-len(floor))*min_slope)
507
508
509
def comp2ceil(c, min_slope, max_slope):
510
"""
511
Given a composition, returns the lowest regular function N->N below
512
it.
513
514
EXAMPLES::
515
516
sage: from sage.combinat.integer_list import comp2ceil
517
sage: f = comp2ceil([2, 1, 1],-1,0)
518
sage: [f(i) for i in range(10)]
519
[2, 1, 1, 1, 2, 3, 4, 5, 6, 7]
520
"""
521
if len(c) == 0: return lambda i: 0
522
ceil = lower_regular(c, min_slope, max_slope)
523
return lambda i: ceil[i] if i < len(ceil) else max(0, ceil[-1]-(i-len(ceil))*min_slope)
524
525
526
def upper_bound(min_length, max_length, floor, ceiling, min_slope, max_slope):
527
"""
528
Compute a coarse upper bound on the size of a vector satisfying the
529
constraints.
530
531
TESTS::
532
533
sage: import sage.combinat.integer_list as integer_list
534
sage: f = lambda x: lambda i: x
535
sage: integer_list.upper_bound(0,4,f(0), f(1),-infinity,infinity)
536
4
537
sage: integer_list.upper_bound(0, infinity, f(0), f(1), -infinity, infinity)
538
+Infinity
539
sage: integer_list.upper_bound(0, infinity, f(0), f(1), -infinity, -1)
540
1
541
sage: integer_list.upper_bound(0, infinity, f(0), f(5), -infinity, -1)
542
15
543
sage: integer_list.upper_bound(0, infinity, f(0), f(5), -infinity, -2)
544
9
545
"""
546
from sage.functions.all import floor as flr
547
if max_length < infinity:
548
return sum( [ ceiling(j) for j in range(max_length)] )
549
elif max_slope < 0 and ceiling(1) < infinity:
550
maxl = flr(-ceiling(1)/max_slope)
551
return ceiling(1)*(maxl+1) + binomial(maxl+1,2)*max_slope
552
#FIXME: only checking the first 10000 values, but that should generally
553
#be enough
554
elif [ceiling(j) for j in range(10000)] == [0]*10000:
555
return 0
556
else:
557
return infinity
558
559
560
561
def is_a(comp, min_length, max_length, floor, ceiling, min_slope, max_slope):
562
"""
563
Returns True if comp meets the constraints imposed by the
564
arguments.
565
566
.. warning::
567
568
INTERNAL FUNCTION! DO NOT USE DIRECTLY!
569
570
EXAMPLES::
571
572
sage: from sage.combinat.integer_list import is_a
573
sage: IV = IntegerVectors(2,3,min_slope=0)
574
sage: params = IV._parameters()
575
sage: all([is_a(iv, *params) for iv in IV])
576
True
577
"""
578
if len(comp) < min_length or len(comp) > max_length:
579
return False
580
for i in range(len(comp)):
581
if comp[i] < floor(i+1):
582
return False
583
if comp[i] > ceiling(i+1):
584
return False
585
for i in range(len(comp)-1):
586
slope = comp[i+1] - comp[i]
587
if slope < min_slope or slope > max_slope:
588
return False
589
return True
590
591
from combinat import CombinatorialClass
592
593
class IntegerListsLex(CombinatorialClass):
594
r"""
595
A combinatorial class `C` for integer lists satisfying certain
596
sum, length, upper/lower bound and regularity constraints. The
597
purpose of this tool is mostly to provide a Constant Amortized
598
Time iterator through those lists, in lexicographic order.
599
600
INPUT:
601
602
- ``n`` - a non negative integer
603
- ``min_length`` - a non negative integer
604
- ``max_length`` - an integer or `\infty`
605
- ``length`` - an integer; overrides min_length and max_length if specified
606
- ``floor`` - a function `f` (or list); defaults to ``lambda i: 0``
607
- ``ceiling`` - a function `f` (or list); defaults to ``lambda i: infinity``
608
- ``min_slope`` - an integer or `-\infty`; defaults to `-\infty`
609
- ``max_slope`` - an integer or `+\infty`; defaults to `+\infty`
610
611
An *integer list* is a list `l` of nonnegative integers, its
612
*parts*. The *length* of `l` is the number of its parts;
613
the *sum* of `l` is the sum of its parts.
614
615
.. note::
616
617
Two valid integer lists are considered equivalent if they only
618
differ by trailing zeroes. In this case, only the list with the
619
least number of trailing zeroes will be produced.
620
621
The constraints on the lists are as follow:
622
623
- Sum: `sum(l) == n`
624
625
- Length: `min\_length \leq len(l) \leq max\_length`
626
627
- Lower and upper bounds: `floor(i) \leq l[i] \leq ceiling(i)`, for `i=0,\dots,len(l)`
628
629
- Regularity condition: `minSlope \leq l[i+1]-l[i] \leq maxSlope`, for `i=0,\dots,len(l)-1`
630
631
This is a generic low level tool. The interface has been designed
632
with efficiency in mind. It is subject to incompatible changes in
633
the future. More user friendly interfaces are provided by high
634
level tools like Partitions or Compositions.
635
636
EXAMPLES:
637
638
We create the combinatorial class of lists of length 3 and sum 2::
639
640
sage: C = IntegerListsLex(2, length=3)
641
sage: C
642
Integer lists of sum 2 satisfying certain constraints
643
sage: C.cardinality()
644
6
645
sage: [p for p in C]
646
[[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]]
647
648
sage: [2, 0, 0] in C
649
True
650
sage: [2, 0, 1] in C
651
False
652
sage: "a" in C
653
False
654
sage: ["a"] in C
655
False
656
657
sage: C.first()
658
[2, 0, 0]
659
660
One can specify lower and upper bound on each part::
661
662
sage: list(IntegerListsLex(5, length = 3, floor = [1,2,0], ceiling = [3,2,3]))
663
[[3, 2, 0], [2, 2, 1], [1, 2, 2]]
664
665
Using the slope condition, one can generate integer partitions
666
(but see :mod:`sage.combinat.partition.Partitions`)::
667
668
sage: list(IntegerListsLex(4, max_slope=0))
669
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
670
671
This is the list of all partitions of `7` with parts at least `2`::
672
673
sage: list(IntegerListsLex(7, max_slope = 0, min_part = 2))
674
[[7], [5, 2], [4, 3], [3, 2, 2]]
675
676
This is the list of all partitions of `5` and length at most 3
677
which are bounded below by [2,1,1]::
678
679
sage: list(IntegerListsLex(5, max_slope = 0, max_length = 3, floor = [2,1,1]))
680
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]]
681
682
Note that [5] is considered valid, because the lower bound
683
constraint only apply to existing positions in the list. To
684
obtain instead the partitions containing [2,1,1], one need to
685
use min_length::
686
687
sage: list(IntegerListsLex(5, max_slope = 0, min_length = 3, max_length = 3, floor = [2,1,1]))
688
[[3, 1, 1], [2, 2, 1]]
689
690
This is the list of all partitions of `5` which are contained in [3,2,2]::
691
692
sage: list(IntegerListsLex(5, max_slope = 0, max_length = 3, ceiling = [3,2,2]))
693
[[3, 2], [3, 1, 1], [2, 2, 1]]
694
695
696
This is the list of all compositions of `4` (but see Compositions)::
697
698
sage: list(IntegerListsLex(4, min_part = 1))
699
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 3], [1, 2, 1], [1, 1, 2], [1, 1, 1, 1]]
700
701
This is the list of all integer vectors of sum `4` and length `3`::
702
703
sage: list(IntegerListsLex(4, length = 3))
704
[[4, 0, 0], [3, 1, 0], [3, 0, 1], [2, 2, 0], [2, 1, 1], [2, 0, 2], [1, 3, 0], [1, 2, 1], [1, 1, 2], [1, 0, 3], [0, 4, 0], [0, 3, 1], [0, 2, 2], [0, 1, 3], [0, 0, 4]]
705
706
707
There are all the lists of sum 4 and length 4 such that l[i] <= i::
708
709
sage: list(IntegerListsLex(4, length=4, ceiling=lambda i: i))
710
[[0, 1, 2, 1], [0, 1, 1, 2], [0, 1, 0, 3], [0, 0, 2, 2], [0, 0, 1, 3]]
711
712
This is the list of all monomials of degree `4` which divide the
713
monomial `x^3y^1z^2` (a monomial being identified with its
714
exponent vector)::
715
716
sage: R.<x,y,z> = QQ[]
717
sage: m = [3,1,2]
718
sage: def term(exponents):
719
... return x^exponents[0] * y^exponents[1] * z^exponents[2]
720
...
721
sage: list( IntegerListsLex(4, length = len(m), ceiling = m, element_constructor = term) )
722
[x^3*y, x^3*z, x^2*y*z, x^2*z^2, x*y*z^2]
723
724
Note the use of the element_constructor feature.
725
726
In general, the complexity of the iteration algorithm is constant
727
time amortized for each integer list produced. There is one
728
degenerate case though where the algorithm may run forever without
729
producing anything. If max_length is `+\infty` and max_slope `>0`,
730
testing whether there exists a valid integer list of sum `n` may
731
be only semi-decidable. In the following example, the algorithm
732
will enter an infinite loop, because it needs to decide whether
733
`ceiling(i)` is nonzero for some `i`::
734
735
sage: list( IntegerListsLex(1, ceiling = lambda i: 0) ) # todo: not implemented
736
737
738
.. note::
739
740
Caveat: counting is done by brute force generation. In some
741
special cases, it would be possible to do better by counting
742
techniques for integral point in polytopes.
743
744
.. note::
745
746
Caveat: with the current implementation, the constraints should
747
satisfy the following conditions:
748
749
- The upper and lower bounds themselves should satisfy the
750
slope constraints.
751
752
- The maximal and minimal slopes values should not be equal.
753
754
- The maximal and minimal part values should not be equal.
755
756
Those conditions are not checked by the algorithm, and the
757
result may be completely incorrect if they are not satisfied:
758
759
In the following example, the slope condition is not satisfied
760
by the upper bound on the parts, and [3,3] is erroneously
761
included in the result::
762
763
sage: list(IntegerListsLex(6, max_part=3,max_slope=-1))
764
[[3, 3], [3, 2, 1]]
765
766
767
With some work, this could be fixed without affecting the overall
768
complexity and efficiency. Also, the generation algorithm could be
769
extended to deal with non-constant slope constraints and with
770
negative parts, as well as to accept a range parameter instead of
771
a single integer for the sum `n` of the lists (the later was
772
readily implemented in MuPAD-Combinat). Encouragements,
773
suggestions, and help are welcome.
774
775
TODO: integrate all remaining tests from http://mupad-combinat.svn.sourceforge.net/viewvc/mupad-combinat/trunk/MuPAD-Combinat/lib/COMBINAT/TEST/MachineIntegerListsLex.tst
776
777
TESTS::
778
779
sage: g = lambda x: lambda i: x
780
sage: list(IntegerListsLex(0, floor = g(1), min_slope = 0))
781
[[]]
782
sage: list(IntegerListsLex(0, floor = g(1), min_slope = 0, max_slope = 0))
783
[[]]
784
sage: list(IntegerListsLex(0, max_length=0, floor = g(1), min_slope = 0, max_slope = 0))
785
[[]]
786
sage: list(IntegerListsLex(0, max_length=0, floor = g(0), min_slope = 0, max_slope = 0))
787
[[]]
788
sage: list(IntegerListsLex(0, min_part = 1, min_slope = 0))
789
[[]]
790
sage: list(IntegerListsLex(1, min_part = 1, min_slope = 0))
791
[[1]]
792
sage: list(IntegerListsLex(0, min_length = 1, min_part = 1, min_slope = 0))
793
[]
794
sage: list(IntegerListsLex(0, min_length = 1, min_slope = 0))
795
[[0]]
796
sage: list(IntegerListsLex(3, max_length=2, ))
797
[[3], [2, 1], [1, 2], [0, 3]]
798
sage: partitions = {"min_part": 1, "max_slope": 0}
799
sage: partitions_min_2 = {"floor": g(2), "max_slope": 0}
800
sage: compositions = {"min_part": 1}
801
sage: integer_vectors = lambda l: {"length": l}
802
sage: lower_monomials = lambda c: {"length": c, "floor": lambda i: c[i]}
803
sage: upper_monomials = lambda c: {"length": c, "ceiling": lambda i: c[i]}
804
sage: constraints = { "min_part":1, "min_slope": -1, "max_slope": 0}
805
sage: list(IntegerListsLex(6, **partitions))
806
[[6],
807
[5, 1],
808
[4, 2],
809
[4, 1, 1],
810
[3, 3],
811
[3, 2, 1],
812
[3, 1, 1, 1],
813
[2, 2, 2],
814
[2, 2, 1, 1],
815
[2, 1, 1, 1, 1],
816
[1, 1, 1, 1, 1, 1]]
817
sage: list(IntegerListsLex(6, **constraints))
818
[[6],
819
[3, 3],
820
[3, 2, 1],
821
[2, 2, 2],
822
[2, 2, 1, 1],
823
[2, 1, 1, 1, 1],
824
[1, 1, 1, 1, 1, 1]]
825
sage: list(IntegerListsLex(1, **partitions_min_2))
826
[]
827
sage: list(IntegerListsLex(2, **partitions_min_2))
828
[[2]]
829
sage: list(IntegerListsLex(3, **partitions_min_2))
830
[[3]]
831
sage: list(IntegerListsLex(4, **partitions_min_2))
832
[[4], [2, 2]]
833
sage: list(IntegerListsLex(5, **partitions_min_2))
834
[[5], [3, 2]]
835
sage: list(IntegerListsLex(6, **partitions_min_2))
836
[[6], [4, 2], [3, 3], [2, 2, 2]]
837
sage: list(IntegerListsLex(7, **partitions_min_2))
838
[[7], [5, 2], [4, 3], [3, 2, 2]]
839
sage: list(IntegerListsLex(9, **partitions_min_2))
840
[[9], [7, 2], [6, 3], [5, 4], [5, 2, 2], [4, 3, 2], [3, 3, 3], [3, 2, 2, 2]]
841
sage: list(IntegerListsLex(10, **partitions_min_2))
842
[[10],
843
[8, 2],
844
[7, 3],
845
[6, 4],
846
[6, 2, 2],
847
[5, 5],
848
[5, 3, 2],
849
[4, 4, 2],
850
[4, 3, 3],
851
[4, 2, 2, 2],
852
[3, 3, 2, 2],
853
[2, 2, 2, 2, 2]]
854
sage: list(IntegerListsLex(4, **compositions))
855
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 3], [1, 2, 1], [1, 1, 2], [1, 1, 1, 1]]
856
857
"""
858
def __init__(self,
859
n,
860
length = None, min_length=0, max_length=infinity,
861
floor=None, ceiling = None,
862
min_part = 0, max_part = infinity,
863
min_slope=-infinity, max_slope=infinity,
864
name = None,
865
element_constructor = None):
866
"""
867
TESTS::
868
869
sage: C = IntegerListsLex(2, length=3)
870
sage: C == loads(dumps(C))
871
True
872
sage: C == loads(dumps(C)) # this did fail at some point, really!
873
True
874
sage: C is loads(dumps(C)) # todo: not implemented
875
True
876
sage: C.cardinality().parent() is ZZ
877
True
878
"""
879
if floor is None:
880
self.floor_list = []
881
elif type(floor) is type([]): # FIXME: how to refer to type list rather than the function list above?
882
self.floor_list = floor
883
else:
884
self.floor = floor
885
if ceiling is None:
886
self.ceiling_list = []
887
elif type(ceiling) is type([]):
888
self.ceiling_list = ceiling
889
else:
890
self.ceiling = ceiling
891
if length is not None:
892
min_length = length
893
max_length = length
894
if name is not None:
895
self._name = name
896
if n in ZZ:
897
self.n = n
898
self.n_range = [n]
899
else:
900
self.n_range = n
901
self.min_length = min_length
902
self.max_length = max_length
903
self.min_part = min_part
904
self.max_part = max_part
905
# FIXME: the internal functions currently assume that floor and ceiling start at 1
906
# this is a workaround
907
self.max_slope = max_slope
908
self.min_slope = min_slope
909
if element_constructor is not None:
910
self._element_constructor_ = element_constructor
911
912
_element_constructor_ = type([])
913
914
@lazy_attribute
915
def _name(self):
916
"""
917
Returns the name of this combinatorial class.
918
919
EXAMPLES::
920
921
sage: C = IntegerListsLex(2, length=3)
922
sage: C._name
923
'Integer lists of sum 2 satisfying certain constraints'
924
925
sage: C = IntegerListsLex([1,2,4], length=3)
926
sage: C._name
927
'Integer lists of sum in [1, 2, 4] satisfying certain constraints'
928
"""
929
if hasattr(self, "n"):
930
return "Integer lists of sum %s satisfying certain constraints"%self.n
931
else:
932
return "Integer lists of sum in %s satisfying certain constraints"%self.n_range
933
934
def floor(self, i):
935
"""
936
Returns the minimum part that can appear in the $i^{th}$ position in any
937
list produced.
938
939
EXAMPLES::
940
941
sage: C = IntegerListsLex(4, length=2, min_part=1)
942
sage: C.floor(0)
943
1
944
sage: C = IntegerListsLex(4, length=2, floor=[1,2])
945
sage: C.floor(0)
946
1
947
sage: C.floor(1)
948
2
949
950
"""
951
return self.floor_list[i] if i < len(self.floor_list ) else self.min_part
952
953
def ceiling(self, i):
954
"""
955
Returns the maximum part that can appear in the $i^{th}$
956
position in any list produced.
957
958
EXAMPLES::
959
960
sage: C = IntegerListsLex(4, length=2, max_part=3)
961
sage: C.ceiling(0)
962
3
963
sage: C = IntegerListsLex(4, length=2, ceiling=[3,2])
964
sage: C.ceiling(0)
965
3
966
sage: C.ceiling(1)
967
2
968
969
"""
970
return self.ceiling_list[i] if i < len(self.ceiling_list) else self.max_part
971
972
973
# Temporary adapter to use the preexisting list/iterator/is_a function above.
974
# FIXME: fix their specs so that floor and ceiling start from 0 instead of 1...
975
# FIXME: integrate them as methods of this class
976
def build_args(self):
977
"""
978
Returns a list of arguments that can be passed into the pre-existing
979
first,next,is_a, ... functions in this module.
980
981
n is currently not included in this list.
982
983
EXAMPLES::
984
985
sage: C = IntegerListsLex(2, length=3)
986
sage: C.build_args()
987
[3,
988
3,
989
<function <lambda> at 0x...>,
990
<function <lambda> at 0x...>,
991
-Infinity,
992
+Infinity]
993
994
"""
995
return [self.min_length, self.max_length,
996
lambda i: self.floor(i-1), lambda i: self.ceiling(i-1),
997
self.min_slope, self.max_slope]
998
999
def first(self):
1000
"""
1001
Returns the lexicographically maximal element in this
1002
combinatorial class.
1003
1004
EXAMPLES::
1005
1006
sage: C = IntegerListsLex(2, length=3)
1007
sage: C.first()
1008
[2, 0, 0]
1009
"""
1010
return self._element_constructor_(first(self.n_range[0], *(self.build_args())))
1011
1012
def __iter__(self):
1013
"""
1014
Returns an iterator for the elements of this combinatorial class.
1015
1016
EXAMPLES::
1017
1018
sage: C = IntegerListsLex(2, length=3)
1019
sage: list(C) #indirect doctest
1020
[[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]]
1021
1022
"""
1023
args = self.build_args()
1024
for n in self.n_range:
1025
l = first(n, *args)
1026
while l is not None:
1027
yield self._element_constructor_(l)
1028
l = next(l, *args)
1029
1030
def count(self):
1031
"""
1032
Default brute force implementation of count by iteration
1033
through all the objects.
1034
1035
Note that this skips the call to _element_constructor, unlike
1036
the default implementation from CombinatorialClass
1037
1038
TODO: do the iteration in place to save on copying time
1039
"""
1040
args = self.build_args()
1041
c = ZZ(0)
1042
for n in self.n_range:
1043
l = first(n, *args)
1044
while l is not None:
1045
c += 1
1046
l = next(l, *args)
1047
return c
1048
1049
def __contains__(self, v):
1050
"""
1051
Returns True if and only if v is in this combinatorial class.
1052
1053
EXAMPLES::
1054
1055
sage: C = IntegerListsLex(2, length=3)
1056
sage: [2, 0, 0] in C
1057
True
1058
sage: [2, 0] in C
1059
False
1060
sage: [3, 0, 0] in C
1061
False
1062
sage: all(v in C for v in C)
1063
True
1064
"""
1065
return type(v) is type([]) and is_a(v, *(self.build_args())) and sum(v) in self.n_range
1066
1067