Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/integer_vectors_mod_permgroup.py
4045 views
1
r"""
2
Integer vectors modulo the action of a permutation group
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2010-12 Nicolas Borie <nicolas.borie at math dot u-psud.fr>
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# The full text of the GPL is available at:
10
# http://www.gnu.org/licenses/
11
#*****************************************************************************
12
13
from itertools import imap
14
from sage.structure.unique_representation import UniqueRepresentation
15
from sage.rings.semirings.non_negative_integer_semiring import NN
16
17
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
18
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
19
20
from sage.structure.list_clone import ClonableIntArray
21
from sage.combinat.backtrack import SearchForest
22
23
from sage.combinat.enumeration_mod_permgroup import is_canonical, orbit, canonical_children, canonical_representative_of_orbit_of
24
25
from sage.combinat.integer_vector import IntegerVectors
26
27
class IntegerVectorsModPermutationGroup(UniqueRepresentation):
28
r"""
29
Returns an enumerated set containing integer vectors which are
30
maximal in their orbit under the action of the permutation group
31
``G`` for the lexicographic order.
32
33
In Sage, a permutation group `G` is viewed as a subgroup of the
34
symmetric group `S_n` of degree `n` and `n` is said to be the degree
35
of `G`. Any integer vector `v` is said to be canonical if it
36
is maximal in its orbit under the action of `G`. `v` is
37
canonical if and only if
38
39
.. math::
40
41
v = \max_{\text{lex order}} \{g \cdot v | g \in G \}
42
43
The action of `G` is on position. This means for example that the
44
simple transposition `s_1 = (1, 2)` swaps the first and the second entries
45
of any integer vector `v = [a_1, a_2, a_3, \dots , a_n]`
46
47
.. math::
48
49
s_1 \cdot v = [a_2, a_1, a_3, \dots , a_n]
50
51
This functions returns a parent which contains a single integer
52
vector by orbit under the action of the permutation group `G`. The
53
approach chosen here is to keep the maximal integer vector for the
54
lexicographic order in each orbit. Such maximal vector will be
55
called canonical integer vector under the action of the
56
permutation group `G`.
57
58
INPUT:
59
60
- ``G`` - a permutation group
61
- ``sum`` - (default: None) - a nonnegative integer
62
- ``max_part`` - (default: None) - a nonnegative integer setting the
63
maximum of entries of elements
64
- ``sgs`` - (default: None) - a strong generating system of the
65
group `G`. If you do not provide it, it will be calculated at the
66
creation of the parent
67
68
OUTPUT:
69
70
- If ``sum`` and ``max_part`` are None, it returns the infinite enumerated
71
set of all integer vectors (list of integers) maximal in their orbit for
72
the lexicographic order.
73
74
- If ``sum`` is an integer, it returns a finite enumerated set containing
75
all integer vectors maximal in their orbit for the lexicographic order
76
and whose entries sum to ``sum``.
77
78
EXAMPLES:
79
80
Here is the set enumerating integer vectors modulo the action of the cyclic
81
group of `3` elements::
82
83
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]))
84
sage: I.category()
85
Join of Category of infinite enumerated sets and Category of quotients of sets
86
sage: I.cardinality()
87
+Infinity
88
sage: I.list()
89
Traceback (most recent call last):
90
...
91
NotImplementedError: infinite list
92
sage: p = iter(I)
93
sage: for i in range(10): p.next()
94
[0, 0, 0]
95
[1, 0, 0]
96
[2, 0, 0]
97
[1, 1, 0]
98
[3, 0, 0]
99
[2, 1, 0]
100
[2, 0, 1]
101
[1, 1, 1]
102
[4, 0, 0]
103
[3, 1, 0]
104
105
The method
106
:meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_All.is_canonical`
107
tests if any integer vector is maximal in its orbit. This method
108
is also used in the containment test::
109
110
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
111
sage: I.is_canonical([5,2,0,4])
112
True
113
sage: I.is_canonical([5,0,6,4])
114
False
115
sage: I.is_canonical([1,1,1,1])
116
True
117
sage: [2,3,1,0] in I
118
False
119
sage: [5,0,5,0] in I
120
True
121
sage: 'Bla' in I
122
False
123
sage: I.is_canonical('bla')
124
Traceback (most recent call last):
125
...
126
AssertionError: bla should be a list or a integer vector
127
128
If you give a value to the extra argument ``sum``, the set returned
129
will be a finite set containing only canonical vectors whose entries
130
sum to ``sum``.::
131
132
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), sum=6)
133
sage: I.cardinality()
134
10
135
sage: I.list()
136
[[6, 0, 0], [5, 1, 0], [5, 0, 1], [4, 2, 0], [4, 1, 1],
137
[4, 0, 2], [3, 3, 0], [3, 2, 1], [3, 1, 2], [2, 2, 2]]
138
sage: I.category()
139
Join of Category of finite enumerated sets and Category of quotients of sets
140
141
To get the orbit of any integer vector `v` under the action of the group,
142
use the method :meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_All.orbit`;
143
we convert the returned set of vectors into a list in increasing lexicographic order,
144
to get a reproducible test::
145
146
sage: from sage.combinat.enumeration_mod_permgroup import lex_cmp
147
sage: sorted(I.orbit([6,0,0]), cmp=lex_cmp)
148
[[0, 0, 6], [0, 6, 0], [6, 0, 0]]
149
sage: sorted(I.orbit([5,1,0]), cmp=lex_cmp)
150
[[0, 5, 1], [1, 0, 5], [5, 1, 0]]
151
sage: I.orbit([2,2,2])
152
set([[2, 2, 2]])
153
154
TESTS:
155
156
Let us check that canonical integer vectors of the symmetric group
157
are just sorted list of integers::
158
159
sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time
160
sage: p = iter(I) # long time
161
sage: for i in range(100): # long time
162
... v = list(p.next())
163
... assert sorted(v, reverse=True) == v
164
165
We now check that there is as much of canonical vectors under the
166
symmetric group `S_n` whose entries sum to `d` than partitions of
167
`d` of at most `n` parts::
168
169
sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time
170
sage: for i in range(10): # long time
171
... d1 = I.subset(i).cardinality()
172
... d2 = Partitions(i, max_length=5).cardinality()
173
... print d1
174
... assert d1 == d2
175
1
176
1
177
2
178
3
179
5
180
7
181
10
182
13
183
18
184
23
185
186
We present a last corner case: trivial groups. For the trivial
187
group ``G`` acting on a list of length `n`, all integer vectors of
188
length `n` are canonical::
189
190
sage: G = PermutationGroup([[(6,)]]) # long time
191
sage: G.cardinality() # long time
192
1
193
sage: I = IntegerVectorsModPermutationGroup(G) # long time
194
sage: for i in range(10): # long time
195
... d1 = I.subset(i).cardinality()
196
... d2 = IntegerVectors(i,6).cardinality()
197
... print d1
198
... assert d1 == d2
199
1
200
6
201
21
202
56
203
126
204
252
205
462
206
792
207
1287
208
2002
209
"""
210
@staticmethod
211
def __classcall__(cls, G, sum=None, max_part=None, sgs=None):
212
r"""
213
TESTS::
214
215
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]))
216
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), None)
217
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 2)
218
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), -2)
219
Traceback (most recent call last):
220
...
221
ValueError: Value -2 in not in Non negative integer semiring.
222
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 8, max_part=5)
223
"""
224
if sum is None and max_part is None:
225
return IntegerVectorsModPermutationGroup_All(G, sgs=sgs)
226
else:
227
if sum is not None:
228
assert (sum == NN(sum))
229
if max_part is not None:
230
assert (max_part == NN(max_part))
231
return IntegerVectorsModPermutationGroup_with_constraints(G, sum, max_part, sgs=sgs)
232
233
class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, SearchForest):
234
r"""
235
A class for integer vectors enumerated up to the action of a
236
permutation group.
237
238
A Sage permutation group is viewed as a subgroup of the symmetric
239
group `S_n` for a certain `n`. This group has a natural action by
240
position on vectors of length `n`. This class implements a set
241
which keeps a single vector for each orbit. We say that a vector
242
is canonical if it is the maximum in its orbit under the action of
243
the permutation group for the lexicographic order.
244
245
EXAMPLES::
246
247
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
248
sage: I
249
Integer vectors of length 4 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
250
sage: I.cardinality()
251
+Infinity
252
sage: TestSuite(I).run()
253
sage: it = iter(I)
254
sage: [it.next(), it.next(), it.next(), it.next(), it.next()]
255
[[0, 0, 0, 0],
256
[1, 0, 0, 0],
257
[2, 0, 0, 0],
258
[1, 1, 0, 0],
259
[1, 0, 1, 0]]
260
sage: x = it.next(); x
261
[3, 0, 0, 0]
262
sage: I.first()
263
[0, 0, 0, 0]
264
265
TESTS::
266
267
sage: TestSuite(I).run()
268
"""
269
def __init__(self, G, sgs=None):
270
"""
271
TESTS::
272
273
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
274
sage: I
275
Integer vectors of length 4 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
276
sage: I.category()
277
Join of Category of infinite enumerated sets and Category of quotients of sets
278
sage: TestSuite(I).run()
279
"""
280
SearchForest.__init__(self, algorithm = 'breadth', category = InfiniteEnumeratedSets().Quotients())
281
self._permgroup = G
282
self.n = G.degree()
283
284
# self.sgs: strong_generating_system
285
if sgs is None:
286
self._sgs = G.strong_generating_system()
287
else:
288
self._sgs = map(lambda x: list(x), list(sgs))
289
290
def _repr_(self):
291
"""
292
TESTS::
293
294
sage: IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]))
295
Integer vectors of length 3 enumerated up to the action of Permutation Group with generators [(1,2,3)]
296
"""
297
return "Integer vectors of length %s enumerated up to the action of %s"%(str(self.n), self._permgroup.__repr__())
298
299
def ambient(self):
300
r"""
301
Return the ambient space from which ``self`` is a quotient.
302
303
EXAMPLES::
304
305
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
306
sage: S.ambient()
307
Integer vectors
308
"""
309
# TODO: Fix me once 'IntegerVectors(length=bla)' will return
310
# the integer vectors of length bla
311
return IntegerVectors(length=self.n)
312
313
def lift(self, elt):
314
r"""
315
Lift the element ``elt`` inside the ambient space from which ``self`` is a quotient.
316
317
EXAMPLES::
318
319
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
320
sage: v = S.lift(S([4,3,0,1])); v
321
[4, 3, 0, 1]
322
sage: type(v)
323
<type 'list'>
324
"""
325
# TODO: For now, Sage integer vectors are just python list.
326
# Once Integer vectors will have an element class, update this
327
# code properly
328
return list(elt)
329
330
def retract(self, elt):
331
r"""
332
Return the canonical representative of the orbit of the
333
integer ``elt`` under the action of the permutation group
334
defining ``self``.
335
336
If the element ``elt`` is already maximal in its orbit for
337
the lexicographic order, ``elt`` is thus the good
338
representative for its orbit.
339
340
EXAMPLES::
341
342
sage: [0,0,0,0] in IntegerVectors(length=4)
343
True
344
sage: [1,0,0,0] in IntegerVectors(length=4)
345
True
346
sage: [0,1,0,0] in IntegerVectors(length=4)
347
True
348
sage: [1,0,1,0] in IntegerVectors(length=4)
349
True
350
sage: [0,1,0,1] in IntegerVectors(length=4)
351
True
352
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
353
sage: S.retract([0,0,0,0])
354
[0, 0, 0, 0]
355
sage: S.retract([1,0,0,0])
356
[1, 0, 0, 0]
357
sage: S.retract([0,1,0,0])
358
[1, 0, 0, 0]
359
sage: S.retract([1,0,1,0])
360
[1, 0, 1, 0]
361
sage: S.retract([0,1,0,1])
362
[1, 0, 1, 0]
363
"""
364
# TODO: Once Sage integer vector will have a data structure
365
# based on ClonableIntArray, remove the conversion intarray
366
assert len(elt) == self.n, "%s is a quotient set of %s"%(self, self.ambient())
367
intarray = self.element_class(self, elt, check=False)
368
return self.element_class(self, canonical_representative_of_orbit_of(self._sgs, intarray), check=False)
369
370
def roots(self):
371
r"""
372
Returns the root of generation of ``self``. This method is
373
required to build the tree structure of ``self`` which
374
inherits from the class :class:`~sage.combinat.backtrack.SearchForest`.
375
376
EXAMPLES::
377
378
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
379
sage: I.roots()
380
[[0, 0, 0, 0]]
381
"""
382
return [self.element_class(self, self.n*[0,], check=False)]
383
384
def children(self, x):
385
r"""
386
Returns the list of children of the element ``x``. This method
387
is required to build the tree structure of ``self`` which
388
inherits from the class :class:`~sage.combinat.backtrack.SearchForest`.
389
390
EXAMPLES::
391
392
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
393
sage: I.children(I([2,1,0,0], check=False))
394
[[2, 2, 0, 0], [2, 1, 1, 0], [2, 1, 0, 1]]
395
"""
396
return canonical_children(self._sgs, x, -1)
397
398
def permutation_group(self):
399
r"""
400
Returns the permutation group given to define ``self``.
401
402
EXAMPLES::
403
404
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
405
sage: I.permutation_group()
406
Permutation Group with generators [(1,2,3,4)]
407
"""
408
return self._permgroup
409
410
def is_canonical(self, v, check=True):
411
r"""
412
Returns ``True`` if the integer list ``v`` is maximal in its
413
orbit under the action of the permutation group given to
414
define ``self``. Such integer vectors are said to be
415
canonical. A vector `v` is canonical if and only if
416
417
.. math::
418
419
v = \max_{\text{lex order}} \{g \cdot v | g \in G \}
420
421
EXAMPLES::
422
423
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
424
sage: I.is_canonical([4,3,2,1])
425
True
426
sage: I.is_canonical([4,0,0,1])
427
True
428
sage: I.is_canonical([4,0,3,3])
429
True
430
sage: I.is_canonical([4,0,4,4])
431
False
432
"""
433
if check:
434
assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector'%v
435
assert (self.n == len(v)), '%s should be of length %s'%(v, self.n)
436
for p in v:
437
assert (p == NN(p)), 'Elements of %s should be integers'%s
438
return is_canonical(self._sgs, self.element_class(self, list(v), check=False))
439
440
def __contains__(self, v):
441
"""
442
EXAMPLES::
443
444
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
445
sage: [2,2,0,0] in I
446
True
447
sage: [2,0,1,0] in I
448
True
449
sage: [2,0,0,1] in I
450
True
451
sage: [2,0,0,2] in I
452
False
453
sage: [2,0,0,2,12] in I
454
False
455
"""
456
try:
457
return self.is_canonical(self.element_class(self, list(v), check=False), check=False)
458
except:
459
return False
460
461
def __call__(self, v, check=True):
462
r"""
463
Returns an element of ``self`` constructed from ``v`` if
464
possible.
465
466
TESTS::
467
468
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
469
sage: I([3,2,1,0])
470
[3, 2, 1, 0]
471
"""
472
try:
473
if v.parent() is self:
474
return v
475
else:
476
raise ValueError, '%s shoud be a Python list of integer'%(v)
477
except:
478
return self.element_class(self, list(v), check=check)
479
480
def orbit(self, v):
481
r"""
482
Returns the orbit of the integer vector ``v`` under the action of the
483
permutation group defining ``self``. The result is a set.
484
485
EXAMPLES:
486
487
In order to get reproducible doctests, we convert the returned sets
488
into lists in increasing lexicographic order::
489
490
sage: from sage.combinat.enumeration_mod_permgroup import lex_cmp
491
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
492
sage: sorted(I.orbit([2,2,0,0]), cmp=lex_cmp)
493
[[0, 0, 2, 2], [0, 2, 2, 0], [2, 0, 0, 2], [2, 2, 0, 0]]
494
sage: sorted(I.orbit([2,1,0,0]), cmp=lex_cmp)
495
[[0, 0, 2, 1], [0, 2, 1, 0], [1, 0, 0, 2], [2, 1, 0, 0]]
496
sage: sorted(I.orbit([2,0,1,0]), cmp=lex_cmp)
497
[[0, 1, 0, 2], [0, 2, 0, 1], [1, 0, 2, 0], [2, 0, 1, 0]]
498
sage: sorted(I.orbit([2,0,2,0]), cmp=lex_cmp)
499
[[0, 2, 0, 2], [2, 0, 2, 0]]
500
sage: I.orbit([1,1,1,1])
501
set([[1, 1, 1, 1]])
502
"""
503
assert isinstance(v, (list, ClonableIntArray)), '%s shoud be a Python list or an element of %s'%(v, self)
504
try:
505
if v.parent() is self:
506
return orbit(self._sgs, v)
507
raise TypeError
508
except:
509
return orbit(self._sgs, self.element_class(self, v, check=False))
510
511
def subset(self, sum=None, max_part=None):
512
r"""
513
Returns the subset of ``self`` containing integer vectors
514
whose entries sum to ``sum``.
515
516
EXAMPLES::
517
518
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
519
sage: S.subset(4)
520
Integer vectors of length 4 and of sum 4 enumerated up to
521
the action of Permutation Group with generators
522
[(1,2,3,4)]
523
"""
524
return IntegerVectorsModPermutationGroup_with_constraints(self.permutation_group(), sum, max_part)
525
526
class Element(ClonableIntArray):
527
r"""
528
Element class for the set of integer vectors of given sum enumerated modulo
529
the action of a permutation group. These vector are clonable lists of integers
530
which must check conditions comming form the parent appearing in the method
531
:meth:`~sage.structure.list_clone.ClonableIntArray.check`.
532
533
TESTS::
534
535
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
536
sage: v = I.element_class(I, [4,3,2,1]); v
537
[4, 3, 2, 1]
538
sage: TestSuite(v).run()
539
sage: I.element_class(I, [4,3,2,5])
540
Traceback (most recent call last):
541
...
542
AssertionError
543
"""
544
def check(self):
545
r"""
546
Checks that ``self`` verify the invariants needed for
547
living in ``self.parent()``.
548
549
EXAMPLES::
550
551
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
552
sage: v = I.an_element()
553
sage: v.check()
554
sage: w = I([0,4,0,0], check=False); w
555
[0, 4, 0, 0]
556
sage: w.check()
557
Traceback (most recent call last):
558
...
559
AssertionError
560
"""
561
assert self.parent().is_canonical(self)
562
563
564
class IntegerVectorsModPermutationGroup_with_constraints(UniqueRepresentation, SearchForest):
565
r"""
566
This class models finite enumerated sets of integer vectors with
567
constraint enumerated up to the action of a permutation group.
568
Integer vectors are enumerated modulo the action of the
569
permutation group. To implement that, we keep a single integer
570
vector by orbit under the action of the permutation
571
group. Elements chosen are vectors maximal in their orbit for the
572
lexicographic order.
573
574
For more information see :class:`IntegerVectorsModPermutationGroup`.
575
576
EXAMPLES::
577
578
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=1)
579
sage: I.list()
580
[[0, 0, 0, 0], [1, 0, 0, 0], [1, 1, 0, 0], [1, 0, 1, 0], [1, 1, 1, 0], [1, 1, 1, 1]]
581
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=6, max_part=4)
582
sage: I.list()
583
[[4, 2, 0, 0], [4, 1, 1, 0], [4, 1, 0, 1], [4, 0, 2, 0], [4, 0, 1, 1],
584
[4, 0, 0, 2], [3, 3, 0, 0], [3, 2, 1, 0], [3, 2, 0, 1], [3, 1, 2, 0],
585
[3, 1, 1, 1], [3, 1, 0, 2], [3, 0, 3, 0], [3, 0, 2, 1], [3, 0, 1, 2],
586
[2, 2, 2, 0], [2, 2, 1, 1], [2, 1, 2, 1]]
587
588
Here is the enumeration of unlabeled graphs over 5 vertices::
589
590
sage: G = IntegerVectorsModPermutationGroup(TransitiveGroup(10,12), max_part=1) # optional
591
sage: G.cardinality() # optional
592
34
593
594
TESTS::
595
596
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4)
597
sage: TestSuite(I).run()
598
"""
599
def __init__(self, G, d, max_part, sgs=None):
600
r"""
601
TESTS::
602
603
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4)
604
"""
605
SearchForest.__init__(self, algorithm = 'breadth', category = (FiniteEnumeratedSets(), FiniteEnumeratedSets().Quotients()))
606
self._permgroup = G
607
self.n = G.degree()
608
self._sum = d
609
if max_part is None:
610
self._max_part = -1
611
else:
612
self._max_part = max_part
613
614
# self.sgs: strong_generating_system
615
if sgs is None:
616
self._sgs = G.strong_generating_system()
617
else:
618
self._sgs = map(lambda x: list(x), list(sgs))
619
620
def _repr_(self):
621
r"""
622
TESTS::
623
624
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]])); S
625
Integer vectors of length 4 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
626
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6); S
627
Integer vectors of length 4 and of sum 6 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
628
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4); S
629
Vectors of length 4 and of sum 6 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
630
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=4); S
631
Integer vectors of length 4 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
632
"""
633
if self._sum is not None:
634
if self._max_part >= 0:
635
return "Vectors of length %s and of sum %s whose entries is in {0, ..., %s} enumerated up to the action of %s"%(self.n, self._sum, self._max_part, self.permutation_group())
636
else:
637
return "Integer vectors of length %s and of sum %s enumerated up to the action of %s"%(self.n, self._sum, self.permutation_group())
638
else:
639
return "Integer vectors of length %s whose entries is in {0, ..., %s} enumerated up to the action of %s"%(self.n, self._max_part, self.permutation_group())
640
641
def roots(self):
642
r"""
643
Returns the root of generation of ``self``.This method is
644
required to build the tree structure of ``self`` which
645
inherits from the class
646
:class:`~sage.combinat.backtrack.SearchForest`.
647
648
EXAMPLES::
649
650
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
651
sage: I.roots()
652
[[0, 0, 0, 0]]
653
"""
654
return [self.element_class(self, self.n*[0,], check=False)]
655
656
def children(self, x):
657
r"""
658
Returns the list of children of the element ``x``. This method
659
is required to build the tree structure of ``self`` which
660
inherits from the class
661
:class:`~sage.combinat.backtrack.SearchForest`.
662
663
EXAMPLES::
664
665
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
666
sage: I.children(I([2,1,0,0], check=False))
667
[[2, 2, 0, 0], [2, 1, 1, 0], [2, 1, 0, 1]]
668
"""
669
return canonical_children(self._sgs, x, -1)
670
671
def permutation_group(self):
672
r"""
673
Returns the permutation group given to define ``self``.
674
675
EXAMPLES::
676
677
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 5)
678
sage: I.permutation_group()
679
Permutation Group with generators [(1,2,3)]
680
"""
681
return self._permgroup
682
683
def __contains__(self, v):
684
r"""
685
TESTS::
686
687
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),6)
688
sage: [6,0,0,0] in I
689
True
690
sage: [5,0,1,0] in I
691
True
692
sage: [0,5,1,0] in I
693
False
694
sage: [3,0,1,3] in I
695
False
696
sage: [3,3,1,0] in I
697
False
698
"""
699
try:
700
return (self(v)).parent() is self
701
except:
702
return False
703
704
def __call__(self, v, check=True):
705
r"""
706
Make `v` an element living in ``self``.
707
708
TESTS::
709
710
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 4)
711
sage: v = I([2,1,0,1]); v
712
[2, 1, 0, 1]
713
sage: v.parent()
714
Integer vectors of length 4 and of sum 4 enumerated up to
715
the action of Permutation Group with generators
716
[(1,2,3,4)]
717
"""
718
try:
719
if v.parent() is self:
720
return v
721
else:
722
raise ValueError, '%s shoud be a Python list of integer'%(v)
723
except:
724
return self.element_class(self, list(v), check=check)
725
726
def __iter__(self):
727
r"""
728
TESTS::
729
730
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4)
731
sage: for i in I: i
732
[4, 0, 0, 0]
733
[3, 1, 0, 0]
734
[3, 0, 1, 0]
735
[3, 0, 0, 1]
736
[2, 2, 0, 0]
737
[2, 1, 1, 0]
738
[2, 1, 0, 1]
739
[2, 0, 2, 0]
740
[2, 0, 1, 1]
741
[1, 1, 1, 1]
742
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=7, max_part=3)
743
sage: for i in I: i
744
[3, 3, 1, 0]
745
[3, 3, 0, 1]
746
[3, 2, 2, 0]
747
[3, 2, 1, 1]
748
[3, 2, 0, 2]
749
[3, 1, 3, 0]
750
[3, 1, 2, 1]
751
[3, 1, 1, 2]
752
[3, 0, 2, 2]
753
[2, 2, 2, 1]
754
"""
755
if self._max_part < 0:
756
return self.elements_of_depth_iterator(self._sum)
757
else:
758
SF = SearchForest((self([0]*(self.n), check=False),),
759
lambda x : map(lambda y : self(y, check=False), canonical_children(self._sgs, x, self._max_part)),
760
algorithm = 'breadth')
761
if self._sum is None:
762
return iter(SF)
763
else:
764
return SF.elements_of_depth_iterator(self._sum)
765
766
def is_canonical(self, v, check=True):
767
r"""
768
Returns ``True`` if the integer list ``v`` is maximal in its
769
orbit under the action of the permutation group given to
770
define ``self``. Such integer vectors are said to be
771
canonical. A vector `v` is canonical if and only if
772
773
.. math::
774
775
v = \max_{\text{lex order}} \{g \cdot v | g \in G \}
776
777
EXAMPLES::
778
779
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=3)
780
sage: I.is_canonical([3,0,0,0])
781
True
782
sage: I.is_canonical([1,0,2,0])
783
False
784
sage: I.is_canonical([2,0,1,0])
785
True
786
"""
787
if check:
788
assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector'%v
789
assert (self.n == len(v)), '%s should be of length %s'%(v, self.n)
790
for p in v:
791
assert (p == NN(p)), 'Elements of %s should be integers'%s
792
return is_canonical(self._sgs, self.element_class(self, list(v), check=False))
793
794
def ambient(self):
795
r"""
796
Return the ambient space from which ``self`` is a quotient.
797
798
EXAMPLES::
799
800
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6); S.ambient()
801
Integer vectors that sum to 6
802
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=12); S.ambient()
803
Integer vectors that sum to 6 with constraints: max_part=12
804
805
.. todo::
806
807
Integer vectors should accept ``max_part`` as a single argument, and the following should change::
808
809
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=12); S.ambient()
810
Integer vectors
811
812
"""
813
if self._sum is not None:
814
if self._max_part <= -1:
815
return IntegerVectors(n=self._sum)
816
else:
817
return IntegerVectors(n=self._sum, max_part=self._max_part)
818
else:
819
# Fix me once max_part should be accepted as a single
820
# argument for integer vectors
821
return IntegerVectors(max_part=self._max_part)
822
823
def lift(self, elt):
824
r"""
825
Lift the element ``elt`` inside the ambient space from which ``self`` is a quotient.
826
827
EXAMPLES::
828
829
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=1)
830
sage: v = S.lift([1,0,1,0]); v
831
[1, 0, 1, 0]
832
sage: v in IntegerVectors(max_part=1)
833
True
834
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=6)
835
sage: v = S.lift(S.list()[5]); v
836
[4, 1, 1, 0]
837
sage: v in IntegerVectors(n=6)
838
True
839
"""
840
# TODO: For now, Sage integer vectors are just python list.
841
# Once Integer vectors will have an element class, update this
842
# code properly
843
return list(elt)
844
845
def retract(self, elt):
846
r"""
847
Return the canonical representative of the orbit of the
848
integer ``elt`` under the action of the permutation group
849
defining ``self``.
850
851
If the element ``elt`` is already maximal in its orbits for
852
the lexicographic order, ``elt`` is thus the good
853
representative for its orbit.
854
855
EXAMPLES::
856
857
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=2, max_part=1)
858
sage: S.retract([1,1,0,0])
859
[1, 1, 0, 0]
860
sage: S.retract([1,0,1,0])
861
[1, 0, 1, 0]
862
sage: S.retract([1,0,0,1])
863
[1, 1, 0, 0]
864
sage: S.retract([0,1,1,0])
865
[1, 1, 0, 0]
866
sage: S.retract([0,1,0,1])
867
[1, 0, 1, 0]
868
sage: S.retract([0,0,1,1])
869
[1, 1, 0, 0]
870
"""
871
# TODO: Once Sage integer vector will have a data structure
872
# based on ClonableIntArray, remove the conversion intarray
873
assert len(elt) == self.n, "%s is a quotient set of %s"%(self, self.ambient())
874
if self._sum is not None:
875
assert sum(elt) == self._sum, "%s is a quotient set of %s"%(self, self.ambient())
876
if self._max_part >= 0:
877
assert max(elt) <= self._max_part, "%s is a quotient set of %s"%(self, self.ambient())
878
intarray = self.element_class(self, elt, check=False)
879
return self.element_class(self, canonical_representative_of_orbit_of(self._sgs, intarray), check=False)
880
881
def an_element(self):
882
r"""
883
Returns an element of ``self`` or raises an EmptySetError when
884
``self`` is empty.
885
886
EXAMPLES::
887
888
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=0, max_part=1); S.an_element()
889
[0, 0, 0, 0]
890
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=1, max_part=1); S.an_element()
891
[1, 0, 0, 0]
892
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=2, max_part=1); S.an_element()
893
[1, 1, 0, 0]
894
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=3, max_part=1); S.an_element()
895
[1, 1, 1, 0]
896
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=4, max_part=1); S.an_element()
897
[1, 1, 1, 1]
898
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=5, max_part=1); S.an_element()
899
Traceback (most recent call last):
900
...
901
EmptySetError
902
"""
903
if self._max_part < 0:
904
return self([self._sum]+(self.n-1)*[0], check=False)
905
else:
906
try:
907
v = iter(self)
908
return v.next()
909
except StopIteration:
910
from sage.categories.sets_cat import EmptySetError
911
raise EmptySetError
912
913
def orbit(self, v):
914
r"""
915
Returns the orbit of the vector ``v`` under the action of the
916
permutation group defining ``self``. The result is a set.
917
918
INPUT:
919
920
- ``v`` - an element of ``self`` or any list of length the
921
degree of the permutation group.
922
923
EXAMPLES:
924
925
We convert the result in a list in increasing lexicographic
926
order, to get a reproducible doctest::
927
928
sage: from sage.combinat.enumeration_mod_permgroup import lex_cmp
929
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]),4)
930
sage: I.orbit([1,1,1,1])
931
set([[1, 1, 1, 1]])
932
sage: sorted(I.orbit([3,0,0,1]), cmp=lex_cmp)
933
[[0, 0, 1, 3], [0, 1, 3, 0], [1, 3, 0, 0], [3, 0, 0, 1]]
934
"""
935
assert isinstance(v, (list, ClonableIntArray)), '%s shoud be a Python list or an element of %s'%(v, self)
936
try:
937
if v.parent() is self:
938
return orbit(self._sgs, v)
939
except:
940
return orbit(self._sgs, self.element_class(self, v, check=False))
941
942
class Element(ClonableIntArray):
943
r"""
944
Element class for the set of integer vectors with constraints enumerated
945
modulo the action of a permutation group. These vectors are clonable lists
946
of integers which must check conditions comming form the parent as in
947
the method :meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_with_constraints.Element.check`.
948
949
TESTS::
950
951
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 4)
952
sage: v = I.element_class(I, [3,1,0,0]); v
953
[3, 1, 0, 0]
954
sage: TestSuite(v).run()
955
sage: v = I.element_class(I, [3,2,0,0])
956
Traceback (most recent call last):
957
...
958
AssertionError: [3, 2, 0, 0] should be a integer vector of sum 4
959
"""
960
def check(self):
961
r"""
962
Checks that ``self`` meets the constraints of being an element of ``self.parent()``.
963
964
EXAMPLES::
965
966
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 4)
967
sage: v = I.an_element()
968
sage: v.check()
969
sage: w = I([0,4,0,0], check=False); w
970
[0, 4, 0, 0]
971
sage: w.check()
972
Traceback (most recent call last):
973
...
974
AssertionError
975
"""
976
if self.parent()._sum is not None:
977
assert sum(self) == self.parent()._sum, '%s should be a integer vector of sum %s'%(self, self.parent()._sum)
978
if self.parent()._max_part >= 0:
979
assert max(self) <= self.parent()._max_part, 'Entries of %s must be inferiors to %s'%(self, self.parent()._max_part)
980
assert self.parent().is_canonical(self)
981
982