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