Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/misc.py
4045 views
1
r"""
2
Miscellaneous
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Mike Hansen <[email protected]>,
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# This code is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
13
#
14
# The full text of the GPL is available at:
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
from sage.misc.misc import prod
19
20
class DoublyLinkedList():
21
"""
22
A doubly linked list class that provides constant time hiding and
23
unhiding of entries.
24
25
Note that this list's indexing is 1-based.
26
27
EXAMPLES::
28
29
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3]); dll
30
Doubly linked list of [1, 2, 3]: [1, 2, 3]
31
sage: dll.hide(1); dll
32
Doubly linked list of [1, 2, 3]: [2, 3]
33
sage: dll.unhide(1); dll
34
Doubly linked list of [1, 2, 3]: [1, 2, 3]
35
sage: dll.hide(2); dll
36
Doubly linked list of [1, 2, 3]: [1, 3]
37
sage: dll.unhide(2); dll
38
Doubly linked list of [1, 2, 3]: [1, 2, 3]
39
"""
40
def __init__(self, l):
41
"""
42
TESTS::
43
44
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
45
sage: dll == loads(dumps(dll))
46
True
47
"""
48
n = len(l)
49
self.l = l
50
self.next_value = {}
51
self.next_value['begin'] = l[0]
52
self.next_value[l[n-1]] = 'end'
53
for i in xrange(n-1):
54
self.next_value[l[i]] = l[i+1]
55
56
self.prev_value = {}
57
self.prev_value['end'] = l[-1]
58
self.prev_value[l[0]] = 'begin'
59
for i in xrange(1,n):
60
self.prev_value[l[i]] = l[i-1]
61
62
def __cmp__(self, x):
63
"""
64
TESTS::
65
66
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
67
sage: dll2 = sage.combinat.misc.DoublyLinkedList([1,2,3])
68
sage: dll == dll2
69
True
70
sage: dll.hide(1)
71
sage: dll == dll2
72
False
73
"""
74
if not isinstance(x, DoublyLinkedList):
75
return -1
76
if self.l != x.l:
77
return -1
78
if self.next_value != x.next_value:
79
return -1
80
if self.prev_value != x.prev_value:
81
return -1
82
return 0
83
84
def __repr__(self):
85
"""
86
TESTS::
87
88
sage: repr(sage.combinat.misc.DoublyLinkedList([1,2,3]))
89
'Doubly linked list of [1, 2, 3]: [1, 2, 3]'
90
"""
91
return "Doubly linked list of %s: %s"%(self.l, list(self))
92
93
def __iter__(self):
94
"""
95
TESTS::
96
97
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
98
sage: list(dll)
99
[1, 2, 3]
100
"""
101
j = self.next_value['begin']
102
while j != 'end':
103
yield j
104
j = self.next_value[j]
105
106
def hide(self, i):
107
"""
108
TESTS::
109
110
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
111
sage: dll.hide(1)
112
sage: list(dll)
113
[2, 3]
114
"""
115
self.next_value[self.prev_value[i]] = self.next_value[i]
116
self.prev_value[self.next_value[i]] = self.prev_value[i]
117
118
def unhide(self,i):
119
"""
120
TESTS::
121
122
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
123
sage: dll.hide(1); dll.unhide(1)
124
sage: list(dll)
125
[1, 2, 3]
126
"""
127
self.next_value[self.prev_value[i]] = i
128
self.prev_value[self.next_value[i]] = i
129
130
def head(self):
131
"""
132
TESTS::
133
134
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
135
sage: dll.head()
136
1
137
sage: dll.hide(1)
138
sage: dll.head()
139
2
140
"""
141
return self.next_value['begin']
142
143
def next(self, j):
144
"""
145
TESTS::
146
147
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
148
sage: dll.next(1)
149
2
150
sage: dll.hide(2)
151
sage: dll.next(1)
152
3
153
"""
154
return self.next_value[j]
155
156
def prev(self, j):
157
"""
158
TESTS::
159
160
sage: dll = sage.combinat.misc.DoublyLinkedList([1,2,3])
161
sage: dll.prev(3)
162
2
163
sage: dll.hide(2)
164
sage: dll.prev(3)
165
1
166
"""
167
return self.prev_value[j]
168
169
170
171
def _monomial_exponent_to_lower_factorial(me, x):
172
r"""
173
Converts a tuple of exponents to the monomial obtained by replacing
174
each me[i] with `x_i*(x_i - 1)*\cdots*(x_i - a_i + 1)`
175
176
EXAMPLES::
177
178
sage: from sage.combinat.misc import _monomial_exponent_to_lower_factorial
179
sage: R.<x,y,z> = QQ[]
180
sage: a = R.gens()
181
sage: _monomial_exponent_to_lower_factorial(([1,0,0]),a)
182
x
183
sage: _monomial_exponent_to_lower_factorial(([2,0,0]),a)
184
x^2 - x
185
sage: _monomial_exponent_to_lower_factorial(([0,2,0]),a)
186
y^2 - y
187
sage: _monomial_exponent_to_lower_factorial(([1,1,0]),a)
188
x*y
189
sage: _monomial_exponent_to_lower_factorial(([1,1,2]),a)
190
x*y*z^2 - x*y*z
191
sage: _monomial_exponent_to_lower_factorial(([2,2,2]),a)
192
x^2*y^2*z^2 - x^2*y^2*z - x^2*y*z^2 - x*y^2*z^2 + x^2*y*z + x*y^2*z + x*y*z^2 - x*y*z
193
"""
194
terms = []
195
for i in range(len(me)):
196
for j in range(me[i]):
197
terms.append( x[i]-j )
198
return prod(terms)
199
200
def umbral_operation(poly):
201
r"""
202
Returns the umbral operation `\downarrow` applied to poly.
203
204
The umbral operation replaces each instance of
205
`x_i^{a_i}` with
206
`x_i*(x_i - 1)*\cdots*(x_i - a_i + 1)`.
207
208
EXAMPLES::
209
210
sage: P = PolynomialRing(QQ, 2, 'x')
211
sage: x = P.gens()
212
sage: from sage.combinat.misc import umbral_operation
213
sage: umbral_operation(x[0]^3) == x[0]*(x[0]-1)*(x[0]-2)
214
True
215
sage: umbral_operation(x[0]*x[1])
216
x0*x1
217
sage: umbral_operation(x[0]+x[1])
218
x0 + x1
219
sage: umbral_operation(x[0]^2*x[1]^2) == x[0]*(x[0]-1)*x[1]*(x[1]-1)
220
True
221
"""
222
x = poly.parent().gens()
223
exponents = poly.exponents()
224
coefficients = poly.coefficients()
225
length = len(exponents)
226
return sum( [coefficients[i]*_monomial_exponent_to_lower_factorial(exponents[i],x) for i in range(length)] )
227
228
229
class IterableFunctionCall:
230
"""
231
This class wraps functions with a yield statement (generators) by
232
an object that can be iterated over. For example,
233
234
EXAMPLES::
235
236
sage: def f(): yield 'a'; yield 'b'
237
238
This does not work::
239
240
sage: for z in f: print z
241
Traceback (most recent call last):
242
...
243
TypeError: 'function' object is not iterable
244
245
Use IterableFunctionCall if you want something like the above to
246
work::
247
248
sage: from sage.combinat.misc import IterableFunctionCall
249
sage: g = IterableFunctionCall(f)
250
sage: for z in g: print z
251
a
252
b
253
254
If your function takes arguments, just put them after the function
255
name. You needn't enclose them in a tuple or anything, just put them
256
there::
257
258
sage: def f(n, m): yield 'a' * n; yield 'b' * m; yield 'foo'
259
sage: g = IterableFunctionCall(f, 2, 3)
260
sage: for z in g: print z
261
aa
262
bbb
263
foo
264
"""
265
def __init__(self, f, *args, **kwargs):
266
"""
267
EXAMPLES::
268
269
sage: from sage.combinat.misc import IterableFunctionCall
270
sage: IterableFunctionCall(iter, [1,2,3])
271
Iterable function call <built-in function iter> with args=([1, 2, 3],) and kwargs={}
272
"""
273
self.f = f
274
self.args = args
275
self.kwargs = kwargs
276
277
def __iter__(self):
278
"""
279
EXAMPLES::
280
281
sage: from sage.combinat.misc import IterableFunctionCall
282
sage: list(iter(IterableFunctionCall(iter, [1,2,3])))
283
[1, 2, 3]
284
"""
285
return self.f(*self.args, **self.kwargs)
286
287
def __repr__(self):
288
"""
289
EXAMPLES::
290
291
sage: from sage.combinat.misc import IterableFunctionCall
292
sage: repr(IterableFunctionCall(iter, [1,2,3]))
293
'Iterable function call <built-in function iter> with args=([1, 2, 3],) and kwargs={}'
294
"""
295
return "Iterable function call %s with args=%s and kwargs=%s"%(self.f, self.args, self.kwargs)
296
297
298
299
300
301
def check_integer_list_constraints(l, **kwargs):
302
"""
303
EXAMPLES::
304
305
sage: from sage.combinat.misc import check_integer_list_constraints
306
sage: cilc = check_integer_list_constraints
307
sage: l = [[2,1,3],[1,2],[3,3],[4,1,1]]
308
sage: cilc(l, min_part=2)
309
[[3, 3]]
310
sage: cilc(l, max_part=2)
311
[[1, 2]]
312
sage: cilc(l, length=2)
313
[[1, 2], [3, 3]]
314
sage: cilc(l, max_length=2)
315
[[1, 2], [3, 3]]
316
sage: cilc(l, min_length=3)
317
[[2, 1, 3], [4, 1, 1]]
318
sage: cilc(l, max_slope=0)
319
[[3, 3], [4, 1, 1]]
320
sage: cilc(l, min_slope=1)
321
[[1, 2]]
322
sage: cilc(l, outer=[2,2])
323
[[1, 2]]
324
sage: cilc(l, inner=[2,2])
325
[[3, 3]]
326
327
::
328
329
sage: cilc([1,2,3], length=3, singleton=True)
330
[1, 2, 3]
331
sage: cilc([1,2,3], length=2, singleton=True) is None
332
True
333
"""
334
if 'singleton' in kwargs and kwargs['singleton']:
335
singleton = True
336
result = [ l ]
337
n = sum(l)
338
del kwargs['singleton']
339
else:
340
singleton = False
341
if len(l) > 0:
342
n = sum(l[0])
343
result = l
344
else:
345
return []
346
347
348
min_part = kwargs.get('min_part', None)
349
max_part = kwargs.get('max_part', None)
350
351
min_length = kwargs.get('min_length', None)
352
max_length = kwargs.get('max_length', None)
353
354
min_slope = kwargs.get('min_slope', None)
355
max_slope = kwargs.get('max_slope', None)
356
357
length = kwargs.get('length', None)
358
359
inner = kwargs.get('inner', None)
360
outer = kwargs.get('outer', None)
361
362
#Preprocess the constraints
363
if outer is not None:
364
max_length = len(outer)
365
for i in range(max_length):
366
if outer[i] == "inf":
367
outer[i] = n+1
368
if inner is not None:
369
min_length = len(inner)
370
371
if length is not None:
372
max_length = length
373
min_length = length
374
375
filters = {}
376
filters['length'] = lambda x: len(x) == length
377
filters['min_part'] = lambda x: min(x) >= min_part
378
filters['max_part'] = lambda x: max(x) <= max_part
379
filters['min_length'] = lambda x: len(x) >= min_length
380
filters['max_length'] = lambda x: len(x) <= max_length
381
filters['min_slope'] = lambda x: min([x[i+1]-x[i] for i in range(len(x)-1)]+[min_slope+1]) >= min_slope
382
filters['max_slope'] = lambda x: max([x[i+1]-x[i] for i in range(len(x)-1)]+[max_slope-1]) <= max_slope
383
filters['outer'] = lambda x: len(outer) >= len(x) and min([outer[i]-x[i] for i in range(len(x))]) >= 0
384
filters['inner'] = lambda x: len(x) >= len(inner) and max([inner[i]-x[i] for i in range(len(inner))]) <= 0
385
386
for key in kwargs:
387
result = filter( filters[key], result )
388
389
if singleton:
390
try:
391
return result[0]
392
except IndexError:
393
return None
394
else:
395
return result
396
397
398