Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matroids/basis_exchange_matroid.pyx
8817 views
1
"""
2
Basis exchange matroids
3
4
:class:`BasisExchangeMatroid <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid>`
5
is an abstract class implementing default matroid functionality in terms of
6
basis exchange. Several concrete matroid classes are subclasses of this. They
7
have the following methods in addition to the ones provided by the parent
8
class :mod:`Matroid <sage.matroids.matroid>`.
9
10
- :func:`bases_count() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.bases_count>`
11
- :func:`groundset_list() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.groundset_list>`
12
13
AUTHORS:
14
15
- Rudi Pendavingh, Stefan van Zwam (2013-04-01): initial version
16
17
TESTS::
18
19
sage: from sage.matroids.advanced import *
20
sage: import sage.matroids.basis_exchange_matroid
21
sage: M = sage.matroids.basis_exchange_matroid.BasisExchangeMatroid(
22
....: groundset=[1, 2, 3], rank=2)
23
sage: TestSuite(M).run(skip="_test_pickling")
24
25
Note that this is an abstract base class, without data structure, so no
26
pickling mechanism was implemented.
27
28
Methods
29
=======
30
"""
31
#*****************************************************************************
32
# Copyright (C) 2013 Rudi Pendavingh <[email protected]>
33
# Copyright (C) 2013 Stefan van Zwam <[email protected]>
34
#
35
# Distributed under the terms of the GNU General Public License (GPL)
36
# as published by the Free Software Foundation; either version 2 of
37
# the License, or (at your option) any later version.
38
# http://www.gnu.org/licenses/
39
#*****************************************************************************
40
include 'sage/misc/bitset.pxi'
41
42
DEF BINT_EXCEPT = -2 ** 31 - 1
43
44
from matroid cimport Matroid
45
from set_system cimport SetSystem
46
from copy import copy
47
from itertools import combinations, permutations
48
49
cdef class BasisExchangeMatroid(Matroid):
50
r"""
51
Class BasisExchangeMatroid is a virtual class that derives from Matroid.
52
It implements each of the elementary matroid methods
53
(:meth:`rank() <sage.matroids.matroid.Matroid.rank>`,
54
:meth:`max_independent() <sage.matroids.matroid.Matroid.max_independent>`,
55
:meth:`circuit() <sage.matroids.matroid.Matroid.circuit>`,
56
:meth:`closure() <sage.matroids.matroid.Matroid.closure>` etc.),
57
essentially by crawling the base exchange graph of the matroid. This is
58
the graph whose vertices are the bases of the matroid, two bases being
59
adjacent in the graph if their symmetric difference has 2 members.
60
61
This base exchange graph is not stored as such, but should be provided
62
implicity by the child class in the form of two methods
63
``__is_exchange_pair(x, y)`` and ``__exchange(x, y)``, as well as an
64
initial basis. At any moment, BasisExchangeMatroid keeps a current basis
65
`B`. The method ``__is_exchange_pair(x, y)`` should return a boolean
66
indicating whether `B - x + y` is a basis. The method ``__exchange(x, y)``
67
is called when the current basis `B` is replaced by said `B-x + y`. It is
68
up to the child class to update its internal data structure to make
69
information relative to the new basis more accessible. For instance, a
70
linear matroid would perform a row reduction to make the column labeled by
71
`y` a standard basis vector (and therefore the columns indexed by `B-x+y`
72
would form an identity matrix).
73
74
Each of the elementary matroid methods has a straightforward greedy-type
75
implementation in terms of these two methods. For example, given a subset
76
`F` of the groundset, one can step to a basis `B` over the edges of the
77
base exchange graph which has maximal intersection with `F`, in each step
78
increasing the intersection of the current `B` with `F`. Then one computes
79
the rank of `F` as the cardinality of the intersection of `F` and `B`.
80
81
The following matroid classes can/will implement their oracle efficiently
82
by deriving from ``BasisExchangeMatroid``:
83
84
- :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`: keeps
85
a list of all bases.
86
87
- ``__is_exchange_pair(x, y)`` reduces to a query whether `B - x + y`
88
is a basis.
89
- ``__exchange(x, y)`` has no work to do.
90
91
- :class:`LinearMatroid <sage.matroids.linear_matroid.LinearMatroid>`:
92
keeps a matrix representation `A` of the matroid so that `A[B] = I`.
93
94
- ``__is_exchange_pair(x, y)`` reduces to testing whether `A[r, y]`
95
is nonzero, where `A[r, x]=1`.
96
- ``__exchange(x, y)`` should modify the matrix so that `A[B - x + y]`
97
becomes `I`, which means pivoting on `A[r, y]`.
98
99
- ``TransversalMatroid`` (not yet implemented): If `A` is a set of subsets
100
of `E`, then `I` is independent if it is a system of distinct
101
representatives of `A`, i.e. if `I` is covered by a matching of an
102
appropriate bipartite graph `G`, with color classes `A` and `E` and an
103
edge `(A_i,e)` if `e` is in the subset `A_i`. At any time you keep a
104
maximum matching `M` of `G` covering the current basis `B`.
105
106
- ``__is_exchange_pair(x, y)`` checks for the existence of an
107
`M`-alternating path `P` from `y` to `x`.
108
- ``__exchange(x, y)`` replaces `M` by the symmetric difference of
109
`M` and `E(P)`.
110
111
- ``AlgebraicMatroid`` (not yet implemented): keeps a list of polynomials
112
in variables `E - B + e` for each variable `e` in `B`.
113
114
- ``__is_exchange_pair(x, y)`` checks whether the polynomial that
115
relates `y` to `E-B` uses `x`.
116
- ``__exchange(x, y)`` make new list of polynomials by computing
117
resultants.
118
119
All but the first of the above matroids are algebraic, and all
120
implementations specializations of the algebraic one.
121
122
BasisExchangeMatroid internally renders subsets of the ground set as
123
bitsets. It provides optimized methods for enumerating bases, nonbases,
124
flats, circuits, etc.
125
"""
126
127
def __init__(self, groundset, basis=None, rank=None):
128
"""
129
Construct a BasisExchangeMatroid.
130
131
A BasisExchangeMatroid is a virtual class. It is unlikely that you
132
want to create a BasisExchangeMatroid from the command line. See the
133
class docstring for
134
:class:`BasisExchangeMatroid <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid>`.
135
136
INPUT:
137
138
- ``groundset`` -- a set.
139
- ``basis`` -- (default: ``None``) a subset of groundset.
140
- ``rank`` -- (default: ``None``) an integer.
141
142
This initializer sets up a correspondance between elements of
143
``groundset`` and ``range(len(groundset))``. ``BasisExchangeMatroid``
144
uses this correspondence for encoding of subsets of the groundset as
145
bitpacked sets of integers --- see ``__pack()`` and ``__unpack()``. In
146
general, methods of ``BasisExchangeMatroid`` having a name starting
147
with two underscores deal with such encoded subsets.
148
149
A second task of this initializer is to store the rank and intialize
150
the 'current' basis.
151
152
EXAMPLES::
153
154
sage: from sage.matroids.basis_exchange_matroid import BasisExchangeMatroid
155
sage: M = BasisExchangeMatroid(groundset='abcdef', basis='abc')
156
sage: sorted(M.groundset())
157
['a', 'b', 'c', 'd', 'e', 'f']
158
sage: sorted(M.basis())
159
['a', 'b', 'c']
160
"""
161
self._groundset_size = len(groundset)
162
self._bitset_size = max(self._groundset_size, 1)
163
if basis is None:
164
self._matroid_rank = rank
165
else:
166
self._matroid_rank = len(basis)
167
bitset_init(self._current_basis, self._bitset_size)
168
bitset_init(self._inside, self._bitset_size)
169
bitset_init(self._outside, self._bitset_size)
170
bitset_init(self._input, self._bitset_size)
171
bitset_init(self._input2, self._bitset_size)
172
bitset_init(self._output, self._bitset_size)
173
bitset_init(self._temp, self._bitset_size)
174
175
self._groundset = frozenset(groundset)
176
if not isinstance(groundset, tuple):
177
self._E = tuple(groundset)
178
else:
179
self._E = groundset
180
self._idx = {}
181
cdef long i
182
for i in xrange(self._groundset_size):
183
self._idx[self._E[i]] = i
184
185
if basis is not None:
186
self.__pack(self._current_basis, frozenset(basis))
187
188
def __dealloc__(self):
189
bitset_free(self._current_basis)
190
bitset_free(self._inside)
191
bitset_free(self._outside)
192
bitset_free(self._input)
193
bitset_free(self._input2)
194
bitset_free(self._output)
195
bitset_free(self._temp)
196
197
cdef __relabel(self, l):
198
"""
199
Relabel each element `e` as `l[e]`, where `l` is a given injective map.
200
201
INPUT:
202
203
- `l`, a python object such that `l[e]` is the new label of e.
204
205
OUTPUT:
206
207
``None``.
208
209
NOTE:
210
For internal use. Matroids are immutable but this method does modify the matroid. The use this method will only
211
be safe in very limited circumstances, such as perhaps on a fresh copy of a matroid.
212
213
"""
214
cdef long i
215
E = []
216
for i in range(self._groundset_size):
217
if self._E[i] in l:
218
E.append(l[self._E[i]])
219
else:
220
E.append(self._E[i])
221
self._E = tuple(E)
222
self._groundset = frozenset(E)
223
224
self._idx = {}
225
for i in xrange(self._groundset_size):
226
self._idx[self._E[i]] = i
227
228
if self._weak_partition_var:
229
self._weak_partition_var._relabel(l)
230
231
if self._strong_partition_var:
232
self._strong_partition_var._relabel(l)
233
if self._heuristic_partition_var:
234
self._heuristic_partition_var._relabel(l)
235
236
# the engine
237
cdef __pack(self, bitset_t I, F):
238
"""
239
Encode a subset F of the groundset into a bitpacked set of integers
240
"""
241
bitset_clear(I)
242
for f in F:
243
bitset_add(I, self._idx[f])
244
245
cdef __unpack(self, bitset_t I):
246
"""
247
Unencode a bitpacked set of integers to a subset of the groundset.
248
"""
249
cdef long i
250
F = set()
251
i = bitset_first(I)
252
while i >= 0:
253
F.add(self._E[i])
254
i = bitset_next(I, i + 1)
255
return frozenset(F)
256
257
# this method needs to be overridden by child class
258
cdef bint __is_exchange_pair(self, long x, long y) except BINT_EXCEPT:
259
"""
260
Test if current_basis-x + y is a basis
261
"""
262
raise NotImplementedError
263
264
# if this method is overridden by a child class, the child class needs to call this method
265
cdef bint __exchange(self, long x, long y) except BINT_EXCEPT:
266
"""
267
put current_basis <-- current_basis-x + y
268
"""
269
bitset_discard(self._current_basis, x)
270
bitset_add(self._current_basis, y)
271
272
cdef __move(self, bitset_t X, bitset_t Y):
273
"""
274
Change current_basis to minimize intersection with ``X``, maximize intersection with ``Y``.
275
"""
276
cdef long x, y
277
x = bitset_first(X)
278
while x >= 0:
279
y = bitset_first(Y)
280
while y >= 0:
281
if self.__is_exchange_pair(x, y):
282
self.__exchange(x, y)
283
bitset_discard(Y, y)
284
bitset_discard(X, x)
285
if bitset_isempty(Y):
286
return
287
break
288
else:
289
y = bitset_next(Y, y + 1)
290
x = bitset_next(X, x + 1)
291
292
cdef __fundamental_cocircuit(self, bitset_t C, long x):
293
"""
294
Return the unique cocircuit that meets ``self._current_basis`` in exactly element ``x``.
295
"""
296
cdef long y
297
bitset_clear(C)
298
bitset_complement(self._temp, self._current_basis)
299
y = bitset_first(self._temp)
300
while y >= 0:
301
if self.__is_exchange_pair(x, y):
302
bitset_add(C, y)
303
y = bitset_next(self._temp, y + 1)
304
bitset_add(C, x)
305
306
cdef __fundamental_circuit(self, bitset_t C, long y):
307
"""
308
Return the unique circuit contained in ``self._current_basis`` union ``y``.
309
"""
310
cdef long x
311
bitset_clear(C)
312
x = bitset_first(self._current_basis)
313
while x >= 0:
314
if self.__is_exchange_pair(x, y):
315
bitset_add(C, x)
316
x = bitset_next(self._current_basis, x + 1)
317
bitset_add(C, y)
318
319
cdef __max_independent(self, bitset_t R, bitset_t F):
320
"""
321
Bitpacked version of ``max_independent``.
322
"""
323
bitset_difference(self._inside, self._current_basis, F)
324
bitset_difference(self._outside, F, self._current_basis)
325
self.__move(self._inside, self._outside)
326
bitset_intersection(R, self._current_basis, F)
327
328
cdef __circuit(self, bitset_t R, bitset_t F):
329
"""
330
Bitpacked version of ``circuit``.
331
"""
332
bitset_difference(self._inside, self._current_basis, F)
333
bitset_difference(self._outside, F, self._current_basis)
334
cdef long x, y
335
y = bitset_first(self._outside)
336
if y < 0:
337
raise ValueError('no circuit in independent set.')
338
while y >= 0:
339
x = bitset_first(self._inside)
340
while x >= 0:
341
if self.__is_exchange_pair(x, y):
342
self.__exchange(x, y)
343
bitset_discard(self._outside, y)
344
bitset_discard(self._inside, x)
345
if bitset_isempty(self._outside):
346
raise ValueError('no circuit in independent set.')
347
break
348
else:
349
x = bitset_next(self._inside, x + 1)
350
if x == -1:
351
self.__fundamental_circuit(R, y)
352
return
353
y = bitset_next(self._outside, y + 1)
354
355
cdef __closure(self, bitset_t R, bitset_t F):
356
"""
357
Bitpacked version of ``closure``.
358
"""
359
bitset_difference(self._inside, self._current_basis, F)
360
bitset_difference(self._outside, F, self._current_basis)
361
self.__move(self._inside, self._outside)
362
bitset_set_first_n(R, self._groundset_size)
363
cdef long x = bitset_first(self._inside)
364
while x >= 0:
365
self.__fundamental_cocircuit(F, x)
366
bitset_difference(R, R, F)
367
x = bitset_next(self._inside, x + 1)
368
369
cdef __max_coindependent(self, bitset_t R, bitset_t F):
370
"""
371
Bitpacked version of ``max_coindependent``.
372
"""
373
bitset_complement(R, F)
374
bitset_difference(self._inside, self._current_basis, R)
375
bitset_difference(self._outside, R, self._current_basis)
376
self.__move(self._inside, self._outside)
377
bitset_difference(R, F, self._current_basis)
378
379
cdef __cocircuit(self, bitset_t R, bitset_t F):
380
"""
381
Bitpacked version of ``cocircuit``.
382
"""
383
bitset_complement(R, F)
384
bitset_difference(self._inside, self._current_basis, R)
385
bitset_difference(self._outside, R, self._current_basis)
386
cdef long x, y
387
x = bitset_first(self._inside)
388
if x < 0:
389
raise ValueError('no cocircuit in coindependent set.')
390
while x >= 0:
391
y = bitset_first(self._outside)
392
while y >= 0:
393
if self.__is_exchange_pair(x, y):
394
self.__exchange(x, y)
395
bitset_discard(self._outside, y)
396
bitset_discard(self._inside, x)
397
if bitset_isempty(self._inside):
398
raise ValueError('no cocircuit in coindependent set.')
399
break
400
else:
401
y = bitset_next(self._outside, y + 1)
402
if y == -1:
403
self.__fundamental_cocircuit(R, x)
404
return
405
x = bitset_next(self._inside, x + 1)
406
407
cdef __coclosure(self, bitset_t R, bitset_t F):
408
"""
409
Bitpacked version of ``closure``.
410
"""
411
bitset_complement(R, F)
412
bitset_difference(self._inside, self._current_basis, R)
413
bitset_difference(self._outside, R, self._current_basis)
414
self.__move(self._inside, self._outside)
415
bitset_set_first_n(R, self._groundset_size)
416
cdef long y = bitset_first(self._outside)
417
while y >= 0:
418
self.__fundamental_circuit(F, y)
419
bitset_difference(R, R, F)
420
y = bitset_next(self._outside, y + 1)
421
422
cdef __augment(self, bitset_t R, bitset_t X, bitset_t Y):
423
"""
424
Bitpacked version of ``augment``.
425
"""
426
bitset_difference(self._inside, self._current_basis, X)
427
bitset_difference(self._outside, X, self._current_basis)
428
self.__move(self._inside, self._outside)
429
bitset_difference(self._inside, self._inside, Y)
430
bitset_difference(self._outside, Y, self._current_basis)
431
self.__move(self._inside, self._outside)
432
bitset_intersection(R, self._current_basis, Y)
433
434
cdef bint __is_independent(self, bitset_t F):
435
"""
436
Bitpacked version of ``is_independent``.
437
"""
438
bitset_difference(self._inside, self._current_basis, F)
439
bitset_difference(self._outside, F, self._current_basis)
440
self.__move(self._inside, self._outside)
441
return bitset_isempty(self._outside)
442
443
cdef __move_current_basis(self, bitset_t X, bitset_t Y):
444
"""
445
Bitpacked version of ``_move_current_basis``.
446
"""
447
bitset_difference(self._inside, self._current_basis, X)
448
bitset_difference(self._outside, X, self._current_basis)
449
self.__move(self._inside, self._outside)
450
bitset_intersection(self._inside, self._current_basis, Y)
451
bitset_complement(self._outside, self._current_basis)
452
bitset_difference(self._outside, self._outside, Y)
453
self.__move(self._inside, self._outside)
454
455
# functions for derived classes and for parent class
456
cdef bint _set_current_basis(self, F):
457
"""
458
Set _current_basis to subset of the groundset ``F``.
459
"""
460
self.__pack(self._input, F)
461
bitset_difference(self._inside, self._current_basis, self._input)
462
bitset_difference(self._outside, self._input, self._current_basis)
463
self.__move(self._inside, self._outside)
464
return bitset_isempty(self._outside) and bitset_isempty(self._inside)
465
466
# groundset and full_rank
467
cpdef groundset(self):
468
"""
469
Return the groundset of the matroid.
470
471
The groundset is the set of elements that comprise the matroid.
472
473
OUTPUT:
474
475
A set.
476
477
EXAMPLES::
478
479
sage: M = matroids.named_matroids.Fano()
480
sage: sorted(M.groundset())
481
['a', 'b', 'c', 'd', 'e', 'f', 'g']
482
"""
483
return self._groundset
484
485
cpdef groundset_list(self):
486
"""
487
Return a list of elements of the groundset of the matroid.
488
489
The order of the list does not change between calls.
490
491
OUTPUT:
492
493
A list.
494
495
.. SEEALSO::
496
497
:meth:`M.groundset() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.groundset>`
498
499
EXAMPLES::
500
501
sage: M = matroids.named_matroids.Fano()
502
sage: type(M.groundset())
503
<type 'frozenset'>
504
sage: type(M.groundset_list())
505
<type 'list'>
506
sage: sorted(M.groundset_list())
507
['a', 'b', 'c', 'd', 'e', 'f', 'g']
508
509
sage: E = M.groundset_list()
510
sage: E.remove('a')
511
sage: sorted(M.groundset_list())
512
['a', 'b', 'c', 'd', 'e', 'f', 'g']
513
"""
514
return list(self._E)
515
516
def __len__(self):
517
"""
518
Return the size of the groundset.
519
520
EXAMPLES::
521
522
sage: M = matroids.named_matroids.Fano()
523
sage: len(M)
524
7
525
sage: len(M.groundset())
526
7
527
528
"""
529
return self._groundset_size
530
531
cpdef full_rank(self):
532
r"""
533
Return the rank of the matroid.
534
535
The *rank* of the matroid is the size of the largest independent
536
subset of the groundset.
537
538
OUTPUT:
539
540
Integer.
541
542
EXAMPLES::
543
544
sage: M = matroids.named_matroids.Fano()
545
sage: M.full_rank()
546
3
547
sage: M.dual().full_rank()
548
4
549
"""
550
return self._matroid_rank
551
552
cpdef full_corank(self):
553
r"""
554
Return the corank of the matroid.
555
556
The *corank* of the matroid equals the rank of the dual matroid. It is
557
given by ``M.size() - M.full_rank()``.
558
559
OUTPUT:
560
561
Integer.
562
563
.. SEEALSO::
564
565
:meth:`M.dual() <sage.matroids.matroid.Matroid.dual>`,
566
:meth:`M.full_rank() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.full_rank>`
567
568
EXAMPLES::
569
570
sage: M = matroids.named_matroids.Fano()
571
sage: M.full_corank()
572
4
573
sage: M.dual().full_corank()
574
3
575
"""
576
return self._groundset_size - self._matroid_rank
577
578
# matroid oracles
579
580
cpdef basis(self):
581
r"""
582
Return an arbitrary basis of the matroid.
583
584
A *basis* is an inclusionwise maximal independent set.
585
586
.. NOTE::
587
588
The output of this method can change in between calls. It reflects
589
the internal state of the matroid. This state is updated by lots
590
of methods, including the method ``M._move_current_basis()``.
591
592
OUTPUT:
593
594
Set of elements.
595
596
EXAMPLES::
597
598
sage: M = matroids.named_matroids.Fano()
599
sage: sorted(M.basis())
600
['a', 'b', 'c']
601
sage: M.rank('cd')
602
2
603
sage: sorted(M.basis())
604
['a', 'c', 'd']
605
606
"""
607
return self.__unpack(self._current_basis)
608
609
cpdef _move_current_basis(self, X, Y):
610
"""
611
Change current basis so that intersection with X is maximized,
612
intersection with Y is minimized.
613
614
INPUT:
615
616
- ``X`` -- an object with Python's ``frozenset`` interface containing
617
a subset of ``self.groundset()``.
618
- ``Y`` -- an object with Python's ``frozenset`` interface containing
619
a subset of ``self.groundset()``.
620
621
OUTPUT:
622
623
Nothing.
624
625
EXAMPLES::
626
627
sage: from sage.matroids.advanced import *
628
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
629
sage: sorted(M.basis())
630
['a', 'b', 'c', 'e']
631
sage: M._move_current_basis('ef', 'a')
632
sage: sorted(M.basis())
633
['b', 'c', 'e', 'f']
634
635
"""
636
self.__pack(self._input, X)
637
self.__pack(self._input2, Y)
638
self.__move_current_basis(self._input, self._input2)
639
640
cpdef _max_independent(self, F):
641
"""
642
Compute a maximal independent subset.
643
644
INPUT:
645
646
- ``F`` -- An object with Python's ``frozenset`` interface containing
647
a subset of ``self.groundset()``.
648
649
OUTPUT:
650
651
A subset of ``F``.
652
653
EXAMPLES::
654
655
sage: from sage.matroids.advanced import *
656
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
657
sage: sorted(M._max_independent(set(['a', 'c', 'd', 'e', 'f'])))
658
['a', 'c', 'd', 'e']
659
660
.. NOTE::
661
662
This is an unguarded method. For the version that verifies if
663
the input is indeed a subset of the ground set,
664
see :meth:`<sage.matroids.matroid.Matroid.max_independent>`.
665
666
"""
667
self.__pack(self._input, F)
668
self.__max_independent(self._output, self._input)
669
return self.__unpack(self._output)
670
671
cpdef _rank(self, F):
672
"""
673
Compute the rank of a subset of the ground set.
674
675
INPUT:
676
677
- ``F`` -- An object with Python's ``frozenset`` interface containing
678
a subset of ``self.groundset()``.
679
680
OUTPUT:
681
682
Integer.
683
684
EXAMPLES::
685
686
sage: from sage.matroids.advanced import *
687
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
688
sage: M._rank(set(['a', 'c', 'd', 'e', 'f']))
689
4
690
691
.. NOTE::
692
693
This is an unguarded method. For the version that verifies if
694
the input is indeed a subset of the ground set,
695
see :meth:`<sage.matroids.matroid.Matroid.rank>`.
696
697
"""
698
self.__pack(self._input, F)
699
self.__max_independent(self._output, self._input)
700
return bitset_len(self._output)
701
702
cpdef _circuit(self, F):
703
"""
704
Return a minimal dependent subset.
705
706
INPUT:
707
708
- ``F`` -- An object with Python's ``frozenset`` interface containing
709
a subset of ``self.groundset()``.
710
711
OUTPUT:
712
713
A circuit contained in ``F``, if it exists. Otherwise an error is
714
raised.
715
716
EXAMPLES::
717
718
sage: from sage.matroids.advanced import *
719
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
720
sage: sorted(sage.matroids.matroid.Matroid._circuit(M,
721
....: set(['a', 'c', 'd', 'e', 'f'])))
722
['c', 'd', 'e', 'f']
723
sage: sorted(sage.matroids.matroid.Matroid._circuit(M,
724
....: set(['a', 'c', 'd'])))
725
Traceback (most recent call last):
726
...
727
ValueError: no circuit in independent set.
728
729
.. NOTE::
730
731
This is an unguarded method. For the version that verifies if
732
the input is indeed a subset of the ground set,
733
see :meth:`<sage.matroids.matroid.Matroid.circuit>`.
734
"""
735
self.__pack(self._input, F)
736
self.__circuit(self._output, self._input)
737
return self.__unpack(self._output)
738
739
cpdef _fundamental_circuit(self, B, e):
740
r"""
741
Return the `B`-fundamental circuit using `e`.
742
743
Internal version that does no input checking.
744
745
INPUT:
746
747
- ``B`` -- a basis of the matroid.
748
- ``e`` -- an element not in ``B``.
749
750
OUTPUT:
751
752
A set of elements.
753
754
EXAMPLES::
755
756
sage: M = matroids.named_matroids.P8()
757
sage: sorted(M._fundamental_circuit('abcd', 'e'))
758
['a', 'b', 'c', 'e']
759
"""
760
self.__pack(self._input, B)
761
bitset_clear(self._input2)
762
self.__move_current_basis(self._input, self._input2)
763
self.__fundamental_circuit(self._output, self._idx[e])
764
return self.__unpack(self._output)
765
766
cpdef _closure(self, F):
767
"""
768
Return the closure of a set.
769
770
INPUT:
771
772
- ``F`` -- An object with Python's ``frozenset`` interface containing
773
a subset of ``self.groundset()``.
774
775
OUTPUT:
776
777
The smallest closed set containing ``F``.
778
779
EXAMPLES::
780
781
sage: from sage.matroids.advanced import *
782
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
783
sage: sorted(M._closure(set(['a', 'b', 'c'])))
784
['a', 'b', 'c', 'd']
785
786
.. NOTE::
787
788
This is an unguarded method. For the version that verifies if the
789
input is indeed a subset of the ground set, see
790
:meth:`<sage.matroids.matroid.Matroid.closure>`.
791
792
"""
793
self.__pack(self._input, F)
794
self.__closure(self._output, self._input)
795
return self.__unpack(self._output)
796
797
cpdef _max_coindependent(self, F):
798
"""
799
Compute a maximal coindependent subset.
800
801
INPUT:
802
803
- ``F`` -- An object with Python's ``frozenset`` interface containing
804
a subset of ``self.groundset()``.
805
806
OUTPUT:
807
808
A maximal coindependent subset of ``F``.
809
810
EXAMPLES::
811
812
sage: from sage.matroids.advanced import *
813
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
814
sage: sorted(M._max_coindependent(set(['a', 'c', 'd', 'e', 'f'])))
815
['a', 'c', 'd', 'f']
816
817
.. NOTE::
818
819
This is an unguarded method. For the version that verifies if the
820
input is indeed a subset of the ground set,
821
see :meth:`<sage.matroids.matroid.Matroid.max_coindependent>`.
822
823
"""
824
self.__pack(self._input, F)
825
self.__max_coindependent(self._output, self._input)
826
return self.__unpack(self._output)
827
828
cpdef _corank(self, F):
829
"""
830
Return the corank of a set.
831
832
INPUT:
833
834
- ``F`` -- An object with Python's ``frozenset`` interface containing
835
a subset of ``self.groundset()``.
836
837
OUTPUT:
838
839
Integer, the corank of ``F``.
840
841
EXAMPLES::
842
843
sage: from sage.matroids.advanced import *
844
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
845
sage: M._corank(set(['a', 'e', 'g', 'd', 'h']))
846
4
847
848
.. NOTE::
849
850
This is an unguarded method. For the version that verifies if the
851
input is indeed a subset of the ground set,
852
see :meth:`<sage.matroids.matroid.Matroid.corank>`.
853
"""
854
self.__pack(self._input, F)
855
self.__max_coindependent(self._output, self._input)
856
return bitset_len(self._output)
857
858
cpdef _cocircuit(self, F):
859
"""
860
Return a minimal codependent subset.
861
862
INPUT:
863
864
- ``F`` -- An object with Python's ``frozenset`` interface containing
865
a subset of ``self.groundset()``.
866
867
OUTPUT:
868
869
A cocircuit contained in ``F``, if it exists. Otherwise an error is
870
raised.
871
872
EXAMPLES::
873
874
sage: from sage.matroids.advanced import *
875
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
876
sage: sorted(sage.matroids.matroid.Matroid._cocircuit(M,
877
....: set(['a', 'c', 'd', 'e', 'f'])))
878
['c', 'd', 'e', 'f']
879
sage: sorted(sage.matroids.matroid.Matroid._cocircuit(M,
880
....: set(['a', 'c', 'd'])))
881
Traceback (most recent call last):
882
...
883
ValueError: no cocircuit in coindependent set.
884
885
..NOTE::
886
887
This is an unguarded method. For the version that verifies if the
888
input is indeed a subset of the ground set,
889
see :meth:`<sage.matroids.matroid.Matroid.cocircuit>`.
890
"""
891
self.__pack(self._input, F)
892
self.__cocircuit(self._output, self._input)
893
return self.__unpack(self._output)
894
895
cpdef _fundamental_cocircuit(self, B, e):
896
r"""
897
Return the `B`-fundamental circuit using `e`.
898
899
Internal version that does no input checking.
900
901
INPUT:
902
903
- ``B`` -- a basis of the matroid.
904
- ``e`` -- an element of ``B``.
905
906
OUTPUT:
907
908
A set of elements.
909
910
EXAMPLES::
911
912
sage: M = matroids.named_matroids.P8()
913
sage: sorted(M._fundamental_cocircuit('efgh', 'e'))
914
['b', 'c', 'd', 'e']
915
"""
916
self.__pack(self._input, B)
917
bitset_clear(self._input2)
918
self.__move_current_basis(self._input, self._input2)
919
self.__fundamental_cocircuit(self._output, self._idx[e])
920
return self.__unpack(self._output)
921
922
cpdef _coclosure(self, F):
923
"""
924
Return the coclosure of a set.
925
926
INPUT:
927
928
- ``X`` -- An object with Python's ``frozenset`` interface containing
929
a subset of ``self.groundset()``.
930
931
OUTPUT:
932
933
The smallest coclosed set containing ``X``.
934
935
EXAMPLES::
936
937
sage: from sage.matroids.advanced import *
938
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
939
sage: sorted(M._coclosure(set(['a', 'b', 'c'])))
940
['a', 'b', 'c', 'd']
941
942
..NOTE::
943
944
This is an unguarded method. For the version that verifies if the
945
input is indeed a subset of the ground set,
946
see :meth:`<sage.matroids.matroid.Matroid.coclosure>`.
947
948
"""
949
self.__pack(self._input, F)
950
self.__coclosure(self._output, self._input)
951
return self.__unpack(self._output)
952
953
cpdef _augment(self, X, Y):
954
r"""
955
Return a maximal subset `I` of `Y` such that `r(X + I)=r(X) + r(I)`.
956
957
This version of ``augment`` does no type checking. In particular,
958
``Y`` is assumed to be disjoint from ``X``.
959
960
INPUT:
961
962
- ``X`` -- An object with Python's ``frozenset`` interface containing
963
a subset of ``self.groundset()``.
964
- ``Y`` -- An object with Python's ``frozenset`` interface containing
965
a subset of ``self.groundset()``, and disjoint from ``X``.
966
967
OUTPUT:
968
969
A subset `I` of ``Y`` such that `r(X + I)=r(X) + r(I)`.
970
971
EXAMPLES::
972
973
sage: from sage.matroids.advanced import *
974
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
975
sage: sorted(M._augment(set(['a']), set(['e', 'f', 'g', 'h'])))
976
['e', 'f', 'g']
977
978
"""
979
self.__pack(self._input, X)
980
self.__pack(self._input2, Y)
981
self.__augment(self._output, self._input, self._input2)
982
return self.__unpack(self._output)
983
984
cpdef _is_independent(self, F):
985
"""
986
Test if input is independent.
987
988
INPUT:
989
990
- ``F`` -- An object with Python's ``frozenset`` interface containing
991
a subset of ``self.groundset()``.
992
993
OUTPUT:
994
995
Boolean.
996
997
EXAMPLES::
998
999
sage: from sage.matroids.advanced import *
1000
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
1001
sage: M._is_independent(set(['a', 'b', 'c']))
1002
True
1003
sage: M._is_independent(set(['a', 'b', 'c', 'd']))
1004
False
1005
1006
..NOTE::
1007
1008
This is an unguarded method. For the version that verifies if
1009
the input is indeed a subset of the ground set,
1010
see :meth:`<sage.matroids.matroid.Matroid.is_independent>`.
1011
"""
1012
self.__pack(self._input, F)
1013
return self.__is_independent(self._input)
1014
1015
# enumeration
1016
1017
cpdef f_vector(self):
1018
r"""
1019
Return the `f`-vector of the matroid.
1020
1021
The `f`-*vector* is a vector `(f_0, ..., f_r)`, where `f_i` is the
1022
number of flats of rank `i`, and `r` is the rank of the matroid.
1023
1024
OUTPUT:
1025
1026
List of integers.
1027
1028
EXAMPLES::
1029
1030
sage: M = matroids.named_matroids.S8()
1031
sage: M.f_vector()
1032
[1, 8, 22, 14, 1]
1033
1034
"""
1035
cdef bitset_t *flats, *todo
1036
if self._matroid_rank == 0:
1037
return [0]
1038
flats = <bitset_t*>sage_malloc((self.full_rank() + 1) * sizeof(bitset_t))
1039
todo = <bitset_t*>sage_malloc((self.full_rank() + 1) * sizeof(bitset_t))
1040
1041
for i in xrange(self.full_rank() + 1):
1042
bitset_init(flats[i], self._bitset_size)
1043
bitset_init(todo[i], self._bitset_size)
1044
f_vec = [0 for i in xrange(self.full_rank() + 1)]
1045
i = 0
1046
bitset_clear(todo[0])
1047
self.__closure(flats[0], todo[0])
1048
bitset_complement(todo[0], flats[0])
1049
self._f_vector_rec(f_vec, flats, todo, 0, 0)
1050
for i in xrange(self.full_rank() + 1):
1051
bitset_free(flats[i])
1052
bitset_free(todo[i])
1053
sage_free(flats)
1054
sage_free(todo)
1055
return f_vec
1056
1057
cdef _f_vector_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long i):
1058
"""
1059
Recursion for the f_vector method.
1060
"""
1061
cdef long e
1062
f_vec[i] += 1
1063
e = bitset_next(todo[i], elt)
1064
while e >= 0:
1065
bitset_copy(self._input, flats[i])
1066
bitset_add(self._input, e)
1067
self.__closure(flats[i + 1], self._input)
1068
bitset_difference(todo[i], todo[i], flats[i + 1])
1069
bitset_difference(todo[i + 1], flats[i + 1], flats[i])
1070
if bitset_first(todo[i + 1]) == e:
1071
bitset_copy(todo[i + 1], todo[i])
1072
self._f_vector_rec(f_vec, flats, todo, e + 1, i + 1)
1073
e = bitset_next(todo[i], e)
1074
1075
cpdef flats(self, r):
1076
"""
1077
Return the collection of flats of the matroid of specified rank.
1078
1079
A *flat* is a closed set.
1080
1081
INPUT:
1082
1083
- ``r`` -- A natural number.
1084
1085
OUTPUT:
1086
1087
An iterable containing all flats of rank ``r``.
1088
1089
.. SEEALSO::
1090
1091
:meth:`Matroid.closure() <sage.matroids.matroid.Matroid.closure>`
1092
1093
EXAMPLES::
1094
1095
sage: M = matroids.named_matroids.S8()
1096
sage: M.f_vector()
1097
[1, 8, 22, 14, 1]
1098
sage: len(M.flats(2))
1099
22
1100
sage: len(M.flats(8))
1101
0
1102
sage: len(M.flats(4))
1103
1
1104
"""
1105
cdef bitset_t *flats, *todo
1106
if r < 0 or r > self.full_rank():
1107
return SetSystem(self._E)
1108
if r == self.full_rank():
1109
return SetSystem(self._E, subsets=[self.groundset()])
1110
flats = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))
1111
todo = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))
1112
1113
for i in xrange(r + 1):
1114
bitset_init(flats[i], self._bitset_size)
1115
bitset_init(todo[i], self._bitset_size)
1116
Rflats = SetSystem(self._E)
1117
i = 0
1118
bitset_clear(todo[0])
1119
self.__closure(flats[0], todo[0])
1120
bitset_complement(todo[0], flats[0])
1121
self._flats_rec(Rflats, r, flats, todo, 0, 0)
1122
for i in xrange(r + 1):
1123
bitset_free(flats[i])
1124
bitset_free(todo[i])
1125
sage_free(flats)
1126
sage_free(todo)
1127
return Rflats
1128
1129
cdef _flats_rec(self, SetSystem Rflats, long R, bitset_t* flats, bitset_t* todo, long elt, long i):
1130
"""
1131
Recursion for the ``flats`` method.
1132
"""
1133
cdef long e
1134
if i == R:
1135
Rflats._append(flats[i])
1136
return
1137
e = bitset_next(todo[i], elt)
1138
while e >= 0:
1139
bitset_copy(self._input, flats[i]) # I fear that self._input is dangerous in a parallel computing environment. --SvZ
1140
bitset_add(self._input, e) # It absolutely is! --RP
1141
self.__closure(flats[i + 1], self._input)
1142
bitset_difference(todo[i], todo[i], flats[i + 1])
1143
bitset_difference(todo[i + 1], flats[i + 1], flats[i])
1144
if bitset_first(todo[i + 1]) == e:
1145
bitset_copy(todo[i + 1], todo[i])
1146
self._flats_rec(Rflats, R, flats, todo, e + 1, i + 1)
1147
e = bitset_next(todo[i], e)
1148
1149
cpdef coflats(self, r):
1150
"""
1151
Return the collection of coflats of the matroid of specified corank.
1152
1153
A *coflat* is a coclosed set.
1154
1155
INPUT:
1156
1157
- ``r`` -- A natural number.
1158
1159
OUTPUT:
1160
1161
An iterable containing all coflats of corank ``r``.
1162
1163
.. SEEALSO::
1164
1165
:meth:`Matroid.coclosure() <sage.matroids.matroid.Matroid.coclosure>`
1166
1167
EXAMPLES::
1168
1169
sage: M = matroids.named_matroids.S8().dual()
1170
sage: M.f_vector()
1171
[1, 8, 22, 14, 1]
1172
sage: len(M.coflats(2))
1173
22
1174
sage: len(M.coflats(8))
1175
0
1176
sage: len(M.coflats(4))
1177
1
1178
"""
1179
cdef bitset_t *coflats, *todo
1180
if r < 0 or r > self.full_corank():
1181
return SetSystem(self._E)
1182
if r == self.full_corank():
1183
return SetSystem(self._E, subsets=[self.groundset()])
1184
coflats = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))
1185
todo = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))
1186
1187
for i in xrange(r + 1):
1188
bitset_init(coflats[i], self._bitset_size)
1189
bitset_init(todo[i], self._bitset_size)
1190
Rcoflats = SetSystem(self._E)
1191
i = 0
1192
bitset_clear(todo[0])
1193
self.__coclosure(coflats[0], todo[0])
1194
bitset_complement(todo[0], coflats[0])
1195
self._coflats_rec(Rcoflats, r, coflats, todo, 0, 0)
1196
for i in xrange(r + 1):
1197
bitset_free(coflats[i])
1198
bitset_free(todo[i])
1199
sage_free(coflats)
1200
sage_free(todo)
1201
return Rcoflats
1202
1203
cdef _coflats_rec(self, SetSystem Rcoflats, long R, bitset_t* coflats, bitset_t* todo, long elt, long i):
1204
"""
1205
Recursion for the ``coflats`` method.
1206
"""
1207
cdef long e
1208
if i == R:
1209
Rcoflats._append(coflats[i])
1210
return
1211
e = bitset_next(todo[i], elt)
1212
while e >= 0:
1213
bitset_copy(self._input, coflats[i])
1214
bitset_add(self._input, e)
1215
self.__coclosure(coflats[i + 1], self._input)
1216
bitset_difference(todo[i], todo[i], coflats[i + 1])
1217
bitset_difference(todo[i + 1], coflats[i + 1], coflats[i])
1218
if bitset_first(todo[i + 1]) == e:
1219
bitset_copy(todo[i + 1], todo[i])
1220
self._coflats_rec(Rcoflats, R, coflats, todo, e + 1, i + 1)
1221
e = bitset_next(todo[i], e)
1222
1223
cdef _flat_element_inv(self, long k):
1224
"""
1225
Compute a flat-element invariant of the matroid.
1226
"""
1227
cdef bitset_t *flats, *todo
1228
flats = <bitset_t*>sage_malloc((k + 1) * sizeof(bitset_t))
1229
todo = <bitset_t*>sage_malloc((k + 1) * sizeof(bitset_t))
1230
1231
for i in xrange(k + 1):
1232
bitset_init(flats[i], self._bitset_size)
1233
bitset_init(todo[i], self._bitset_size)
1234
f_inc = [[0 for e in range(self._groundset_size + 1)] for i in xrange(k + 1)]
1235
i = 0
1236
bitset_clear(todo[0])
1237
self.__closure(flats[0], todo[0])
1238
bitset_complement(todo[0], flats[0])
1239
self._flat_element_inv_rec(f_inc, k, flats, todo, 0, 0)
1240
for i in xrange(k + 1):
1241
bitset_free(flats[i])
1242
bitset_free(todo[i])
1243
sage_free(flats)
1244
sage_free(todo)
1245
fie = {}
1246
for e in range(self._groundset_size):
1247
t = tuple([f_inc[i][e] for i in xrange(k + 1)])
1248
if t in fie:
1249
fie[t].add(self._E[e])
1250
else:
1251
fie[t] = set([self._E[e]])
1252
f_vec = tuple([f_inc[i][self._groundset_size] for i in xrange(k + 1)])
1253
return fie, f_vec
1254
1255
cdef _flat_element_inv_rec(self, object f_inc, long R, bitset_t* flats, bitset_t* todo, long elt, long i):
1256
"""
1257
Recursion for ``_flat_element_inv``.
1258
"""
1259
cdef long e, k
1260
k = bitset_len(flats[i])
1261
k = k * k
1262
e = bitset_first(flats[i])
1263
inc = f_inc[i]
1264
while e >= 0:
1265
inc[e] += k
1266
e = bitset_next(flats[i], e + 1)
1267
inc[self._groundset_size] += k
1268
if i == R:
1269
return
1270
e = bitset_next(todo[i], elt)
1271
while e >= 0:
1272
bitset_copy(self._input, flats[i])
1273
bitset_add(self._input, e)
1274
self.__closure(flats[i + 1], self._input)
1275
bitset_difference(todo[i], todo[i], flats[i + 1])
1276
bitset_difference(todo[i + 1], flats[i + 1], flats[i])
1277
if bitset_first(todo[i + 1]) == e:
1278
bitset_copy(todo[i + 1], todo[i])
1279
self._flat_element_inv_rec(f_inc, R, flats, todo, e + 1, i + 1)
1280
e = bitset_next(todo[i], e)
1281
1282
cpdef bases_count(self):
1283
"""
1284
Return the number of bases of the matroid.
1285
1286
A *basis* is an inclusionwise maximal independent set.
1287
1288
.. SEEALSO::
1289
1290
:meth:`M.basis() <sage.matroids.matroid.Matroid.basis>`.
1291
1292
OUTPUT:
1293
1294
Integer.
1295
1296
EXAMPLES::
1297
1298
sage: M = matroids.named_matroids.N1()
1299
sage: M.bases_count()
1300
184
1301
"""
1302
if self._bcount is not None:
1303
return self._bcount
1304
cdef long res = 0
1305
bitset_clear(self._input)
1306
bitset_set_first_n(self._input, self._matroid_rank)
1307
repeat = True
1308
while repeat:
1309
if self.__is_independent(self._input):
1310
res += 1
1311
repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)
1312
self._bcount = res
1313
return self._bcount
1314
1315
cpdef independent_r_sets(self, long r):
1316
"""
1317
Return the list of size-``r`` independent subsets of the matroid.
1318
1319
INPUT:
1320
1321
- ``r`` -- a nonnegative integer.
1322
1323
OUTPUT:
1324
1325
An iterable containing all independent subsets of the matroid of
1326
cardinality ``r``.
1327
1328
EXAMPLES::
1329
1330
sage: M = matroids.named_matroids.N1()
1331
sage: M.bases_count()
1332
184
1333
sage: [len(M.independent_r_sets(r)) for r in range(M.full_rank() + 1)]
1334
[1, 10, 45, 120, 201, 184]
1335
1336
"""
1337
cdef SetSystem BB
1338
BB = SetSystem(self._E)
1339
if r < 0:
1340
return BB
1341
bitset_clear(self._input)
1342
bitset_set_first_n(self._input, r)
1343
repeat = True
1344
while repeat:
1345
if self.__is_independent(self._input):
1346
BB._append(self._input)
1347
repeat = nxksrd(self._input, self._groundset_size, r, True)
1348
return BB
1349
1350
cpdef bases(self):
1351
"""
1352
Return the list of bases of the matroid.
1353
1354
A *basis* is a maximal independent set.
1355
1356
OUTPUT:
1357
1358
An iterable containing all bases of the matroid.
1359
1360
EXAMPLES::
1361
1362
sage: M = matroids.named_matroids.N1()
1363
sage: M.bases_count()
1364
184
1365
sage: len([B for B in M.bases()])
1366
184
1367
"""
1368
return self.independent_r_sets(self.full_rank())
1369
1370
cpdef dependent_r_sets(self, long r):
1371
"""
1372
Return the list of dependent subsets of fixed size.
1373
1374
INPUT:
1375
1376
- ``r`` -- a nonnegative integer.
1377
1378
OUTPUT:
1379
1380
An iterable containing all dependent subsets of size ``r``.
1381
1382
EXAMPLES::
1383
1384
sage: M = matroids.named_matroids.N1()
1385
sage: len(M.nonbases())
1386
68
1387
sage: [len(M.dependent_r_sets(r)) for r in range(M.full_rank() + 1)]
1388
[0, 0, 0, 0, 9, 68]
1389
1390
"""
1391
cdef SetSystem NB
1392
NB = SetSystem(self._E)
1393
if r < 0:
1394
return NB
1395
bitset_clear(self._input)
1396
bitset_set_first_n(self._input, r)
1397
repeat = True
1398
while repeat:
1399
if not self.__is_independent(self._input):
1400
NB._append(self._input)
1401
repeat = nxksrd(self._input, self._groundset_size, r, True)
1402
NB.resize()
1403
return NB
1404
1405
cpdef nonbases(self):
1406
"""
1407
Return the list of nonbases of the matroid.
1408
1409
A *nonbasis* is a set with cardinality ``self.full_rank()`` that is
1410
not a basis.
1411
1412
OUTPUT:
1413
1414
An iterable containing the nonbases of the matroid.
1415
1416
.. SEEALSO::
1417
1418
:meth:`Matroid.basis() <sage.matroids.matroid.Matroid.basis>`
1419
1420
EXAMPLES::
1421
1422
sage: M = matroids.named_matroids.N1()
1423
sage: binomial(M.size(), M.full_rank())-M.bases_count()
1424
68
1425
sage: len([B for B in M.nonbases()])
1426
68
1427
"""
1428
return self.dependent_r_sets(self.full_rank())
1429
1430
cpdef nonspanning_circuits(self):
1431
"""
1432
Return the list of nonspanning circuits of the matroid.
1433
1434
A *nonspanning circuit* is a circuit whose rank is strictly smaller
1435
than the rank of the matroid.
1436
1437
OUTPUT:
1438
1439
An iterable containing all nonspanning circuits.
1440
1441
.. SEEALSO::
1442
1443
:meth:`Matroid.circuit() <sage.matroids.matroid.Matroid.circuit>`,
1444
:meth:`Matroid.rank() <sage.matroids.matroid.Matroid.rank>`
1445
1446
EXAMPLES::
1447
1448
sage: M = matroids.named_matroids.N1()
1449
sage: len(M.nonspanning_circuits())
1450
23
1451
"""
1452
cdef SetSystem NSC
1453
NSC = SetSystem(self._E)
1454
bitset_clear(self._input)
1455
bitset_set_first_n(self._input, self._matroid_rank)
1456
cdef long e, f
1457
repeat = True
1458
while repeat:
1459
if self.__is_independent(self._input):
1460
bitset_complement(self._input2, self._current_basis)
1461
e = bitset_first(self._current_basis)
1462
while e >= 0:
1463
self.__fundamental_cocircuit(self._output, e)
1464
if e > bitset_first(self._output):
1465
bitset_intersection(self._input2, self._input2, self._output)
1466
e = bitset_next(self._current_basis, e + 1)
1467
f = bitset_first(self._input2)
1468
while f >= 0:
1469
self.__fundamental_circuit(self._output, f)
1470
if f == bitset_first(self._output) and bitset_len(self._output) <= self._matroid_rank:
1471
NSC._append(self._output)
1472
f = bitset_next(self._input2, f + 1)
1473
repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)
1474
NSC.resize()
1475
return NSC
1476
1477
cpdef noncospanning_cocircuits(self):
1478
"""
1479
Return the list of noncospanning cocircuits of the matroid.
1480
1481
A *noncospanning cocircuit* is a cocircuit whose corank is strictly
1482
smaller than the corank of the matroid.
1483
1484
OUTPUT:
1485
1486
An iterable containing all nonspanning circuits.
1487
1488
.. SEEALSO::
1489
1490
:meth:`Matroid.cocircuit() <sage.matroids.matroid.Matroid.cocircuit>`,
1491
:meth:`Matroid.corank() <sage.matroids.matroid.Matroid.corank>`
1492
1493
EXAMPLES::
1494
1495
sage: M = matroids.named_matroids.N1()
1496
sage: len(M.noncospanning_cocircuits())
1497
23
1498
"""
1499
cdef SetSystem NSC
1500
NSC = SetSystem(self._E)
1501
bitset_clear(self._input)
1502
bitset_set_first_n(self._input, self._matroid_rank)
1503
cdef long e, f, corank
1504
corank = self._groundset_size - self._matroid_rank
1505
repeat = True
1506
while repeat:
1507
if self.__is_independent(self._input):
1508
bitset_copy(self._input2, self._current_basis)
1509
e = bitset_first(self._current_basis)
1510
for e in xrange(self._groundset_size):
1511
if not bitset_in(self._current_basis, e):
1512
self.__fundamental_circuit(self._output, e)
1513
if e > bitset_first(self._output):
1514
bitset_intersection(self._input2, self._input2, self._output)
1515
f = bitset_first(self._input2)
1516
while f >= 0:
1517
self.__fundamental_cocircuit(self._output, f)
1518
if f == bitset_first(self._output) and bitset_len(self._output) <= corank:
1519
NSC._append(self._output)
1520
f = bitset_next(self._input2, f + 1)
1521
repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)
1522
NSC.resize()
1523
return NSC
1524
1525
cpdef cocircuits(self):
1526
"""
1527
Return the list of cocircuits of the matroid.
1528
1529
OUTPUT:
1530
1531
An iterable containing all cocircuits.
1532
1533
.. SEEALSO::
1534
1535
:meth:`Matroid.cocircuit() <sage.matroids.matroid.Matroid.cocircuit>`
1536
1537
EXAMPLES::
1538
1539
sage: M = Matroid(bases=matroids.named_matroids.NonFano().bases())
1540
sage: sorted([sorted(C) for C in M.cocircuits()])
1541
[['a', 'b', 'c', 'd', 'g'], ['a', 'b', 'c', 'e', 'g'],
1542
['a', 'b', 'c', 'f', 'g'], ['a', 'b', 'd', 'e'],
1543
['a', 'c', 'd', 'f'], ['a', 'e', 'f', 'g'], ['b', 'c', 'e', 'f'],
1544
['b', 'd', 'f', 'g'], ['c', 'd', 'e', 'g']]
1545
"""
1546
1547
cdef SetSystem NSC
1548
NSC = SetSystem(self._E)
1549
bitset_clear(self._input)
1550
bitset_set_first_n(self._input, self._matroid_rank)
1551
cdef long e, f, corank
1552
corank = self._groundset_size - self._matroid_rank
1553
repeat = True
1554
while repeat:
1555
if self.__is_independent(self._input):
1556
bitset_copy(self._input2, self._current_basis)
1557
e = bitset_first(self._current_basis)
1558
for e in xrange(self._groundset_size):
1559
if not bitset_in(self._current_basis, e):
1560
self.__fundamental_circuit(self._output, e)
1561
if e > bitset_first(self._output):
1562
bitset_intersection(self._input2, self._input2, self._output)
1563
f = bitset_first(self._input2)
1564
while f >= 0:
1565
self.__fundamental_cocircuit(self._output, f)
1566
if f == bitset_first(self._output):
1567
NSC._append(self._output)
1568
f = bitset_next(self._input2, f + 1)
1569
repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)
1570
NSC.resize()
1571
return NSC
1572
1573
cpdef circuits(self):
1574
"""
1575
Return the list of circuits of the matroid.
1576
1577
OUTPUT:
1578
1579
An iterable containing all circuits.
1580
1581
.. SEEALSO::
1582
1583
:meth:`M.circuit() <sage.matroids.matroid.Matroid.circuit>`
1584
1585
EXAMPLES::
1586
1587
sage: M = Matroid(matroids.named_matroids.NonFano().bases())
1588
sage: sorted([sorted(C) for C in M.circuits()])
1589
[['a', 'b', 'c', 'g'], ['a', 'b', 'd', 'e'], ['a', 'b', 'f'],
1590
['a', 'c', 'd', 'f'], ['a', 'c', 'e'], ['a', 'd', 'e', 'f'],
1591
['a', 'd', 'g'], ['a', 'e', 'f', 'g'], ['b', 'c', 'd'],
1592
['b', 'c', 'e', 'f'], ['b', 'd', 'e', 'f'], ['b', 'd', 'f', 'g'],
1593
['b', 'e', 'g'], ['c', 'd', 'e', 'f'], ['c', 'd', 'e', 'g'],
1594
['c', 'f', 'g'], ['d', 'e', 'f', 'g']]
1595
"""
1596
cdef SetSystem NSC
1597
NSC = SetSystem(self._E)
1598
bitset_clear(self._input)
1599
bitset_set_first_n(self._input, self._matroid_rank)
1600
cdef long e, f
1601
repeat = True
1602
while repeat:
1603
if self.__is_independent(self._input):
1604
bitset_complement(self._input2, self._current_basis)
1605
e = bitset_first(self._current_basis)
1606
while e >= 0:
1607
self.__fundamental_cocircuit(self._output, e)
1608
if e > bitset_first(self._output):
1609
bitset_intersection(self._input2, self._input2, self._output)
1610
e = bitset_next(self._current_basis, e + 1)
1611
f = bitset_first(self._input2)
1612
while f >= 0:
1613
self.__fundamental_circuit(self._output, f)
1614
if f == bitset_first(self._output):
1615
NSC._append(self._output)
1616
f = bitset_next(self._input2, f + 1)
1617
repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)
1618
NSC.resize()
1619
return NSC
1620
1621
# isomorphism
1622
1623
cpdef _characteristic_setsystem(self):
1624
r"""
1625
Return a characteristic set-system for this matroid, on the same
1626
ground set.
1627
1628
OUTPUT:
1629
1630
A :class:`<sage.matroids.set_system.SetSystem>` instance.
1631
1632
EXAMPLES::
1633
1634
sage: M = matroids.named_matroids.N1()
1635
sage: M._characteristic_setsystem()
1636
Iterator over a system of subsets
1637
sage: len(M._characteristic_setsystem())
1638
23
1639
"""
1640
if 2 * self._matroid_rank > self._groundset_size:
1641
return self.nonspanning_circuits()
1642
else:
1643
return self.noncospanning_cocircuits()
1644
1645
cpdef _weak_invariant(self):
1646
"""
1647
Return an isomophism invariant of the matroid.
1648
1649
Compared to BasisExchangeMatroid._strong_invariant() this invariant
1650
distinguishes less frequently between nonisomorphic matroids but takes
1651
less time to compute. See also
1652
:meth:`<BasisExchangeMatroid._weak_partition>`.
1653
1654
OUTPUT:
1655
1656
An integer isomorphism invariant.
1657
1658
EXAMPLES::
1659
1660
sage: M = Matroid(bases=matroids.named_matroids.Fano().bases())
1661
sage: N = Matroid(matroids.named_matroids.NonFano().bases())
1662
sage: M._weak_invariant() == N._weak_invariant()
1663
False
1664
"""
1665
if self._weak_invariant_var is None:
1666
if self.full_rank() == 0 or self.full_corank() == 0:
1667
self._weak_invariant_var = 0
1668
self._weak_partition_var = SetSystem(self._E, [self.groundset()])
1669
else:
1670
k = min(self.full_rank() - 1, 2)
1671
fie, f_vec = self._flat_element_inv(k)
1672
self._weak_invariant_var = hash(tuple([tuple([(f, len(fie[f])) for f in sorted(fie)]), f_vec]))
1673
self._weak_partition_var = SetSystem(self._E, [fie[f] for f in sorted(fie)])
1674
return self._weak_invariant_var
1675
1676
cpdef _weak_partition(self):
1677
"""
1678
Return an ordered partition based on the incidences of elements with
1679
low-dimensional flats.
1680
1681
EXAMPLES::
1682
1683
sage: M = Matroid(matroids.named_matroids.Vamos().bases())
1684
sage: [sorted(p) for p in M._weak_partition()]
1685
[['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']]
1686
"""
1687
self._weak_invariant()
1688
return self._weak_partition_var
1689
1690
cpdef _strong_invariant(self):
1691
"""
1692
Return an isomophism invariant of the matroid.
1693
1694
Compared to BasisExchangeMatroid._weak_invariant() this invariant
1695
distinguishes more frequently between nonisomorphic matroids but takes
1696
more time to compute. See also
1697
:meth:`<BasisExchangeMatroid._strong_partition>`.
1698
1699
OUTPUT:
1700
1701
An integer isomorphism invariant.
1702
1703
EXAMPLES::
1704
1705
sage: M = Matroid(matroids.named_matroids.Fano().bases())
1706
sage: N = Matroid(matroids.named_matroids.NonFano().bases())
1707
sage: M._strong_invariant() == N._strong_invariant()
1708
False
1709
"""
1710
if self._strong_invariant_var is None:
1711
CP = self._characteristic_setsystem()._equitable_partition(self._weak_partition())
1712
self._strong_partition_var = CP[0]
1713
self._strong_invariant_var = CP[2]
1714
return self._strong_invariant_var
1715
1716
cpdef _strong_partition(self):
1717
"""
1718
Return an equitable partition which refines _weak_partition().
1719
1720
EXAMPLES::
1721
1722
sage: from sage.matroids.advanced import *
1723
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
1724
sage: [sorted(p) for p in M._strong_partition()]
1725
[['a', 'b', 'e', 'f'], ['c', 'd', 'g', 'h']]
1726
"""
1727
self._strong_invariant()
1728
return self._strong_partition_var
1729
1730
cpdef _heuristic_invariant(self):
1731
"""
1732
Return a number characteristic for the construction of
1733
_heuristic_partition().
1734
1735
EXAMPLES::
1736
1737
sage: from sage.matroids.advanced import *
1738
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
1739
sage: N = BasisMatroid(matroids.named_matroids.Vamos())
1740
sage: M._heuristic_invariant() == N._heuristic_invariant()
1741
True
1742
"""
1743
if self._heuristic_invariant_var is None:
1744
CP = self._characteristic_setsystem()._heuristic_partition(self._strong_partition())
1745
self._heuristic_partition_var = CP[0]
1746
self._heuristic_invariant_var = CP[2]
1747
return self._heuristic_invariant_var
1748
1749
cpdef _heuristic_partition(self):
1750
"""
1751
Return an ordered partition into singletons which refines an equitable
1752
partition of the matroid.
1753
1754
The purpose of this partition is to heuristically find an isomorphism
1755
between two matroids, by lining up their respective
1756
heuristic_partitions.
1757
1758
EXAMPLES::
1759
1760
sage: from sage.matroids.advanced import *
1761
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
1762
sage: N = BasisMatroid(matroids.named_matroids.Vamos())
1763
sage: PM = M._heuristic_partition()
1764
sage: PN = N._heuristic_partition()
1765
sage: morphism = {}
1766
sage: for i in xrange(len(M)): morphism[min(PM[i])]=min(PN[i])
1767
sage: M._is_isomorphism(N, morphism)
1768
True
1769
"""
1770
self._heuristic_invariant()
1771
return self._heuristic_partition_var
1772
1773
cdef _flush(self):
1774
"""
1775
Delete all invariants.
1776
"""
1777
self._weak_invariant_var = None
1778
self._strong_invariant_var = None
1779
self._heuristic_invariant_var = None
1780
1781
cpdef _equitable_partition(self, P=None):
1782
"""
1783
Return the equitable refinement of a given ordered partition.
1784
1785
The coarsest equitable partition of the ground set of this matroid
1786
that refines P.
1787
1788
INPUT:
1789
1790
- ``P`` -- (default: ``None``) an ordered partition of the groundset.
1791
If ``None``, the trivial partition is used.
1792
1793
OUTPUT:
1794
1795
A SetSystem.
1796
1797
EXAMPLES::
1798
1799
sage: from sage.matroids.advanced import *
1800
sage: M = BasisMatroid(matroids.named_matroids.Vamos())
1801
sage: [sorted(p) for p in M._equitable_partition()]
1802
[['a', 'b', 'e', 'f'], ['c', 'd', 'g', 'h']]
1803
sage: [sorted(p) for p in M._equitable_partition(['a', 'bcdefgh'])]
1804
[['a'], ['b'], ['e', 'f'], ['c', 'd', 'g', 'h']]
1805
"""
1806
if P is not None:
1807
EQ = self._characteristic_setsystem()._equitable_partition(SetSystem(self._E, P))
1808
else:
1809
EQ = self._characteristic_setsystem()._equitable_partition()
1810
return EQ[0]
1811
1812
cpdef _is_isomorphism(self, other, morphism):
1813
"""
1814
Version of is_isomorphism() that does no type checking.
1815
1816
INPUT:
1817
1818
- ``other`` -- A matroid instance.
1819
- ``morphism`` -- a dictionary mapping the groundset of ``self`` to
1820
the groundset of ``other``
1821
1822
OUTPUT:
1823
1824
Boolean.
1825
1826
EXAMPLES::
1827
1828
sage: from sage.matroids.advanced import *
1829
sage: M = matroids.named_matroids.Pappus()
1830
sage: N = BasisMatroid(matroids.named_matroids.NonPappus())
1831
sage: N._is_isomorphism(M, {e:e for e in M.groundset()})
1832
False
1833
1834
sage: M = matroids.named_matroids.Fano() \ ['g']
1835
sage: N = matroids.Wheel(3)
1836
sage: morphism = {'a':0, 'b':1, 'c': 2, 'd':4, 'e':5, 'f':3}
1837
sage: M._is_isomorphism(N, morphism)
1838
True
1839
"""
1840
if not isinstance(other, BasisExchangeMatroid):
1841
import basis_matroid
1842
ot = basis_matroid.BasisMatroid(other)
1843
else:
1844
ot = other
1845
return self.__is_isomorphism(ot, morphism)
1846
1847
cdef bint __is_isomorphism(self, BasisExchangeMatroid other, morphism):
1848
"""
1849
Bitpacked version of ``is_isomorphism``.
1850
"""
1851
cdef long i
1852
morph = [other._idx[morphism[self._E[i]]] for i in xrange(len(self))]
1853
bitset_clear(self._input)
1854
bitset_set_first_n(self._input, self._matroid_rank)
1855
repeat = True
1856
while repeat:
1857
bitset_clear(other._input)
1858
i = bitset_first(self._input)
1859
while i != -1:
1860
bitset_add(other._input, morph[i])
1861
i = bitset_next(self._input, i + 1)
1862
if self.__is_independent(self._input) != other.__is_independent(other._input):
1863
return False
1864
repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)
1865
return True
1866
1867
cpdef _is_isomorphic(self, other):
1868
"""
1869
Test if ``self`` is isomorphic to ``other``.
1870
1871
Internal version that performs no checks on input.
1872
1873
INPUT:
1874
1875
- ``other`` -- A matroid.
1876
1877
OUTPUT:
1878
1879
Boolean.
1880
1881
EXAMPLES::
1882
1883
sage: from sage.matroids.advanced import *
1884
sage: M1 = BasisMatroid(matroids.Wheel(3))
1885
sage: M2 = BasisMatroid(matroids.CompleteGraphic(4))
1886
sage: M1._is_isomorphic(M2)
1887
True
1888
sage: M1 = BasisMatroid(matroids.named_matroids.Fano())
1889
sage: M2 = BasisMatroid(matroids.named_matroids.NonFano())
1890
sage: M1._is_isomorphic(M2)
1891
False
1892
1893
"""
1894
if not isinstance(other, BasisExchangeMatroid):
1895
return other._is_isomorphic(self)
1896
# Either generic test, which converts other to BasisMatroid,
1897
# or overridden method.
1898
if self is other:
1899
return True
1900
if len(self) != len(other):
1901
return False
1902
if self.full_rank() != other.full_rank():
1903
return False
1904
if self.full_rank() == 0 or self.full_corank() == 0:
1905
return True
1906
if self.full_rank() == 1:
1907
return len(self.loops()) == len(other.loops())
1908
if self.full_corank() == 1:
1909
return len(self.coloops()) == len(other.coloops())
1910
1911
if self._weak_invariant() != other._weak_invariant():
1912
return False
1913
PS = self._weak_partition()
1914
PO = other._weak_partition()
1915
if len(PS) == len(self) and len(PO) == len(other):
1916
morphism = {}
1917
for i in xrange(len(self)):
1918
morphism[min(PS[i])] = min(PO[i])
1919
return self.__is_isomorphism(other, morphism)
1920
1921
if self._strong_invariant() != other._strong_invariant():
1922
return False
1923
PS = self._strong_partition()
1924
PO = other._strong_partition()
1925
if len(PS) == len(self) and len(PO) == len(other):
1926
morphism = {}
1927
for i in xrange(len(self)):
1928
morphism[min(PS[i])] = min(PO[i])
1929
return self.__is_isomorphism(other, morphism)
1930
1931
if self._heuristic_invariant() == other._heuristic_invariant():
1932
PHS = self._heuristic_partition()
1933
PHO = other._heuristic_partition()
1934
morphism = {}
1935
for i in xrange(len(self)):
1936
morphism[min(PHS[i])] = min(PHO[i])
1937
if self._is_isomorphism(other, morphism):
1938
return True
1939
1940
return self._characteristic_setsystem()._isomorphism(other._characteristic_setsystem(), PS, PO) is not None
1941
1942
cpdef is_valid(self):
1943
r"""
1944
Test if the data obey the matroid axioms.
1945
1946
This method checks the basis axioms for the class. If `B` is the set
1947
of bases of a matroid `M`, then
1948
1949
* `B` is nonempty
1950
* if `X` and `Y` are in `B`, and `x` is in `X - Y`, then there is a
1951
`y` in `Y - X` such that `(X - x) + y` is again a member of `B`.
1952
1953
OUTPUT:
1954
1955
Boolean.
1956
1957
EXAMPLES::
1958
1959
sage: from sage.matroids.advanced import *
1960
sage: M = BasisMatroid(matroids.named_matroids.Fano())
1961
sage: M.is_valid()
1962
True
1963
sage: M = Matroid(groundset='abcd', bases=['ab', 'cd'])
1964
sage: M.is_valid()
1965
False
1966
"""
1967
cdef long pointerX, pointerY, x, y, ln
1968
cdef bint foundpair
1969
cdef SetSystem BB
1970
BB = self.bases()
1971
pointerX = 0
1972
ln = len(BB)
1973
while pointerX < ln: # for X in BB
1974
pointerY = 0
1975
while pointerY < ln: # for Y in BB
1976
# Set current basis to Y
1977
bitset_difference(self._inside, self._current_basis, BB._subsets[pointerY])
1978
bitset_difference(self._outside, BB._subsets[pointerY], self._current_basis)
1979
self.__move(self._inside, self._outside)
1980
bitset_difference(self._input, BB._subsets[pointerX], BB._subsets[pointerY])
1981
bitset_difference(self._input2, BB._subsets[pointerY], BB._subsets[pointerX])
1982
x = bitset_first(self._input)
1983
while x >= 0: # for x in X-Y
1984
foundpair = False
1985
y = bitset_first(self._input2)
1986
while y >= 0: # for y in Y-X
1987
if self.__is_exchange_pair(y, x):
1988
foundpair = True
1989
y = -1
1990
else:
1991
y = bitset_next(self._input2, y + 1)
1992
if not foundpair:
1993
return False
1994
x = bitset_next(self._input, x + 1)
1995
pointerY += 1
1996
pointerX += 1
1997
return True
1998
1999
2000
cdef bint nxksrd(bitset_s* b, long n, long k, bint succ):
2001
"""
2002
Next size-k subset of a size-n set in a revolving-door sequence. It will
2003
cycle through all such sets, returning each set exactly once. Each
2004
successive set differs from the last in exactly one element.
2005
2006
Returns ``True`` if there is a next set, ``False`` otherwise.
2007
"""
2008
# next k-subset of n-set in a revolving-door sequence
2009
if n == k or k == 0:
2010
return False
2011
if bitset_in(b, n - 1):
2012
if nxksrd(b, n - 1, k - 1, not succ):
2013
return True
2014
else:
2015
if succ:
2016
return False
2017
else:
2018
if k == 1:
2019
bitset_add(b, n - 2)
2020
else:
2021
bitset_add(b, k - 2)
2022
bitset_discard(b, n - 1)
2023
return True
2024
else:
2025
if nxksrd(b, n - 1, k, succ):
2026
return True
2027
else:
2028
if succ:
2029
if k == 1:
2030
bitset_discard(b, n - 2)
2031
else:
2032
bitset_discard(b, k - 2)
2033
bitset_add(b, n - 1)
2034
return True
2035
else:
2036
return False
2037
2038