Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/sets/finite_set_maps.py
4057 views
1
r"""
2
Maps between finite sets
3
4
This module implements parents modeling the set of all maps between
5
two finite sets. At the user level, any such parent should be
6
constructed using the factory class :class:`FiniteSetMaps` which
7
properly selects which of its subclasses to use.
8
9
AUTHORS:
10
11
- Florent Hivert
12
"""
13
#*****************************************************************************
14
# Copyright (C) 2010 Florent Hivert <[email protected]>,
15
#
16
# Distributed under the terms of the GNU General Public License (GPL)
17
# http://www.gnu.org/licenses/
18
#*****************************************************************************
19
20
21
from sage.structure.parent import Parent
22
from sage.rings.integer import Integer
23
from sage.structure.unique_representation import UniqueRepresentation
24
from sage.categories.sets_cat import Sets, EmptySetError
25
from sage.categories.finite_monoids import FiniteMonoids
26
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
27
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
28
from sage.combinat.cartesian_product import CartesianProduct
29
from sage.sets.integer_range import IntegerRange
30
from sage.sets.finite_set_map_cy import (
31
FiniteSetMap_MN, FiniteSetMap_Set,
32
FiniteSetEndoMap_N, FiniteSetEndoMap_Set )
33
from sage.misc.cachefunc import cached_method
34
35
# TODO: finite set maps should be morphisms in the category of finite sets
36
37
class FiniteSetMaps(UniqueRepresentation, Parent):
38
"""
39
Maps between finite sets
40
41
Constructs the set of all maps between two sets. The sets can be
42
given using any of the three following ways:
43
44
1. an object in the category ``Sets()``.
45
46
2. a finite iterable. In this case, an object of the class
47
:class:`~sage.sets.finite_enumerated_set.FiniteEnumeratedSet`
48
is constructed from the iterable.
49
50
3. an integer ``n`` designing the set `\{1, 2, \dots, n\}`. In this case
51
an object of the class :class:`~sage.sets.integer_range.IntegerRange`
52
is constructed.
53
54
INPUT:
55
56
- ``domain`` -- a set, finite iterable, or integer.
57
58
- ``codomain`` -- a set, finite iterable, integer, or ``None``
59
(default). In this last case, the maps are endo-maps of the domain.
60
61
- ``action`` -- ``"left"`` (default) or ``"right"``. The side
62
where the maps act on the domain. This is used in particular to
63
define the meaning of the product (composition) of two maps.
64
65
- ``category`` -- the category in which the sets of maps is
66
constructed. By default, this is ``FiniteMonoids()`` if the domain and
67
codomain coincide, and ``FiniteEnumeratedSets()`` otherwise.
68
69
OUTPUT:
70
71
an instance of a subclass of :class:`FiniteSetMaps` modeling
72
the set of all maps between ``domain`` and ``codomain``.
73
74
EXAMPLES:
75
76
We construct the set ``M`` of all maps from `\{a,b\}` to `\{3,4,5\}`::
77
78
sage: M = FiniteSetMaps(["a", "b"], [3, 4, 5]); M
79
Maps from {'a', 'b'} to {3, 4, 5}
80
sage: M.cardinality()
81
9
82
sage: M.domain()
83
{'a', 'b'}
84
sage: M.codomain()
85
{3, 4, 5}
86
sage: for f in M: print f
87
map: a -> 3, b -> 3
88
map: a -> 3, b -> 4
89
map: a -> 3, b -> 5
90
map: a -> 4, b -> 3
91
map: a -> 4, b -> 4
92
map: a -> 4, b -> 5
93
map: a -> 5, b -> 3
94
map: a -> 5, b -> 4
95
map: a -> 5, b -> 5
96
97
Elements can be constructed from functions and dictionaries::
98
99
sage: M(lambda c: ord(c)-94)
100
map: a -> 3, b -> 4
101
102
sage: M.from_dict({'a':3, 'b':5})
103
map: a -> 3, b -> 5
104
105
If the domain is equal to the codomain, then maps can be
106
composed::
107
108
sage: M = FiniteSetMaps([1, 2, 3])
109
sage: f = M.from_dict({1:2, 2:1, 3:3}); f
110
map: 1 -> 2, 2 -> 1, 3 -> 3
111
sage: g = M.from_dict({1:2, 2:3, 3:1}); g
112
map: 1 -> 2, 2 -> 3, 3 -> 1
113
114
sage: f * g
115
map: 1 -> 1, 2 -> 3, 3 -> 2
116
117
This makes `M` into a monoid::
118
119
sage: M.category()
120
Category of finite monoids
121
sage: M.one()
122
map: 1 -> 1, 2 -> 2, 3 -> 3
123
124
By default, composition is from right to left, which corresponds
125
to an action on the left. If one specifies ``action`` to right,
126
then the composition is from left to right::
127
128
sage: M = FiniteSetMaps([1, 2, 3], action = 'right')
129
sage: f = M.from_dict({1:2, 2:1, 3:3})
130
sage: g = M.from_dict({1:2, 2:3, 3:1})
131
sage: f * g
132
map: 1 -> 3, 2 -> 2, 3 -> 1
133
134
If the domains and codomains are both of the form `\{0,\dots\}`,
135
then one can use the shortcut::
136
137
sage: M = FiniteSetMaps(2,3); M
138
Maps from {0, 1} to {0, .., 2}
139
sage: M.cardinality()
140
9
141
142
For a compact notation, the elements are then printed as lists
143
`[f(i), i=0,\dots]`::
144
145
sage: list(M)
146
[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
147
148
TESTS::
149
150
sage: TestSuite(FiniteSetMaps(0)).run()
151
sage: TestSuite(FiniteSetMaps(0, 2)).run()
152
sage: TestSuite(FiniteSetMaps(2, 0)).run()
153
sage: TestSuite(FiniteSetMaps([], [])).run()
154
sage: TestSuite(FiniteSetMaps([1, 2], [])).run()
155
sage: TestSuite(FiniteSetMaps([], [1, 2])).run()
156
"""
157
@staticmethod
158
def __classcall_private__(cls, domain, codomain = None, action = "left", category = None):
159
"""
160
TESTS::
161
162
sage: FiniteSetMaps(3)
163
Maps from {0, .., 2} to itself
164
sage: FiniteSetMaps(4, 2)
165
Maps from {0, .., 3} to {0, 1}
166
sage: FiniteSetMaps(4, ["a","b","c"])
167
Maps from {0, .., 3} to {'a', 'b', 'c'}
168
sage: FiniteSetMaps([1,2], ["a","b","c"])
169
Maps from {1, 2} to {'a', 'b', 'c'}
170
sage: FiniteSetMaps([1,2,4], 3)
171
Maps from {1, 2, 4} to {0, .., 2}
172
"""
173
if codomain is None:
174
if isinstance(domain, (int, Integer)):
175
return FiniteSetEndoMaps_N(domain, action, category)
176
else:
177
if domain not in Sets():
178
domain = FiniteEnumeratedSet(domain)
179
return FiniteSetEndoMaps_Set(domain, action, category)
180
181
if isinstance(domain, (int, Integer)):
182
if isinstance(codomain, (int, Integer)):
183
return FiniteSetMaps_MN(domain, codomain, category)
184
else:
185
domain = IntegerRange(domain)
186
if isinstance(codomain, (int, Integer)):
187
codomain = IntegerRange(codomain)
188
189
if domain not in Sets():
190
domain = FiniteEnumeratedSet(domain)
191
if codomain not in Sets():
192
codomain = FiniteEnumeratedSet(codomain)
193
return FiniteSetMaps_Set(domain, codomain, category)
194
195
def cardinality(self):
196
"""
197
The cardinality of ``self``
198
199
EXAMPLES::
200
201
sage: FiniteSetMaps(4, 3).cardinality()
202
81
203
"""
204
return self.codomain().cardinality()**self.domain().cardinality()
205
206
class FiniteSetMaps_MN(FiniteSetMaps):
207
"""
208
The set of all maps from `\{1, 2, \dots, m\}` to `\{1, 2, \dots, n\}`.
209
210
Users should use the factory class :class:`FiniteSetMaps` to
211
create instances of this class.
212
213
INPUT:
214
215
- ``m``, ``n`` -- integers
216
217
- ``category`` -- the category in which the sets of maps is
218
constructed. It must be a sub-category of
219
``FiniteEnumeratedSets()`` which is the default value.
220
"""
221
222
def __init__(self, m, n, category=None):
223
"""
224
TESTS::
225
226
sage: M = FiniteSetMaps(2,3)
227
sage: M.category()
228
Category of finite enumerated sets
229
sage: M.__class__
230
<class 'sage.sets.finite_set_maps.FiniteSetMaps_MN_with_category'>
231
sage: TestSuite(M).run()
232
"""
233
Parent.__init__(self,
234
category=FiniteEnumeratedSets().or_subcategory(category))
235
self._m = Integer(m)
236
self._n = Integer(n)
237
238
def domain(self):
239
"""
240
The domain of ``self``
241
242
EXAMPLES::
243
244
sage: FiniteSetMaps(3,2).domain()
245
{0, .., 2}
246
"""
247
return IntegerRange(self._m)
248
249
def codomain(self):
250
"""
251
The codomain of ``self``
252
253
EXAMPLES::
254
255
sage: FiniteSetMaps(3,2).codomain()
256
{0, 1}
257
"""
258
return IntegerRange(self._n)
259
260
def _repr_(self):
261
"""
262
TESTS::
263
264
sage: FiniteSetMaps(2,3)
265
Maps from {0, 1} to {0, .., 2}
266
"""
267
return "Maps from %s to %s"%(self.domain(), self.codomain())
268
269
def __contains__(self, x):
270
"""
271
EXAMPLES::
272
273
sage: M = FiniteSetMaps(3,2)
274
sage: [0,1,1] in M
275
True
276
sage: [1,2,4] in M
277
False
278
"""
279
if isinstance(x, self.element_class):
280
return x.parent() is self and len(x) == self._m
281
else:
282
x = list(x)
283
if len(x) != self._m:
284
return False
285
for i in x:
286
if not (0 <= i < self._n):
287
return False
288
return True
289
290
def an_element(self):
291
"""
292
Returns a map in ``self``
293
294
EXAMPLES::
295
296
sage: M = FiniteSetMaps(4, 2)
297
sage: M.an_element()
298
[0, 0, 0, 0]
299
300
sage: M = FiniteSetMaps(0, 0)
301
sage: M.an_element()
302
[]
303
304
An exception :class:`~sage.categories.sets_cat.EmptySetError`
305
is raised if this set is empty, that is if the codomain is
306
empty and the domain is not.
307
308
sage: M = FiniteSetMaps(4, 0)
309
sage: M.cardinality()
310
0
311
sage: M.an_element()
312
Traceback (most recent call last):
313
...
314
EmptySetError
315
"""
316
if self._m > 0 and self._n == 0:
317
raise EmptySetError
318
return self._from_list_([0]*self._m)
319
320
def __iter__(self):
321
"""
322
EXAMPLES::
323
324
sage: M = FiniteSetMaps(2,2)
325
sage: M.list()
326
[[0, 0], [0, 1], [1, 0], [1, 1]]
327
328
TESTS::
329
330
sage: FiniteSetMaps(0,0).list()
331
[[]]
332
sage: FiniteSetMaps(0,1).list()
333
[[]]
334
sage: FiniteSetMaps(0,10).list()
335
[[]]
336
sage: FiniteSetMaps(1,0).list()
337
[]
338
sage: FiniteSetMaps(1,1).list()
339
[[0]]
340
"""
341
for v in CartesianProduct(*([range(self._n)]*self._m)):
342
yield self._from_list_(v)
343
344
def _from_list_(self, v):
345
"""
346
EXAMPLES::
347
348
sage: M = FiniteSetMaps(4,3)
349
sage: M._from_list_([2,1,1,0])
350
[2, 1, 1, 0]
351
"""
352
return self.element_class(self, v, check=False)
353
354
def _element_constructor_(self, *args, **keywords):
355
"""
356
EXAMPLES::
357
358
sage: M = FiniteSetMaps(4,3)
359
sage: M([2,1,1,0])
360
[2, 1, 1, 0]
361
"""
362
return self.element_class(self, *args, **keywords)
363
364
Element = FiniteSetMap_MN
365
366
367
class FiniteSetMaps_Set(FiniteSetMaps_MN):
368
"""
369
The sets of all maps between two sets
370
371
Users should use the factory class :class:`FiniteSetMaps` to
372
create instances of this class.
373
374
INPUT:
375
376
- ``domain`` -- an object in the category ``FiniteSets()``.
377
378
- ``codomain`` -- an object in the category ``FiniteSets()``.
379
380
- ``category`` -- the category in which the sets of maps is
381
constructed. It must be a sub-category of ``FiniteEnumeratedSets()``
382
which is the default value.
383
"""
384
def __init__(self, domain, codomain, category=None):
385
"""
386
EXAMPLES::
387
388
sage: M = FiniteSetMaps(["a", "b"], [3, 4, 5])
389
sage: M
390
Maps from {'a', 'b'} to {3, 4, 5}
391
sage: M.cardinality()
392
9
393
sage: for f in M: print f
394
map: a -> 3, b -> 3
395
map: a -> 3, b -> 4
396
map: a -> 3, b -> 5
397
map: a -> 4, b -> 3
398
map: a -> 4, b -> 4
399
map: a -> 4, b -> 5
400
map: a -> 5, b -> 3
401
map: a -> 5, b -> 4
402
map: a -> 5, b -> 5
403
404
TESTS::
405
406
sage: M.__class__
407
<class 'sage.sets.finite_set_maps.FiniteSetMaps_Set_with_category'>
408
sage: M.category()
409
Category of finite enumerated sets
410
sage: TestSuite(M).run()
411
"""
412
FiniteSetMaps_MN.__init__(self, domain.cardinality(), codomain.cardinality(),
413
category=category)
414
415
self._domain = domain
416
self._codomain = codomain
417
418
import sage.combinat.ranker as ranker
419
ldomain = domain.list()
420
lcodomain = codomain.list()
421
self._unrank_domain = ranker.unrank_from_list(ldomain)
422
self._rank_domain = ranker.rank_from_list(ldomain)
423
self._unrank_codomain = ranker.unrank_from_list(lcodomain)
424
self._rank_codomain = ranker.rank_from_list(lcodomain)
425
426
def domain(self):
427
"""
428
The domain of ``self``
429
430
EXAMPLES::
431
432
sage: FiniteSetMaps(["a", "b"], [3, 4, 5]).domain()
433
{'a', 'b'}
434
"""
435
return self._domain
436
437
def codomain(self):
438
"""
439
The codomain of ``self``
440
441
EXAMPLES::
442
443
sage: FiniteSetMaps(["a", "b"], [3, 4, 5]).codomain()
444
{3, 4, 5}
445
"""
446
return self._codomain
447
448
# TODO: consistency from_dict / from_list
449
def _from_list_(self, v):
450
"""
451
Create a function from a list
452
453
The list gives in the order of the element of the domain the
454
rank (index) of its image in the codomain.
455
456
EXAMPLES::
457
458
sage: M = FiniteSetMaps(["a", "b"], [3, 4, 5])
459
sage: M._from_list_([2,1])
460
map: a -> 5, b -> 4
461
"""
462
return self.element_class.from_list(self, v)
463
464
def from_dict(self, d):
465
"""
466
Create a map from a dictionary
467
468
EXAMPLES::
469
470
sage: M = FiniteSetMaps(["a", "b"], [3, 4, 5])
471
sage: M.from_dict({"a": 4, "b": 3})
472
map: a -> 4, b -> 3
473
"""
474
return self.element_class.from_dict(self, d)
475
476
Element = FiniteSetMap_Set
477
478
479
class FiniteSetEndoMaps_N(FiniteSetMaps_MN):
480
"""
481
The sets of all maps from `\{1, 2, \dots, n\}` to itself
482
483
Users should use the factory class :class:`FiniteSetMaps` to
484
create instances of this class.
485
486
INPUT:
487
488
- ``n`` -- an integer.
489
490
- ``category`` -- the category in which the sets of maps is
491
constructed. It must be a sub-category of ``FiniteMonoids()``
492
which is the default value.
493
"""
494
495
def __init__(self, n, action, category=None):
496
"""
497
EXAMPLES::
498
499
sage: M = FiniteSetMaps(3)
500
sage: M.category()
501
Category of finite monoids
502
sage: M.__class__
503
<class 'sage.sets.finite_set_maps.FiniteSetEndoMaps_N_with_category'>
504
sage: TestSuite(M).run()
505
"""
506
Parent.__init__(self, category=FiniteMonoids().or_subcategory(category))
507
self._m = n
508
self._n = n
509
self._action = action
510
511
@cached_method
512
def one(self):
513
"""
514
EXAMPLES::
515
516
sage: M = FiniteSetMaps(4)
517
sage: M.one()
518
[0, 1, 2, 3]
519
"""
520
return self._from_list_(range(self._n))
521
522
def an_element(self):
523
"""
524
Returns a map in ``self``
525
526
EXAMPLES::
527
528
sage: M = FiniteSetMaps(4)
529
sage: M.an_element()
530
[3, 2, 1, 0]
531
"""
532
return self._from_list_(range(self._n-1, -1, -1))
533
534
def _repr_(self):
535
"""
536
TESTS::
537
538
sage: FiniteSetMaps(2)
539
Maps from {0, 1} to itself
540
"""
541
return "Maps from %s to itself"%(self.domain())
542
543
Element = FiniteSetEndoMap_N
544
545
class FiniteSetEndoMaps_Set(FiniteSetMaps_Set, FiniteSetEndoMaps_N):
546
"""
547
The sets of all maps from a set to itself
548
549
Users should use the factory class :class:`FiniteSetMaps` to
550
create instances of this class.
551
552
INPUT:
553
554
- ``domain`` -- an object in the category ``FiniteSets()``.
555
556
- ``category`` -- the category in which the sets of maps is
557
constructed. It must be a sub-category of ``FiniteMonoids()``
558
which is the default value.
559
"""
560
def __init__(self, domain, action, category=None):
561
"""
562
TESTS::
563
564
sage: M = FiniteSetMaps(["a", "b", "c"])
565
sage: M.category()
566
Category of finite monoids
567
sage: M.__class__
568
<class 'sage.sets.finite_set_maps.FiniteSetEndoMaps_Set_with_category'>
569
sage: TestSuite(M).run()
570
"""
571
FiniteSetMaps_MN.__init__(self, domain.cardinality(), domain.cardinality(),
572
category=FiniteMonoids().or_subcategory(category))
573
574
self._domain = domain
575
self._codomain = domain
576
577
import sage.combinat.ranker as ranker
578
ldomain = domain.list()
579
self._unrank_domain = ranker.unrank_from_list(ldomain)
580
self._rank_domain = ranker.rank_from_list(ldomain)
581
self._unrank_codomain = self._unrank_domain
582
self._rank_codomain = self._rank_domain
583
self._action = action
584
585
Element = FiniteSetEndoMap_Set
586
587
588