Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/lyndon_word.py
4100 views
1
# -*- coding: utf-8 -*-
2
"""
3
Lyndon words
4
"""
5
#*****************************************************************************
6
# Copyright (C) 2007 Mike Hansen <[email protected]>,
7
#
8
# Distributed under the terms of the GNU General Public License (GPL)
9
#
10
# This code is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
# General Public License for more details.
14
#
15
# The full text of the GPL is available at:
16
#
17
# http://www.gnu.org/licenses/
18
#*****************************************************************************
19
20
from combinat import CombinatorialClass
21
from sage.combinat.composition import Composition, Compositions
22
from sage.rings.all import factorial, divisors, gcd, moebius, Integer
23
from sage.misc.misc import prod
24
import __builtin__
25
import necklace
26
from integer_vector import IntegerVectors
27
28
from sage.combinat.words.word import FiniteWord_list
29
from sage.combinat.words.words import Words_all, FiniteWords_length_k_over_OrderedAlphabet
30
from sage.combinat.words.alphabet import OrderedAlphabet
31
32
def LyndonWords(e=None, k=None):
33
"""
34
Returns the combinatorial class of Lyndon words.
35
36
A Lyndon word `w` is a word that is lexicographically less than all of
37
its rotations. Equivalently, whenever `w` is split into two non-empty
38
substrings, `w` is lexicographically less than the right substring.
39
40
INPUT:
41
42
- no input at all
43
44
or
45
46
- ``e`` - integer, size of alphabet
47
- ``k`` - integer, length of the words
48
49
or
50
51
- ``e`` - a composition
52
53
OUTPUT:
54
55
A combinatorial class of Lyndon words.
56
57
EXAMPLES::
58
59
sage: LyndonWords()
60
Lyndon words
61
62
If e is an integer, then e specifies the length of the
63
alphabet; k must also be specified in this case::
64
65
sage: LW = LyndonWords(3,3); LW
66
Lyndon words from an alphabet of size 3 of length 3
67
sage: LW.first()
68
word: 112
69
sage: LW.last()
70
word: 233
71
sage: LW.random_element()
72
word: 112
73
sage: LW.cardinality()
74
8
75
76
If e is a (weak) composition, then it returns the class of Lyndon
77
words that have evaluation e::
78
79
sage: LyndonWords([2, 0, 1]).list()
80
[word: 113]
81
sage: LyndonWords([2, 0, 1, 0, 1]).list()
82
[word: 1135, word: 1153, word: 1315]
83
sage: LyndonWords([2, 1, 1]).list()
84
[word: 1123, word: 1132, word: 1213]
85
"""
86
if e is None and k is None:
87
return LyndonWords_class()
88
elif isinstance(e, (int, Integer)):
89
if e > 0:
90
if not isinstance(k, (int, Integer)):
91
raise TypeError, "k must be a non-negative integer"
92
if k < 0:
93
raise TypeError, "k must be a non-negative integer"
94
return LyndonWords_nk(e, k)
95
elif e in Compositions():
96
return LyndonWords_evaluation(e)
97
98
raise TypeError, "e must be a positive integer or a composition"
99
100
class LyndonWord(FiniteWord_list):
101
def __init__(self, data, check=True):
102
r"""
103
Construction of a Lyndon word.
104
105
INPUT:
106
107
- ``data`` - list
108
- ``check`` - bool (optional, default: True) if True, a
109
verification that the input data represent a Lyndon word.
110
111
OUTPUT:
112
113
A Lyndon word.
114
115
EXAMPLES::
116
117
sage: LyndonWord([1,2,2])
118
word: 122
119
sage: LyndonWord([1,2,3])
120
word: 123
121
sage: LyndonWord([2,1,2,3])
122
Traceback (most recent call last):
123
...
124
ValueError: Not a Lyndon word
125
126
If check is False, then no verification is done::
127
128
sage: LyndonWord([2,1,2,3], check=False)
129
word: 2123
130
"""
131
super(LyndonWord,self).__init__(parent=LyndonWords(),data=data)
132
if check and not self.is_lyndon():
133
raise ValueError, "Not a Lyndon word"
134
135
class LyndonWords_class(Words_all):
136
def __repr__(self):
137
r"""
138
String representation.
139
140
EXAMPLES::
141
142
sage: LyndonWords()
143
Lyndon words
144
"""
145
return "Lyndon words"
146
147
def __contains__(self, x):
148
"""
149
TESTS::
150
151
sage: LW33 = LyndonWords(3,3)
152
sage: all([lw in LyndonWords() for lw in LW33])
153
True
154
"""
155
try:
156
return LyndonWord(x)
157
except ValueError:
158
return False
159
160
class LyndonWords_evaluation(CombinatorialClass):
161
def __init__(self, e):
162
"""
163
TESTS::
164
165
sage: LW21 = LyndonWords([2,1]); LW21
166
Lyndon words with evaluation [2, 1]
167
sage: LW21 == loads(dumps(LW21))
168
True
169
"""
170
self.e = Composition(e)
171
172
def __repr__(self):
173
"""
174
TESTS::
175
176
sage: repr(LyndonWords([2,1,1]))
177
'Lyndon words with evaluation [2, 1, 1]'
178
"""
179
return "Lyndon words with evaluation %s"%self.e
180
181
def __contains__(self, x):
182
"""
183
EXAMPLES::
184
185
sage: [1,2,1,2] in LyndonWords([2,2])
186
False
187
sage: [1,1,2,2] in LyndonWords([2,2])
188
True
189
sage: all([ lw in LyndonWords([2,1,3,1]) for lw in LyndonWords([2,1,3,1])])
190
True
191
"""
192
try:
193
w = LyndonWord(x)
194
except ValueError:
195
return False
196
ed = w.evaluation_dict()
197
for (i,a) in enumerate(self.e):
198
if ed[i+1] != a:
199
return False
200
return True
201
202
def cardinality(self):
203
"""
204
Returns the number of Lyndon words with the evaluation e.
205
206
EXAMPLES::
207
208
sage: LyndonWords([]).cardinality()
209
0
210
sage: LyndonWords([2,2]).cardinality()
211
1
212
sage: LyndonWords([2,3,2]).cardinality()
213
30
214
215
Check to make sure that the count matches up with the number of
216
Lyndon words generated.
217
218
::
219
220
sage: comps = [[],[2,2],[3,2,7],[4,2]]+Compositions(4).list()
221
sage: lws = [ LyndonWords(comp) for comp in comps]
222
sage: all( [ lw.cardinality() == len(lw.list()) for lw in lws] )
223
True
224
"""
225
evaluation = self.e
226
le = __builtin__.list(evaluation)
227
if len(evaluation) == 0:
228
return 0
229
230
n = sum(evaluation)
231
232
return sum([moebius(j)*factorial(n/j) / prod([factorial(ni/j) for ni in evaluation]) for j in divisors(gcd(le))])/n
233
234
def __iter__(self):
235
"""
236
An iterator for the Lyndon words with evaluation e.
237
238
EXAMPLES::
239
240
sage: LyndonWords([1]).list() #indirect doctest
241
[word: 1]
242
sage: LyndonWords([2]).list() #indirect doctest
243
[]
244
sage: LyndonWords([3]).list() #indirect doctest
245
[]
246
sage: LyndonWords([3,1]).list() #indirect doctest
247
[word: 1112]
248
sage: LyndonWords([2,2]).list() #indirect doctest
249
[word: 1122]
250
sage: LyndonWords([1,3]).list() #indirect doctest
251
[word: 1222]
252
sage: LyndonWords([3,3]).list() #indirect doctest
253
[word: 111222, word: 112122, word: 112212]
254
sage: LyndonWords([4,3]).list() #indirect doctest
255
[word: 1111222, word: 1112122, word: 1112212, word: 1121122, word: 1121212]
256
"""
257
if self.e == []:
258
return
259
260
for z in necklace._sfc(self.e, equality=True):
261
yield LyndonWord([i+1 for i in z], check=False)
262
263
class LyndonWords_nk(FiniteWords_length_k_over_OrderedAlphabet):
264
def __init__(self, n, k):
265
"""
266
TESTS::
267
268
sage: LW23 = LyndonWords(2,3); LW23
269
Lyndon words from an alphabet of size 2 of length 3
270
sage: LW23== loads(dumps(LW23))
271
True
272
"""
273
self.n = Integer(n)
274
self.k = Integer(k)
275
alphabet = OrderedAlphabet(range(1,self.n+1))
276
super(LyndonWords_nk,self).__init__(alphabet,self.k)
277
278
def __repr__(self):
279
"""
280
TESTS::
281
282
sage: repr(LyndonWords(2, 3))
283
'Lyndon words from an alphabet of size 2 of length 3'
284
"""
285
return "Lyndon words from an alphabet of size %s of length %s"%(self.n, self.k)
286
287
def __contains__(self, x):
288
"""
289
TESTS::
290
291
sage: LW33 = LyndonWords(3,3)
292
sage: all([lw in LW33 for lw in LW33])
293
True
294
"""
295
try:
296
w = LyndonWord(x)
297
return w.length() == self.k and len(set(w)) <= self.n
298
except ValueError:
299
return False
300
301
def cardinality(self):
302
"""
303
TESTS::
304
305
sage: [ LyndonWords(3,i).cardinality() for i in range(1, 11) ]
306
[3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880]
307
"""
308
if self.k == 0:
309
return 1
310
else:
311
s = 0
312
for d in divisors(self.k):
313
s += moebius(d)*(self.n**(self.k/d))
314
return s/self.k
315
316
def __iter__(self):
317
"""
318
TESTS::
319
320
sage: LyndonWords(3,3).list() # indirect doctest
321
[word: 112, word: 113, word: 122, word: 123, word: 132, word: 133, word: 223, word: 233]
322
"""
323
for c in IntegerVectors(self.k, self.n):
324
cf = filter(lambda x: x != 0, c)
325
nonzero_indices = []
326
for i in range(len(c)):
327
if c[i] != 0:
328
nonzero_indices.append(i)
329
for lw in LyndonWords_evaluation(cf):
330
yield LyndonWord(map(lambda x: nonzero_indices[x-1]+1, lw), check=False)
331
332
def StandardBracketedLyndonWords(n, k):
333
"""
334
Returns the combinatorial class of standard bracketed Lyndon words
335
from [1, ..., n] of length k. These are in one to one
336
correspondence with the Lyndon words and form a basis for the
337
subspace of degree k of the free Lie algebra of rank n.
338
339
EXAMPLES::
340
341
sage: SBLW33 = StandardBracketedLyndonWords(3,3); SBLW33
342
Standard bracketed Lyndon words from an alphabet of size 3 of length 3
343
sage: SBLW33.first()
344
[1, [1, 2]]
345
sage: SBLW33.last()
346
[[2, 3], 3]
347
sage: SBLW33.cardinality()
348
8
349
sage: SBLW33.random_element()
350
[1, [1, 2]]
351
"""
352
return StandardBracketedLyndonWords_nk(n,k)
353
354
class StandardBracketedLyndonWords_nk(CombinatorialClass):
355
def __init__(self, n, k):
356
"""
357
TESTS::
358
359
sage: SBLW = StandardBracketedLyndonWords(3, 2)
360
sage: SBLW == loads(dumps(SBLW))
361
True
362
"""
363
self.n = Integer(n)
364
self.k = Integer(k)
365
366
def __repr__(self):
367
"""
368
TESTS::
369
370
sage: repr(StandardBracketedLyndonWords(3, 3))
371
'Standard bracketed Lyndon words from an alphabet of size 3 of length 3'
372
"""
373
return "Standard bracketed Lyndon words from an alphabet of size %s of length %s"%(self.n, self.k)
374
375
def cardinality(self):
376
"""
377
EXAMPLES::
378
379
sage: StandardBracketedLyndonWords(3, 3).cardinality()
380
8
381
sage: StandardBracketedLyndonWords(3, 4).cardinality()
382
18
383
"""
384
return LyndonWords_nk(self.n,self.k).cardinality()
385
386
def __iter__(self):
387
"""
388
EXAMPLES::
389
390
sage: StandardBracketedLyndonWords(3, 3).list()
391
[[1, [1, 2]],
392
[1, [1, 3]],
393
[[1, 2], 2],
394
[1, [2, 3]],
395
[[1, 3], 2],
396
[[1, 3], 3],
397
[2, [2, 3]],
398
[[2, 3], 3]]
399
"""
400
for lw in LyndonWords_nk(self.n, self.k):
401
yield standard_bracketing(lw)
402
403
def standard_bracketing(lw):
404
"""
405
Returns the standard bracketing of a Lyndon word lw.
406
407
EXAMPLES::
408
409
sage: import sage.combinat.lyndon_word as lyndon_word
410
sage: map( lyndon_word.standard_bracketing, LyndonWords(3,3) )
411
[[1, [1, 2]],
412
[1, [1, 3]],
413
[[1, 2], 2],
414
[1, [2, 3]],
415
[[1, 3], 2],
416
[[1, 3], 3],
417
[2, [2, 3]],
418
[[2, 3], 3]]
419
"""
420
if len(lw) == 1:
421
return lw[0]
422
423
for i in range(1,len(lw)):
424
if lw[i:] in LyndonWords():
425
return [ standard_bracketing( lw[:i] ), standard_bracketing(lw[i:]) ]
426
427