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