Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/categories/enumerated_sets.py
4057 views
1
r"""
2
Enumerated Sets
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2009 Florent Hivert <[email protected]>
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
# http://www.gnu.org/licenses/
9
#******************************************************************************
10
11
from sage.misc.cachefunc import cached_method
12
from category_types import Category
13
from sage.categories.category_singleton import Category_singleton
14
from sage.categories.sets_cat import Sets
15
from sage.categories.sets_cat import EmptySetError
16
17
class EnumeratedSets(Category_singleton):
18
"""
19
The category of enumerated sets
20
21
An *enumerated set* is a *finite* or *countable* set or multiset `S`
22
together with a canonical enumeration of its elements;
23
conceptually, this is very similar to an immutable list. The main
24
difference lies in the names and the return type of the methods,
25
and of course the fact that the list of element is not supposed to
26
be expanded in memory. Whenever possible one should use one of the
27
two sub-categories :class:`FiniteEnumeratedSets` or
28
:class:`InfiniteEnumeratedSets`.
29
30
The purpose of this category is threefold:
31
32
- to fix a common interface for all these sets;
33
- to provide a bunch of default implementations;
34
- to provide consistency tests.
35
36
The standard methods for an enumerated set ``S`` are:
37
38
- ``S.cardinality()``: the number of elements of the set. This
39
is the equivalent for ``len`` on a list except that the
40
return value is specified to be a Sage :class:`Integer` or
41
``infinity``, instead of a Python ``int``;
42
43
- ``iter(S)``: an iterator for the elements of the set;
44
45
- ``S.list()``: the list of the elements of the set, when
46
possible; raises a NotImplementedError if the list is
47
predictably too large to be expanded in memory.
48
49
- ``S.unrank(n)``: the ``n-th`` element of the set when ``n`` is a sage
50
``Integer``. This is the equivanlent for ``l[n]`` on a list.
51
52
- ``S.rank(e)``: the position of the element ``e`` in the set;
53
This is equivalent to ``l.index(e)`` for a list except that
54
the return value is specified to be a Sage :class:`Integer`,
55
instead of a Python ``int``;
56
57
- ``S.first()``: the first object of the set; it is equivalent to
58
``S.unrank(0)``;
59
60
- ``S.next(e)``: the object of the set which follows ``e``; It is
61
equivalent to ``S.unrank(S.rank(e)+1)``.
62
63
- ``S.random_element()``: a random generator for an element of
64
the set. Unless otherwise stated, and for finite enumerated
65
sets, the probability is uniform.
66
67
For examples and tests see:
68
69
- ``FiniteEnumeratedSets().example()``
70
- ``InfiniteEnumeratedSets().example()``
71
72
73
EXAMPLES::
74
75
sage: EnumeratedSets()
76
Category of enumerated sets
77
sage: EnumeratedSets().super_categories()
78
[Category of sets]
79
sage: EnumeratedSets().all_super_categories()
80
[Category of enumerated sets, Category of sets, Category of sets with partial maps, Category of objects]
81
82
TESTS::
83
84
sage: C = EnumeratedSets()
85
sage: TestSuite(C).run()
86
"""
87
88
def super_categories(self):
89
"""
90
EXAMPLES::
91
92
sage: EnumeratedSets().super_categories()
93
[Category of sets]
94
"""
95
return [Sets()]
96
97
98
def _call_(self, X):
99
"""
100
Construct an object in this category from the data in ``X``.
101
102
EXAMPLES::
103
104
sage: EnumeratedSets()(Primes())
105
Set of all prime numbers: 2, 3, 5, 7, ...
106
107
For now, lists, tuples, sets, Sets are coerced into finite
108
enumerated sets::
109
110
sage: S = EnumeratedSets()([1, 2, 3]); S
111
{1, 2, 3}
112
sage: S.category()
113
Category of facade finite enumerated sets
114
115
sage: S = EnumeratedSets()((1, 2, 3)); S
116
{1, 2, 3}
117
sage: S = EnumeratedSets()(set([1, 2, 3])); S
118
{1, 2, 3}
119
sage: S = EnumeratedSets()(Set([1, 2, 3])); S
120
{1, 2, 3}
121
sage: S.category()
122
Category of facade finite enumerated sets
123
"""
124
import sage.sets.set
125
if isinstance(X, (tuple, list, set, sage.sets.set.Set_object_enumerated)):
126
return sage.sets.all.FiniteEnumeratedSet(X)
127
raise NotImplementedError
128
129
class ParentMethods:
130
131
def __iter__(self):
132
"""
133
An iterator for the enumerated set.
134
135
``iter(self)`` allows the combinatorial class to be treated as an
136
iterable. This if the default implementation from the category
137
``EnumeratedSets()`` it just goes through the iterator of the set
138
to count the number of objects.
139
140
By decreasing order of priority, the second column of the
141
following array shows which methods is used to define
142
``__iter__``, when the methods of the first column are overloaded:
143
144
+------------------------+---------------------------------+
145
| Needed methods | Default ``__iterator`` provided |
146
+========================+=================================+
147
| ``first`` and ``next`` | ``_iterator_from_next`` |
148
+------------------------+---------------------------------+
149
| ``unrank`` | ``_iterator_from_unrank`` |
150
+------------------------+---------------------------------+
151
| ``list` | ``_iterator_from_next`` |
152
+------------------------+---------------------------------+
153
154
If non of these are provided raise a ``NotImplementedError``
155
156
EXAMPLES::
157
158
We start with an example where nothing is implemented::
159
160
sage: class broken(UniqueRepresentation, Parent):
161
... def __init__(self):
162
... Parent.__init__(self, category = EnumeratedSets())
163
...
164
sage: it = iter(broken()); [it.next(), it.next(), it.next()]
165
Traceback (most recent call last):
166
...
167
NotImplementedError: iterator called but not implemented
168
169
Here is what happends when ``first`` and ``next`` are implemeted::
170
171
sage: class set_first_next(UniqueRepresentation, Parent):
172
... def __init__(self):
173
... Parent.__init__(self, category = EnumeratedSets())
174
... def first(self):
175
... return 0
176
... def next(self, elt):
177
... return elt+1
178
...
179
sage: it = iter(set_first_next()); [it.next(), it.next(), it.next()]
180
[0, 1, 2]
181
182
Let us try with ``unrank``::
183
184
sage: class set_unrank(UniqueRepresentation, Parent):
185
... def __init__(self):
186
... Parent.__init__(self, category = EnumeratedSets())
187
... def unrank(self, i):
188
... return i + 5
189
...
190
sage: it = iter(set_unrank()); [it.next(), it.next(), it.next()]
191
[5, 6, 7]
192
193
Let us finally try with ``list``::
194
195
sage: class set_list(UniqueRepresentation, Parent):
196
... def __init__(self):
197
... Parent.__init__(self, category = EnumeratedSets())
198
... def list(self):
199
... return [5, 6, 7]
200
...
201
sage: it = iter(set_list()); [it.next(), it.next(), it.next()]
202
[5, 6, 7]
203
204
"""
205
#Check to see if .first() and .next() are overridden in the subclass
206
if ( self.first != self._first_from_iterator and
207
self.next != self._next_from_iterator ):
208
return self._iterator_from_next()
209
#Check to see if .unrank() is overridden in the subclass
210
elif self.unrank != self._unrank_from_iterator:
211
return self._iterator_from_unrank()
212
#Finally, check to see if .list() is overridden in the subclass
213
elif self.list != self._list_default:
214
return self._iterator_from_list()
215
else:
216
raise NotImplementedError, "iterator called but not implemented"
217
218
def cardinality(self):
219
"""
220
The cardinality of ``self``.
221
222
``self.cardinality()`` should return the cardinality of the set
223
``self`` as a sage :class:`Integer` or as ``infinity``.
224
225
This if the default implementation from the category
226
``EnumeratedSets()`` it returns ``NotImplementedError`` since one does
227
not know whether the set is finite or not.
228
229
EXAMPLES::
230
231
sage: class broken(UniqueRepresentation, Parent):
232
... def __init__(self):
233
... Parent.__init__(self, category = EnumeratedSets())
234
...
235
sage: broken().cardinality()
236
Traceback (most recent call last):
237
...
238
NotImplementedError: unknown cardinality
239
"""
240
raise NotImplementedError, "unknown cardinality"
241
242
def list(self):
243
"""
244
Returns an error since the cardinality of self is not known.
245
246
EXAMPLES::
247
248
sage: class broken(UniqueRepresentation, Parent):
249
... def __init__(self):
250
... Parent.__init__(self, category = EnumeratedSets())
251
...
252
sage: broken().list()
253
Traceback (most recent call last):
254
...
255
NotImplementedError: unknown cardinality
256
"""
257
raise NotImplementedError, "unknown cardinality"
258
_list_default = list # needed by the check system.
259
260
261
def _first_from_iterator(self):
262
"""
263
The "first" element of ``self``.
264
265
``self.first()`` returns the first element of the set
266
``self``. This is a generic implementation from the category
267
``EnumeratedSets()`` which can be used when the method ``__iter__`` is
268
provided.
269
270
EXAMPLES::
271
272
sage: C = FiniteEnumeratedSets().example()
273
sage: C.first() # indirect doctest
274
1
275
"""
276
it = self.__iter__()
277
return it.next()
278
first = _first_from_iterator
279
280
def _next_from_iterator(self, obj):
281
"""
282
The "next" element after ``obj`` in ``self``.
283
284
``self.next(e)`` returns the element of the set ``self`` which
285
follows ``e``. This is a generic implementation from the category
286
``EnumeratedSets()`` which can be used when the method ``__iter__``
287
is provided.
288
289
Remark: this is the default (brute force) implementation
290
of the category ``EnumeratedSets()``. Its complexity is
291
`O(r)`, where `r` is the rank of ``obj``.
292
293
EXAMPLES::
294
295
sage: C = InfiniteEnumeratedSets().example()
296
sage: C._next_from_iterator(10) # indirect doctest
297
11
298
299
TODO: specify the behavior when ``obj`` is not in ``self``.
300
"""
301
it = iter(self)
302
el = it.next()
303
while el != obj:
304
el = it.next()
305
return it.next()
306
next = _next_from_iterator
307
308
309
def _unrank_from_iterator(self, r):
310
"""
311
The ``r``-th element of ``self``
312
313
``self.unrank(r)`` returns the ``r``-th element of self where
314
``r`` is an integer between ``0`` and ``n-1`` where ``n`` is the
315
cardinality of ``self``.
316
317
This is the default (brute force) implementation from the
318
category ``EnumeratedSets()`` which can be used when the
319
method ``__iter__`` is provided. Its complexity is `O(r)`,
320
where `r` is the rank of ``obj``.
321
322
EXAMPLES::
323
324
sage: C = FiniteEnumeratedSets().example()
325
sage: C.unrank(2) # indirect doctest
326
3
327
sage: C._unrank_from_iterator(5)
328
Traceback (most recent call last):
329
...
330
ValueError: the value must be between 0 and 2 inclusive
331
"""
332
counter = 0
333
for u in self:
334
if counter == r:
335
return u
336
counter += 1
337
raise ValueError, "the value must be between %s and %s inclusive"%(0,counter-1)
338
unrank = _unrank_from_iterator
339
340
def _rank_from_iterator(self, x):
341
"""
342
The rank of an element of ``self``
343
344
``self.unrank(x)`` returns the rank of `x`, that is its
345
position in the enumeration of ``self``. This is an
346
integer between ``0`` and ``n-1`` where ``n`` is the
347
cardinality of ``self``, or None if `x` is not in `self`.
348
349
This is the default (brute force) implementation from the
350
category ``EnumeratedSets()`` which can be used when the
351
method ``__iter__`` is provided. Its complexity is `O(r)`,
352
where `r` is the rank of ``obj``. For infinite enumerated
353
sets, this won't terminate when `x` is not in ``self``
354
355
EXAMPLES::
356
357
sage: C = FiniteEnumeratedSets().example()
358
sage: list(C)
359
[1, 2, 3]
360
sage: C.rank(3) # indirect doctest
361
2
362
sage: C.rank(5) # indirect doctest
363
"""
364
counter = 0
365
for u in self:
366
if u == x:
367
return counter
368
counter += 1
369
return None
370
371
rank = _rank_from_iterator
372
373
def _iterator_from_list(self):
374
"""
375
An iterator for the elements of ``self``.
376
377
``iter(self)`` returns an iterator for the elements
378
of ``self``. This is a generic implementation from the
379
category ``EnumeratedSets()`` which can be used when the
380
method ``list`` is provided.
381
382
EXAMPLES::
383
384
sage: C = FiniteEnumeratedSets().example()
385
sage: it = C._iterator_from_list()
386
sage: [it.next(), it.next(), it.next()]
387
[1, 2, 3]
388
"""
389
for x in self.list():
390
yield x
391
392
def _iterator_from_next(self):
393
"""
394
An iterator for the elements of ``self``.
395
396
``iter(self)`` returns an iterator for the element of
397
the set ``self``. This is a generic implementation from
398
the category ``EnumeratedSets()`` which can be used when
399
the methods ``first`` and ``next`` are provided.
400
401
EXAMPLES::
402
403
sage: C = InfiniteEnumeratedSets().example()
404
sage: it = C._iterator_from_next()
405
sage: [it.next(), it.next(), it.next(), it.next(), it.next()]
406
[0, 1, 2, 3, 4]
407
"""
408
f = self.first()
409
yield f
410
while True:
411
try:
412
f = self.next(f)
413
except (TypeError, ValueError ):
414
break
415
416
if f is None or f is False :
417
break
418
else:
419
yield f
420
421
def _iterator_from_unrank(self):
422
"""
423
An iterator for the elements of ``self``.
424
425
``iter(self)`` returns an iterator for the elements
426
of the set ``self``. This is a generic implementation from
427
the category ``EnumeratedSets()`` which can be used when
428
the method ``unrank`` is provided.
429
430
EXAMPLES::
431
432
sage: C = InfiniteEnumeratedSets().example()
433
sage: it = C._iterator_from_unrank()
434
sage: [it.next(), it.next(), it.next(), it.next(), it.next()]
435
[0, 1, 2, 3, 4]
436
"""
437
r = 0
438
u = self.unrank(r)
439
yield u
440
while True:
441
r += 1
442
try:
443
u = self.unrank(r)
444
except (TypeError, ValueError, IndexError):
445
break
446
447
if u == None:
448
break
449
else:
450
yield u
451
452
# This @cached_method is not really needed, since the method
453
# an_element itself is cached. We leave it for the moment, so
454
# that Parents that do not yet inherit properly from categories
455
# (e.g. Set([1,2,3]) can use the following trick:
456
# _an_element_ = EnumeratedSets.ParentMethods._an_element_
457
@cached_method
458
def _an_element_from_iterator(self):
459
"""
460
Returns the first element of ``self`` returned by :meth:`__iter__`
461
462
If ``self`` is empty, the exception
463
:class:`~sage.categories.sets_cat.EmptySetError` is raised instead.
464
465
This provides a generic implementation of the method
466
:meth:`_an_element_` for all parents in :class:`EnumeratedSets`.
467
468
EXAMPLES::
469
470
sage: C = FiniteEnumeratedSets().example(); C
471
An example of a finite enumerated set: {1,2,3}
472
sage: C.an_element() # indirect doctest
473
1
474
sage: S = Set([])
475
sage: S.an_element()
476
Traceback (most recent call last):
477
...
478
EmptySetError
479
480
TESTS::
481
482
sage: super(Parent, C)._an_element_
483
Cached version of <function _an_element_from_iterator at ...>
484
"""
485
it = self.__iter__()
486
try:
487
return it.next()
488
except StopIteration:
489
raise EmptySetError
490
491
# Should this be implemented from first instead?
492
_an_element_ = _an_element_from_iterator
493
494
#FIXME: use combinatorial_class_from_iterator once class_from_iterator.patch is in
495
def _some_elements_from_iterator(self):
496
"""
497
Returns some elements in ``self``.
498
499
See :class:`TestSuite` for a typical use case.
500
501
This is a generic implementation from the category
502
``EnumeratedSets()`` which can be used when the method
503
``__iter__`` is provided. It returns an iterator for up to
504
the first 100 elements of ``self``
505
506
EXAMPLES::
507
508
sage: C = FiniteEnumeratedSets().example()
509
sage: list(C.some_elements()) # indirect doctest
510
[1, 2, 3]
511
"""
512
nb = 0
513
for i in self:
514
yield i
515
nb += 1
516
if nb >= 100:
517
break
518
some_elements = _some_elements_from_iterator
519
520
def random_element(self):
521
"""
522
Returns a random element in ``self``.
523
524
Unless otherwise stated, and for finite enumerated sets,
525
the probability is uniform.
526
527
This is a generic implementation from the category
528
``EnumeratedSets()``. It raise a ``NotImplementedError``
529
since one does not know whether the set is finite.
530
531
EXAMPLES::
532
533
sage: class broken(UniqueRepresentation, Parent):
534
... def __init__(self):
535
... Parent.__init__(self, category = EnumeratedSets())
536
...
537
sage: broken().random_element()
538
Traceback (most recent call last):
539
...
540
NotImplementedError: unknown cardinality
541
"""
542
raise NotImplementedError, "unknown cardinality"
543
544
def map(self, f, name=None):
545
r"""
546
Returns the image `\{f(x) | x \in \text{self}\}` of this
547
enumerated set by `f`, as an enumerated set.
548
549
`f` is supposed to be injective.
550
551
EXAMPLES::
552
553
sage: R = SymmetricGroup(3).map(attrcall('reduced_word')); R
554
Image of Symmetric group of order 3! as a permutation group by *.reduced_word()
555
sage: R.cardinality()
556
6
557
sage: R.list()
558
[[], [2], [1], [2, 1], [1, 2], [1, 2, 1]]
559
sage: [ r for r in R]
560
[[], [2], [1], [2, 1], [1, 2], [1, 2, 1]]
561
562
.. warning::
563
564
If the function is not injective, then there may be
565
repeated elements::
566
567
sage: P = SymmetricGroup(3)
568
sage: P.list()
569
[(), (2,3), (1,2), (1,2,3), (1,3,2), (1,3)]
570
sage: P.map(attrcall('length')).list()
571
[0, 1, 1, 2, 2, 3]
572
573
.. warning::
574
575
:class:`MapCombinatorialClass` needs to be refactored to use categories::
576
577
sage: R.category() # todo: not implemented
578
Category of enumerated sets
579
sage: TestSuite(R).run(skip=['_test_an_element', '_test_category', '_test_some_elements'])
580
"""
581
from sage.combinat.combinat import MapCombinatorialClass
582
return MapCombinatorialClass(self, f, name)
583
584
#
585
# Consistency test suite for an enumerated set:
586
#
587
# If the enumerated set is large, one can stop some coeherency tests
588
# after looking at the first element by setting the following variable:
589
max_test_enumerated_set_loop=100 # TODO: Devise a sensible bound !!
590
##########
591
def _test_enumerated_set_contains(self, **options):
592
"""
593
Checks that the methods :meth:`.__contains__` and :meth:`.__iter__` are consistent.
594
595
See also :class:`TestSuite`.
596
597
TESTS::
598
599
sage: C = FiniteEnumeratedSets().example()
600
sage: C._test_enumerated_set_contains()
601
sage: TestSuite(C).run()
602
603
Let us now break the class::
604
605
sage: from sage.categories.examples.finite_enumerated_sets import Example
606
sage: class CCls(Example):
607
... def __contains__(self, obj):
608
... if obj == 3:
609
... return False
610
... else:
611
... return obj in C
612
sage: CC = CCls()
613
sage: CC._test_enumerated_set_contains()
614
Traceback (most recent call last):
615
...
616
AssertionError: False is not true
617
"""
618
tester = self._tester(**options)
619
i = 0
620
for w in self:
621
tester.assertTrue(w in self)
622
i += 1
623
if i > self.max_test_enumerated_set_loop:
624
return
625
626
def _test_enumerated_set_iter_list(self, **options):
627
"""
628
Checks that the methods :meth:`.list` and :meth:`.__iter__` are consistent.
629
630
See also: :class:`TestSuite`.
631
632
EXAMPLES::
633
634
sage: C = FiniteEnumeratedSets().example()
635
sage: C._test_enumerated_set_iter_list()
636
sage: TestSuite(C).run()
637
638
Let us now break the class::
639
640
sage: from sage.categories.examples.finite_enumerated_sets import Example
641
sage: class CCls(Example):
642
... def list(self):
643
... return [1,2,3,4]
644
sage: CC = CCls()
645
sage: CC._test_enumerated_set_iter_list()
646
Traceback (most recent call last):
647
...
648
AssertionError: 3 != 4
649
650
..warning: this test does nothing if the cardinality of
651
``self`` exceeds ``self.max_test_enumerated_set_loop``.
652
"""
653
tester = self._tester(**options)
654
if self.list != self._list_default:
655
# TODO: if self._cardinality is self._cardinality_from_iterator
656
# we could make sure to stop the counting at
657
# self.max_test_enumerated_set_loop
658
if self.cardinality() > self.max_test_enumerated_set_loop:
659
tester.info("Enumerated set too big; skipping test; see ``self.max_test_enumerated_set_loop``")
660
return
661
ls = self.list()
662
i = 0
663
for obj in self:
664
tester.assertEqual(obj, ls[i])
665
i += 1
666
tester.assertEqual(i, len(ls))
667
668
class ElementMethods:
669
670
def rank(self):
671
"""
672
Returns the rank of ``self`` in its parent.
673
674
See also :meth:`EnumeratedSets.ElementMethods.rank`
675
676
EXAMPLES::
677
678
sage: F = FiniteSemigroups().example(('a','b','c'))
679
sage: L = list(F); L
680
['a', 'c', 'ac', 'b', 'ba', 'bc', 'cb', 'ca', 'bca', 'ab', 'bac', 'cab', 'acb', 'cba', 'abc']
681
sage: L[7].rank()
682
7
683
"""
684
return self.parent().rank(self)
685
686