Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/sets/disjoint_union_enumerated_sets.py
4095 views
1
"""
2
Disjoint union of enumerated sets
3
4
AUTHORS:
5
6
- Florent Hivert (2009-07/09): initial implementation.
7
- Florent Hivert (2010-03): classcall related stuff.
8
- Florent Hivert (2010-12): fixed facade element construction.
9
"""
10
#****************************************************************************
11
# Copyright (C) 2009 Florent Hivert <[email protected]>
12
#
13
# Distributed under the terms of the GNU General Public License (GPL)
14
# http://www.gnu.org/licenses/
15
#****************************************************************************
16
17
# from sage.structure.element import Element
18
from sage.structure.parent import Parent
19
from sage.structure.element_wrapper import ElementWrapper
20
from sage.sets.family import Family
21
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
22
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
23
from sage.rings.infinity import Infinity
24
from sage.misc.all import cached_method, lazy_attribute
25
from sage.structure.unique_representation import UniqueRepresentation
26
27
class DisjointUnionEnumeratedSets(UniqueRepresentation, Parent):
28
"""
29
A class for disjoint unions of enumerated sets.
30
31
INPUT:
32
33
- ``family`` -- a list (or iterable or family) of enumerated sets
34
- ``keepkey`` -- a boolean
35
- ``facade`` -- a boolean
36
37
This models the enumerated set obtained by concatenating together
38
the specified ordered sets. The later are supposed to be pairwise
39
disjoint; otherwise, a multiset is created.
40
41
The argument ``family`` can be a list, a tuple, a dictionary, or a
42
family. If it is not a family it is first converted into a family
43
(see :func:`sage.sets.family.Family`).
44
45
Experimental options:
46
47
By default, there is no way to tell from which set of the union an
48
element is generated. The option ``keepkey=True`` keeps track of
49
those by returning pairs ``(key, el)`` where ``key`` is the index
50
of the set to which ``el`` belongs. When this option is specified,
51
the enumerated sets need not be disjoint anymore.
52
53
With the option ``facade=False`` the elements are wrapped in an
54
object whose parent is the disjoint union itself. The wrapped
55
object can then be recovered using the 'value' attribute.
56
57
The two options can be combined.
58
59
The names of those options is imperfect, and subject to change in
60
future versions. Feedback welcome.
61
62
EXAMPLES:
63
64
The input can be a list or a tuple of FiniteEnumeratedSets::
65
66
sage: U1 = DisjointUnionEnumeratedSets((
67
... FiniteEnumeratedSet([1,2,3]),
68
... FiniteEnumeratedSet([4,5,6])))
69
sage: U1
70
Disjoint union of Family ({1, 2, 3}, {4, 5, 6})
71
sage: U1.list()
72
[1, 2, 3, 4, 5, 6]
73
sage: U1.cardinality()
74
6
75
76
The input can also be a dictionary::
77
78
sage: U2 = DisjointUnionEnumeratedSets({1: FiniteEnumeratedSet([1,2,3]),
79
... 2: FiniteEnumeratedSet([4,5,6])})
80
sage: U2
81
Disjoint union of Finite family {1: {1, 2, 3}, 2: {4, 5, 6}}
82
sage: U2.list()
83
[1, 2, 3, 4, 5, 6]
84
sage: U2.cardinality()
85
6
86
87
However in that case the enumeration order is not specified.
88
89
In general the input can be any family::
90
91
sage: U3 = DisjointUnionEnumeratedSets(
92
... Family([2,3,4], Permutations, lazy=True))
93
sage: U3
94
Disjoint union of Lazy family (Permutations(i))_{i in [2, 3, 4]}
95
sage: U3.cardinality()
96
32
97
sage: it = iter(U3)
98
sage: [it.next(), it.next(), it.next(), it.next(), it.next(), it.next()]
99
[[1, 2], [2, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1]]
100
sage: U3.unrank(18)
101
[2, 4, 1, 3]
102
103
This allows for infinite unions::
104
105
sage: U4 = DisjointUnionEnumeratedSets(
106
... Family(NonNegativeIntegers(), Permutations))
107
sage: U4
108
Disjoint union of Lazy family (Permutations(i))_{i in Non negative integers}
109
sage: U4.cardinality()
110
+Infinity
111
sage: it = iter(U4)
112
sage: [it.next(), it.next(), it.next(), it.next(), it.next(), it.next()]
113
[[], [1], [1, 2], [2, 1], [1, 2, 3], [1, 3, 2]]
114
sage: U4.unrank(18)
115
[2, 3, 1, 4]
116
117
.. warning ::
118
119
Beware that some of the operations assume in that case that infinitely
120
many of the enumerated sets are non empty.
121
122
123
.. rubric:: Experimental options
124
125
We demonstrate the ``keepkey`` option::
126
127
sage: Ukeep = DisjointUnionEnumeratedSets(
128
... Family(range(4), Permutations), keepkey=True)
129
sage: it = iter(Ukeep)
130
sage: [it.next() for i in range(6)]
131
[(0, []), (1, [1]), (2, [1, 2]), (2, [2, 1]), (3, [1, 2, 3]), (3, [1, 3, 2])]
132
sage: type(it.next()[1])
133
<class 'sage.combinat.permutation.Permutation_class'>
134
135
We now demonstrate the ``facade`` option::
136
137
sage: UNoFacade = DisjointUnionEnumeratedSets(
138
... Family(range(4), Permutations), facade=False)
139
sage: it = iter(UNoFacade)
140
sage: [it.next() for i in range(6)]
141
[[], [1], [1, 2], [2, 1], [1, 2, 3], [1, 3, 2]]
142
sage: el = it.next(); el
143
[2, 1, 3]
144
sage: type(el)
145
<class 'sage.structure.element_wrapper.DisjointUnionEnumeratedSets_with_category.element_class'>
146
sage: el.parent() == UNoFacade
147
True
148
sage: elv = el.value; elv
149
[2, 1, 3]
150
sage: type(elv)
151
<class 'sage.combinat.permutation.Permutation_class'>
152
153
154
Possible extensions: the current enumeration order is not suitable
155
for unions of infinite enumerated sets (except possibly for the
156
last one). One could add options to specify alternative enumeration
157
orders (anti-diagonal, round robin, ...) to handle this case.
158
159
160
.. rubric:: Inheriting from ``DisjointUnionEnumeratedSets``
161
162
There are two different use cases for inheriting from
163
:class:`DisjointUnionEnumeratedSets`: writing a parent which
164
happens to be a disjoint union of some known parents, or writing
165
generic disjoint unions for some particular classes of
166
:class:`sage.categories.enumerated_sets.EnumeratedSets`.
167
168
- In the first use case, the input of the ``__init__`` method is
169
most likely different from that of
170
:class:`DisjointUnionEnumeratedSets`. Then, one simply
171
writes the ``__init__`` method as usual::
172
173
sage: class MyUnion(DisjointUnionEnumeratedSets):
174
... def __init__(self):
175
... DisjointUnionEnumeratedSets.__init__(self,
176
... Family([1,2], Permutations))
177
sage: pp = MyUnion()
178
sage: pp.list()
179
[[1], [1, 2], [2, 1]]
180
181
In case the :meth:`__init__` method takes optional arguments,
182
or does some normalization on them, a specific method
183
``__classcall_private__`` is required (see the
184
documentation of :class:`UniqueRepresentation`).
185
186
- In the second use case, the input of the ``__init__`` method
187
is the same as that of :class:`DisjointUnionEnumeratedSets`;
188
one therefore wants to inherit the :meth:`__classcall_private__`
189
method as well, which can be achieved as follows::
190
191
sage: class UnionOfSpecialSets(DisjointUnionEnumeratedSets):
192
... __classcall_private__ = staticmethod(DisjointUnionEnumeratedSets.__classcall_private__)
193
...
194
sage: psp = UnionOfSpecialSets(Family([1,2], Permutations))
195
sage: psp.list()
196
[[1], [1, 2], [2, 1]]
197
198
TESTS::
199
200
sage: TestSuite(U1).run()
201
sage: TestSuite(U2).run()
202
sage: TestSuite(U3).run()
203
sage: TestSuite(U4).run()
204
doctest:...: UserWarning: Disjoint union of Lazy family (Permutations(i))_{i in Non negative integers} is an infinite union
205
The default implementation of __contains__ can loop forever. Please overload it.
206
sage: TestSuite(Ukeep).run()
207
sage: TestSuite(UNoFacade).run()
208
209
The following three lines are required for the pickling tests,
210
because the classes ``MyUnion`` and ``UnionOfSpecialSets`` have
211
been defined interactively::
212
213
sage: import __main__
214
sage: __main__.MyUnion = MyUnion
215
sage: __main__.UnionOfSpecialSets = UnionOfSpecialSets
216
217
sage: TestSuite(pp).run()
218
sage: TestSuite(psp).run()
219
220
"""
221
222
@staticmethod
223
def __classcall_private__(cls, fam, facade=True,
224
keepkey=False, category=None):
225
"""
226
Normalization of arguments; see :class:`UniqueRepresentation`.
227
228
TESTS:
229
230
We check that disjoint unions have unique representation::
231
232
sage: U1 = DisjointUnionEnumeratedSets({1: FiniteEnumeratedSet([1,2,3]),
233
... 2: FiniteEnumeratedSet([4,5,6])})
234
sage: U2 = DisjointUnionEnumeratedSets({1: FiniteEnumeratedSet([1,2,3]),
235
... 2: FiniteEnumeratedSet([4,5,6])})
236
sage: U1 == U2
237
True
238
sage: U1 is U2 # indirect doctest
239
True
240
sage: U3 = DisjointUnionEnumeratedSets({1: FiniteEnumeratedSet([1,2,3]),
241
... 2: FiniteEnumeratedSet([4,5])})
242
sage: U1 == U3
243
False
244
"""
245
# facade = options.pop('facade', True);
246
# keepkey = options.pop('keepkey', False);
247
assert(isinstance(facade, bool))
248
assert(isinstance(keepkey, bool))
249
return super(DisjointUnionEnumeratedSets, cls).__classcall__(
250
cls, Family(fam),
251
facade = facade, keepkey = keepkey, category=category)
252
253
254
def __init__(self, family, facade=True, keepkey=False, category = None):
255
"""
256
TESTS::
257
258
sage: U = DisjointUnionEnumeratedSets({1: FiniteEnumeratedSet([1,2,3]),
259
... 2: FiniteEnumeratedSet([4,5,6])})
260
sage: TestSuite(U).run()
261
"""
262
self._family = family
263
self._facade = facade
264
self._keepkey = keepkey
265
if self._is_category_initialized():
266
return
267
if category is None:
268
# try to guess if the result is infinite or not.
269
if self._family in InfiniteEnumeratedSets():
270
category = InfiniteEnumeratedSets()
271
elif self._family.last().cardinality() == Infinity:
272
category = InfiniteEnumeratedSets()
273
else:
274
category = FiniteEnumeratedSets()
275
Parent.__init__(self, category = category)
276
277
def _repr_(self):
278
"""
279
TESTS::
280
281
sage: U = DisjointUnionEnumeratedSets({1: FiniteEnumeratedSet([1,2,3]),
282
... 2: FiniteEnumeratedSet([4,5,6])})
283
sage: U # indirect doctest
284
Disjoint union of Finite family {1: {1, 2, 3}, 2: {4, 5, 6}}
285
"""
286
return "Disjoint union of %s"%self._family
287
288
289
def _is_a(self, x):
290
"""
291
Check if a Sage object belongs to self. This methods is a helper for
292
:meth:`__contains__` and the constructor :meth:`_element_constructor_`.
293
294
EXAMPLES::
295
296
sage: U4 = DisjointUnionEnumeratedSets(
297
... Family(NonNegativeIntegers(), Compositions))
298
sage: U4._is_a(Composition([3,2,1,1]))
299
doctest:...: UserWarning: Disjoint union of Lazy family (Compositions(i))_{i in Non negative integers} is an infinite union
300
The default implementation of __contains__ can loop forever. Please overload it.
301
True
302
"""
303
if self._keepkey:
304
return (isinstance(x, tuple) and
305
x[0] in self._family.keys() and
306
x[1] in self._family[x[0]])
307
else:
308
from warnings import warn
309
if self._family.cardinality() == Infinity:
310
warn("%s is an infinite union\nThe default implementation of __contains__ can loop forever. Please overload it."%(self))
311
return any(x in a for a in self._family)
312
313
314
def __contains__(self, x):
315
"""
316
.. warning::
317
318
If ``self`` is an infinite union and if the answer is
319
logically False, this will loop forever and never answer
320
``False``. Therefore, a warning is issued.
321
322
EXAMPLES::
323
324
sage: U4 = DisjointUnionEnumeratedSets(
325
... Family(NonNegativeIntegers(), Partitions))
326
sage: Partition([]) in U4
327
doctest:...: UserWarning: Disjoint union of Lazy family (Partitions(i))_{i in Non negative integers} is an infinite union
328
The default implementation of __contains__ can loop forever. Please overload it.
329
True
330
331
Note: one has to use a different family from the previous one in this
332
file otherwise the warning is not re-issued::
333
334
sage: Partition([3,2,1,1]) in U4
335
True
336
337
The following call will loop forever::
338
339
sage: 2 in U4 # not tested, loop forever
340
"""
341
if self._facade:
342
return self._is_a(x)
343
else:
344
if isinstance(x, self.element_class):
345
return True
346
else:
347
return self._is_a(x)
348
349
def __iter__(self):
350
"""
351
TESTS::
352
353
sage: U4 = DisjointUnionEnumeratedSets(
354
... Family(NonNegativeIntegers(), Permutations))
355
sage: it = iter(U4)
356
sage: [it.next(), it.next(), it.next(), it.next(), it.next(), it.next()]
357
[[], [1], [1, 2], [2, 1], [1, 2, 3], [1, 3, 2]]
358
359
sage: U4 = DisjointUnionEnumeratedSets(
360
... Family(NonNegativeIntegers(), Permutations),
361
... keepkey=True, facade=False)
362
sage: it = iter(U4)
363
sage: [it.next(), it.next(), it.next(), it.next(), it.next(), it.next()]
364
[(0, []), (1, [1]), (2, [1, 2]), (2, [2, 1]), (3, [1, 2, 3]), (3, [1, 3, 2])]
365
sage: el = it.next(); el.parent() == U4
366
True
367
sage: el.value == (3, Permutation([2,1,3]))
368
True
369
"""
370
for k in self._family.keys():
371
for el in self._family[k]:
372
if self._keepkey:
373
el = (k, el)
374
if self._facade:
375
yield el
376
else:
377
yield self.element_class(el, parent=self) # Bypass correctness tests
378
379
def an_element(self):
380
"""
381
Returns an element of this disjoint union, as per :meth:`Sets.ParentMethods.an_element`.
382
383
EXAMPLES::
384
385
sage: U4 = DisjointUnionEnumeratedSets(
386
... Family([3, 5, 7], Permutations))
387
sage: U4.an_element()
388
[1, 2, 3]
389
"""
390
return self._an_element_from_iterator()
391
392
@cached_method
393
def cardinality(self):
394
"""
395
Returns the cardinality of this disjoint union.
396
397
EXAMPLES:
398
399
For finite disjoint unions, the cardinality is computed by
400
summing the cardinalities of the enumerated sets::
401
402
sage: U = DisjointUnionEnumeratedSets(Family([0,1,2,3], Permutations))
403
sage: U.cardinality()
404
10
405
406
For infinite disjoint unions, this makes the assumption that
407
the result is infinite::
408
409
sage: U = DisjointUnionEnumeratedSets(
410
... Family(NonNegativeIntegers(), Permutations))
411
sage: U.cardinality()
412
+Infinity
413
414
.. warning::
415
416
as pointed out in the main documentation, it is
417
possible to construct examples where this is incorrect::
418
419
sage: U = DisjointUnionEnumeratedSets(
420
... Family(NonNegativeIntegers(), lambda x: []))
421
sage: U.cardinality() # Should be 0!
422
+Infinity
423
424
"""
425
if self._family.cardinality() == Infinity:
426
return Infinity
427
return sum(set.cardinality() for set in self._family)
428
429
@lazy_attribute
430
def _element_constructor_(self):
431
"""
432
TESTS::
433
434
sage: U = DisjointUnionEnumeratedSets(
435
... Family([1,2,3], Partitions), facade=False)
436
sage: U._element_constructor_
437
<bound method DisjointUnionEnumeratedSets_with_category._element_constructor_default of Disjoint union of Finite family {1: Partitions of the integer 1, 2: Partitions of the integer 2, 3: Partitions of the integer 3}>
438
sage: U = DisjointUnionEnumeratedSets(
439
... Family([1,2,3], Partitions), facade=True)
440
sage: U._element_constructor_
441
Traceback (most recent call last):
442
...
443
AttributeError: 'DisjointUnionEnumeratedSets_with_category' object has no attribute '_element_constructor_'
444
"""
445
if not self._facade:
446
return self._element_constructor_default
447
else:
448
return NotImplemented
449
450
def _element_constructor_default(self, el):
451
r"""
452
TESTS::
453
454
sage: U = DisjointUnionEnumeratedSets(
455
... Family([1,2,3], Partitions), facade=False)
456
sage: U([1]) # indirect doctest
457
[1]
458
sage: U([2,1]) # indirect doctest
459
[2, 1]
460
sage: U([1,3,2]) # indirect doctest
461
Traceback (most recent call last):
462
...
463
ValueError: Value [1, 3, 2] does not belong to Disjoint union of Finite family {1: Partitions of the integer 1, 2: Partitions of the integer 2, 3: Partitions of the integer 3}
464
465
sage: U = DisjointUnionEnumeratedSets(
466
... Family([1,2,3], Partitions), keepkey=True, facade=False)
467
sage: U((1, [1])) # indirect doctest
468
(1, [1])
469
sage: U((3,[2,1])) # indirect doctest
470
(3, [2, 1])
471
sage: U((4,[2,1])) # indirect doctest
472
Traceback (most recent call last):
473
...
474
ValueError: Value (4, [2, 1]) does not belong to Disjoint union of Finite family {1: Partitions of the integer 1, 2: Partitions of the integer 2, 3: Partitions of the integer 3}
475
"""
476
if isinstance(el, self.element_class):
477
el = el.value
478
if self._is_a(el):
479
return self.element_class(el, parent=self)
480
else:
481
raise ValueError, "Value %s does not belong to %s"%(el, self)
482
483
@lazy_attribute
484
def Element(self):
485
"""
486
TESTS::
487
488
sage: U = DisjointUnionEnumeratedSets(
489
... Family([1,2,3], Partitions), facade=False)
490
sage: U.Element
491
<class 'sage.structure.element_wrapper.ElementWrapper'>
492
sage: U = DisjointUnionEnumeratedSets(
493
... Family([1,2,3], Partitions), facade=True)
494
sage: U.Element
495
Traceback (most recent call last):
496
...
497
AttributeError: 'DisjointUnionEnumeratedSets_with_category' object has no attribute 'Element'
498
"""
499
if not self._facade:
500
return ElementWrapper
501
else:
502
return NotImplemented
503
504