Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matroids/dual_matroid.py
8817 views
1
r"""
2
Dual matroids
3
4
Theory
5
======
6
7
Let `M` be a matroid with ground set `E`. If `B` is the set of bases of `M`,
8
then the set `\{E - b : b \in B\}` is the set of bases of another matroid, the
9
dual of `M`.
10
11
EXAMPLES::
12
13
sage: M = matroids.named_matroids.Fano()
14
sage: N = M.dual()
15
sage: M.is_basis('abc')
16
True
17
sage: N.is_basis('defg')
18
True
19
sage: M.dual().dual() == M
20
True
21
22
Implementation
23
==============
24
25
The class ``DualMatroid`` wraps around a matroid instance to represent its
26
dual. Only useful for classes that don't have an explicit construction of the
27
dual (such as :class:`RankMatroid <sage.matroids.rank_matroid.RankMatroid>`
28
and
29
:class:`CircuitClosuresMatroid <sage.matroids.circuit_closures_matroid.CircuitClosuresMatroid>`).
30
It is also used as default implementation of the method
31
:meth:`M.dual() <sage.matroids.matroid.Matroid.dual>`.
32
For direct access to the ``DualMatroid`` constructor, run::
33
34
sage: from sage.matroids.advanced import *
35
36
See also :mod:`sage.matroids.advanced`.
37
38
39
AUTHORS:
40
41
- Rudi Pendavingh, Michael Welsh, Stefan van Zwam (2013-04-01): initial version
42
43
Methods
44
=======
45
"""
46
#*****************************************************************************
47
# Copyright (C) 2013 Rudi Pendavingh <[email protected]>
48
# Copyright (C) 2013 Michael Welsh <[email protected]>
49
# Copyright (C) 2013 Stefan van Zwam <[email protected]>
50
#
51
#
52
# Distributed under the terms of the GNU General Public License (GPL)
53
# as published by the Free Software Foundation; either version 2 of
54
# the License, or (at your option) any later version.
55
# http://www.gnu.org/licenses/
56
#*****************************************************************************
57
58
from matroid import Matroid
59
60
61
class DualMatroid(Matroid):
62
r"""
63
Dual of a matroid.
64
65
For some matroid representations it can be computationally expensive to
66
derive an explicit representation of the dual. This class wraps around any
67
matroid to provide an abstract dual. It also serves as default
68
implementation.
69
70
INPUT:
71
72
- ``matroid`` - a matroid.
73
74
EXAMPLES::
75
76
sage: from sage.matroids.advanced import *
77
sage: M = matroids.named_matroids.Vamos()
78
sage: Md = DualMatroid(M) # indirect doctest
79
sage: Md.rank('abd') == M.corank('abd')
80
True
81
sage: Md
82
Dual of 'Vamos: Matroid of rank 4 on 8 elements with circuit-closures
83
{3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'}, {'e', 'f', 'g', 'h'},
84
{'a', 'b', 'g', 'h'}, {'c', 'd', 'e', 'f'}},
85
4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}}'
86
"""
87
88
def __init__(self, matroid):
89
"""
90
See class definition for documentation.
91
92
EXAMPLES::
93
94
sage: from sage.matroids.advanced import *
95
sage: M = matroids.named_matroids.Vamos()
96
sage: Md = DualMatroid(M) # indirect doctest
97
sage: Md.rank('abd') == M.corank('abd')
98
True
99
sage: Md
100
Dual of 'Vamos: Matroid of rank 4 on 8 elements with
101
circuit-closures
102
{3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'},
103
{'e', 'f', 'g', 'h'}, {'a', 'b', 'g', 'h'},
104
{'c', 'd', 'e', 'f'}},
105
4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}}'
106
"""
107
if not isinstance(matroid, Matroid):
108
raise TypeError("no matroid provided to take dual of.")
109
self._matroid = matroid
110
111
def groundset(self):
112
"""
113
Return the groundset of the matroid.
114
115
The groundset is the set of elements that comprise the matroid.
116
117
OUTPUT:
118
119
A set.
120
121
EXAMPLES::
122
123
sage: M = matroids.named_matroids.Pappus().dual()
124
sage: sorted(M.groundset())
125
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
126
"""
127
return self._matroid.groundset()
128
129
def _rank(self, X):
130
r"""
131
Return the rank of a set ``X``.
132
133
This method does no checking on ``X``, and
134
``X`` may be assumed to have the same interface as ``frozenset``.
135
136
INPUT:
137
138
- ``X`` -- an object with Python's ``frozenset`` interface.
139
140
OUTPUT:
141
142
The rank of ``X`` in the matroid.
143
144
EXAMPLES::
145
146
sage: M = matroids.named_matroids.NonPappus().dual()
147
sage: M._rank(['a', 'b', 'c'])
148
3
149
150
"""
151
return self._matroid._corank(X)
152
153
def _corank(self, X):
154
"""
155
Return the corank of a set.
156
157
INPUT:
158
159
- ``X`` -- An object with Python's ``frozenset`` interface containing a subset of ``self.groundset()``.
160
161
OUTPUT:
162
163
The corank of ``X``.
164
165
EXAMPLES::
166
167
sage: M = matroids.named_matroids.Vamos().dual()
168
sage: M._corank(set(['a', 'e', 'g', 'd', 'h']))
169
4
170
"""
171
return self._matroid._rank(X)
172
173
def _max_independent(self, X):
174
"""
175
Compute a maximal independent subset.
176
177
INPUT:
178
179
- ``X`` -- An object with Python's ``frozenset`` interface containing
180
a subset of ``self.groundset()``.
181
182
OUTPUT:
183
184
A maximal independent subset of ``X``.
185
186
EXAMPLES::
187
188
sage: M = matroids.named_matroids.Vamos().dual()
189
sage: sorted(M._max_independent(set(['a', 'c', 'd', 'e', 'f'])))
190
['a', 'c', 'd', 'e']
191
192
"""
193
return self._matroid._max_coindependent(X)
194
195
def _circuit(self, X):
196
"""
197
Return a minimal dependent subset.
198
199
INPUT:
200
201
- ``X`` -- An object with Python's ``frozenset`` interface containing
202
a subset of ``self.groundset()``.
203
204
OUTPUT:
205
206
A circuit contained in ``X``, if it exists. Otherwise an error is
207
raised.
208
209
EXAMPLES::
210
211
sage: M = matroids.named_matroids.Vamos().dual()
212
sage: sorted(sage.matroids.matroid.Matroid._circuit(M,
213
....: set(['a', 'c', 'd', 'e', 'f'])))
214
['c', 'd', 'e', 'f']
215
sage: sorted(sage.matroids.matroid.Matroid._circuit(M,
216
....: set(['a', 'c', 'd'])))
217
Traceback (most recent call last):
218
...
219
ValueError: no circuit in independent set.
220
221
"""
222
return self._matroid._cocircuit(X)
223
224
def _closure(self, X):
225
"""
226
Return the closure of a set.
227
228
INPUT:
229
230
- ``X`` -- An object with Python's ``frozenset`` interface containing
231
a subset of ``self.groundset()``.
232
233
OUTPUT:
234
235
The smallest closed set containing ``X``.
236
237
EXAMPLES::
238
239
sage: M = matroids.named_matroids.Vamos().dual()
240
sage: sorted(M._closure(set(['a', 'b', 'c'])))
241
['a', 'b', 'c', 'd']
242
243
"""
244
return self._matroid._coclosure(X)
245
246
def _max_coindependent(self, X):
247
"""
248
Compute a maximal coindependent subset.
249
250
INPUT:
251
252
- ``X`` -- An object with Python's ``frozenset`` interface containing
253
a subset of ``self.groundset()``.
254
255
OUTPUT:
256
257
A maximal coindependent subset of ``X``.
258
259
EXAMPLES::
260
261
sage: M = matroids.named_matroids.Vamos().dual()
262
sage: sorted(M._max_coindependent(set(['a', 'c', 'd', 'e', 'f'])))
263
['a', 'd', 'e', 'f']
264
265
"""
266
return self._matroid._max_independent(X)
267
268
def _coclosure(self, X):
269
"""
270
Return the coclosure of a set.
271
272
INPUT:
273
274
- ``X`` -- An object with Python's ``frozenset`` interface containing
275
a subset of ``self.groundset()``.
276
277
OUTPUT:
278
279
The smallest coclosed set containing ``X``.
280
281
EXAMPLES::
282
283
sage: M = matroids.named_matroids.Vamos().dual()
284
sage: sorted(M._coclosure(set(['a', 'b', 'c'])))
285
['a', 'b', 'c', 'd']
286
287
"""
288
return self._matroid._closure(X)
289
290
def _cocircuit(self, X):
291
"""
292
Return a minimal codependent subset.
293
294
INPUT:
295
296
- ``X`` -- An object with Python's ``frozenset`` interface containing
297
a subset of ``self.groundset()``.
298
299
OUTPUT:
300
301
A cocircuit contained in ``X``, if it exists. Otherwise an error is
302
raised.
303
304
EXAMPLES::
305
306
sage: M = matroids.named_matroids.Vamos().dual()
307
sage: sorted(M._cocircuit(set(['a', 'c', 'd', 'e', 'f'])))
308
['c', 'd', 'e', 'f']
309
sage: sorted(M._cocircuit(set(['a', 'c', 'd'])))
310
Traceback (most recent call last):
311
...
312
ValueError: no circuit in independent set.
313
314
"""
315
return self._matroid._circuit(X)
316
317
def _minor(self, contractions=None, deletions=None):
318
r"""
319
Return a minor.
320
321
INPUT:
322
323
- ``contractions`` -- An object with Python's ``frozenset`` interface
324
containing a subset of ``self.groundset()``.
325
- ``deletions`` -- An object with Python's ``frozenset`` interface
326
containing a subset of ``self.groundset()``.
327
328
OUTPUT:
329
330
A ``DualMatroid`` instance representing
331
`(``self._matroid`` / ``deletions`` \ ``contractions``)^*`
332
333
.. NOTE::
334
335
This method does NOT do any checks. Besides the assumptions above,
336
we assume the following:
337
338
- ``contractions`` is independent
339
- ``deletions`` is coindependent
340
- ``contractions`` and ``deletions`` are disjoint.
341
342
EXAMPLES::
343
344
sage: M = matroids.named_matroids.Vamos().dual()
345
sage: N = M._minor(contractions=set(['a']), deletions=set([]))
346
sage: N._minor(contractions=set([]), deletions=set(['b', 'c']))
347
Dual of 'M / {'b', 'c'} \ {'a'}, where M is Vamos: Matroid of rank
348
4 on 8 elements with circuit-closures
349
{3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'}, {'e', 'f', 'g',
350
'h'}, {'a', 'b', 'g', 'h'}, {'c', 'd', 'e', 'f'}}, 4: {{'a', 'b',
351
'c', 'd', 'e', 'f', 'g', 'h'}}}'
352
"""
353
# Assumption: if self._matroid cannot make a dual, neither can its minor.
354
return DualMatroid(self._matroid._minor(contractions=deletions, deletions=contractions))
355
356
def dual(self):
357
"""
358
Return the dual of the matroid.
359
360
Let `M` be a matroid with ground set `E`. If `B` is the set of bases
361
of `M`, then the set `\{E - b : b \in B\}` is the set of bases of
362
another matroid, the *dual* of `M`. Note that the dual of the dual of
363
`M` equals `M`, so if this is the
364
:class:`DualMatroid` instance
365
wrapping `M` then the returned matroid is `M`.
366
367
OUTPUT:
368
369
The dual matroid.
370
371
EXAMPLES::
372
373
sage: M = matroids.named_matroids.Pappus().dual()
374
sage: N = M.dual()
375
sage: N.rank()
376
3
377
sage: N
378
Pappus: Matroid of rank 3 on 9 elements with circuit-closures
379
{2: {{'a', 'b', 'c'}, {'a', 'f', 'h'}, {'c', 'e', 'g'},
380
{'b', 'f', 'g'}, {'c', 'd', 'h'}, {'d', 'e', 'f'},
381
{'a', 'e', 'i'}, {'b', 'd', 'i'}, {'g', 'h', 'i'}},
382
3: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}}}
383
"""
384
return self._matroid
385
386
# REPRESENTATION
387
def _repr_(self):
388
"""
389
Return a string representation of the matroid.
390
391
EXAMPLES::
392
393
sage: M = matroids.named_matroids.Vamos().dual()
394
sage: print(M._repr_())
395
Dual of 'Vamos: Matroid of rank 4 on 8 elements with
396
circuit-closures
397
{3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'},
398
{'e', 'f', 'g', 'h'}, {'a', 'b', 'g', 'h'},
399
{'c', 'd', 'e', 'f'}},
400
4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}}'
401
"""
402
return "Dual of '" + repr(self._matroid) + "'"
403
404
# COMPARISON
405
406
def __hash__(self):
407
r"""
408
Return an invariant of the matroid.
409
410
This function is called when matroids are added to a set. It is very
411
desirable to override it so it can distinguish matroids on the same
412
groundset, which is a very typical use case!
413
414
.. WARNING::
415
416
This method is linked to __richcmp__ (in Cython) and __cmp__ or
417
__eq__/__ne__ (in Python). If you override one, you should (and in
418
Cython: MUST) override the other!
419
420
EXAMPLES::
421
422
sage: M = matroids.named_matroids.Vamos().dual()
423
sage: N = matroids.named_matroids.Vamos().dual()
424
sage: O = matroids.named_matroids.Vamos()
425
sage: hash(M) == hash(N)
426
True
427
sage: hash(M) == hash(O)
428
False
429
"""
430
return hash(("Dual", hash(self._matroid)))
431
432
def __eq__(self, other):
433
"""
434
Compare two matroids.
435
436
INPUT:
437
438
- ``other`` -- A matroid.
439
440
OUTPUT:
441
442
Boolean.
443
444
EXAMPLES::
445
446
sage: from sage.matroids.advanced import *
447
sage: M1 = matroids.named_matroids.Fano()
448
sage: M2 = CircuitClosuresMatroid(M1.dual())
449
sage: M3 = CircuitClosuresMatroid(M1).dual()
450
sage: M4 = CircuitClosuresMatroid(groundset='abcdefg',
451
....: circuit_closures={3: ['abcdefg'], 2: ['beg', 'cdb', 'cfg',
452
....: 'ace', 'fed', 'gad', 'fab']}).dual()
453
sage: M1.dual() == M2 # indirect doctest
454
False
455
sage: M2 == M3
456
False
457
sage: M3 == M4
458
True
459
"""
460
if not isinstance(other, DualMatroid):
461
return False
462
return self._matroid == other._matroid
463
464
def __ne__(self, other):
465
"""
466
Compare two matroids.
467
468
INPUT:
469
470
- ``other`` -- A matroid.
471
472
OUTPUT:
473
474
Boolean.
475
476
EXAMPLES::
477
478
sage: from sage.matroids.advanced import *
479
sage: M1 = matroids.named_matroids.Fano()
480
sage: M2 = CircuitClosuresMatroid(M1.dual())
481
sage: M3 = CircuitClosuresMatroid(M1).dual()
482
sage: M4 = CircuitClosuresMatroid(groundset='abcdefg',
483
....: circuit_closures={3: ['abcdefg'], 2: ['beg', 'cdb', 'cfg',
484
....: 'ace', 'fed', 'gad', 'fab']}).dual()
485
sage: M1.dual() != M2 # indirect doctest
486
True
487
sage: M2 != M3
488
True
489
sage: M3 != M4
490
False
491
"""
492
return not self.__eq__(other)
493
494
# COPYING, LOADING, SAVING
495
496
def __copy__(self):
497
"""
498
Create a shallow copy.
499
500
EXAMPLES::
501
502
sage: M = matroids.named_matroids.Vamos()
503
sage: N = copy(M) # indirect doctest
504
sage: M == N
505
True
506
sage: M.groundset() is N.groundset()
507
True
508
509
"""
510
N = DualMatroid(self._matroid)
511
if getattr(self, '__custom_name') is not None: # because of name wrangling, this is not caught by the default copy
512
N.rename(getattr(self, '__custom_name'))
513
return N
514
515
def __deepcopy__(self, memo={}):
516
"""
517
Create a deep copy.
518
519
.. NOTE::
520
521
Since matroids are immutable, a shallow copy normally suffices.
522
523
EXAMPLES::
524
525
sage: M = matroids.named_matroids.Vamos().dual()
526
sage: N = deepcopy(M) # indirect doctest
527
sage: M == N
528
True
529
sage: M.groundset() is N.groundset()
530
False
531
"""
532
from copy import deepcopy
533
N = DualMatroid(deepcopy(self._matroid, memo))
534
if getattr(self, '__custom_name') is not None: # because of name wrangling, this is not caught by the default deepcopy
535
N.rename(deepcopy(getattr(self, '__custom_name'), memo))
536
return N
537
538
def __reduce__(self):
539
"""
540
Save the matroid for later reloading.
541
542
OUTPUT:
543
544
A tuple ``(unpickle, (version, data))``, where ``unpickle`` is the
545
name of a function that, when called with ``(version, data)``,
546
produces a matroid isomorphic to ``self``. ``version`` is an integer
547
(currently 0) and ``data`` is a tuple ``(M, name)`` where ``M`` is
548
the internal matroid, and ``name`` is a custom name.
549
550
EXAMPLES::
551
552
sage: M = matroids.named_matroids.Vamos().dual()
553
sage: M == loads(dumps(M)) # indirect doctest
554
True
555
sage: loads(dumps(M))
556
Dual of 'Vamos: Matroid of rank 4 on 8 elements with
557
circuit-closures
558
{3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'},
559
{'e', 'f', 'g', 'h'}, {'a', 'b', 'g', 'h'},
560
{'c', 'd', 'e', 'f'}},
561
4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}}'
562
563
"""
564
import sage.matroids.unpickling
565
data = (self._matroid, getattr(self, '__custom_name'))
566
version = 0
567
return sage.matroids.unpickling.unpickle_dual_matroid, (version, data)
568
569