Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matroids/constructor.py
8817 views
1
r"""
2
Matroid construction
3
4
Theory
5
======
6
7
Matroids are combinatorial structures that capture the abstract properties
8
of (linear/algebraic/...) dependence. Formally, a matroid is a pair
9
`M = (E, I)` of a finite set `E`, the *groundset*, and a collection of
10
subsets `I`, the independent sets, subject to the following axioms:
11
12
* `I` contains the empty set
13
* If `X` is a set in `I`, then each subset of `X` is in `I`
14
* If two subsets `X`, `Y` are in `I`, and `|X| > |Y|`, then there exists
15
`x \in X - Y` such that `Y + \{x\}` is in `I`.
16
17
See the :wikipedia:`Wikipedia article on matroids <Matroid>` for more theory
18
and examples. Matroids can be obtained from many types of mathematical
19
structures, and Sage supports a number of them.
20
21
There are two main entry points to Sage's matroid functionality. The object
22
:class:`matroids. <sage.matroids.matroids_catalog>` contains a number of
23
constructors for well-known matroids. The function
24
:func:`Matroid() <sage.matroids.constructor.Matroid>` allows you to define
25
your own matroids from a variety of sources. We briefly introduce both below;
26
follow the links for more comprehensive documentation.
27
28
Each matroid object in Sage comes with a number of built-in operations. An
29
overview can be found in the documentation of
30
:mod:`the abstract matroid class <sage.matroids.matroid>`.
31
32
Built-in matroids
33
=================
34
35
For built-in matroids, do the following:
36
37
* Within a Sage session, type ``matroids.`` (Do not press "Enter", and do not
38
forget the final period ".")
39
* Hit "tab".
40
41
You will see a list of methods which will construct matroids. For example::
42
43
sage: M = matroids.Wheel(4)
44
sage: M.is_connected()
45
True
46
47
or::
48
49
sage: U36 = matroids.Uniform(3, 6)
50
sage: U36.equals(U36.dual())
51
True
52
53
A number of special matroids are collected under a ``named_matroids`` submenu.
54
To see which, type ``matroids.named_matroids.<tab>`` as above::
55
56
sage: F7 = matroids.named_matroids.Fano()
57
sage: len(F7.nonspanning_circuits())
58
7
59
60
Constructing matroids
61
=====================
62
63
To define your own matroid, use the function
64
:func:`Matroid() <sage.matroids.constructor.Matroid>`. This function attempts
65
to interpret its arguments to create an appropriate matroid. The input
66
arguments are documented in detail
67
:func:`below <sage.matroids.constructor.Matroid>`.
68
69
EXAMPLES::
70
71
sage: A = Matrix(GF(2), [[1, 0, 0, 0, 1, 1, 1],
72
....: [0, 1, 0, 1, 0, 1, 1],
73
....: [0, 0, 1, 1, 1, 0, 1]])
74
sage: M = Matroid(A)
75
sage: M.is_isomorphic(matroids.named_matroids.Fano())
76
True
77
78
sage: M = Matroid(graphs.PetersenGraph())
79
sage: M.rank()
80
9
81
82
AUTHORS:
83
84
- Rudi Pendavingh, Michael Welsh, Stefan van Zwam (2013-04-01): initial
85
version
86
87
Methods
88
=======
89
"""
90
#*****************************************************************************
91
# Copyright (C) 2013 Rudi Pendavingh <[email protected]>
92
# Copyright (C) 2013 Michael Welsh <[email protected]>
93
# Copyright (C) 2013 Stefan van Zwam <[email protected]>
94
#
95
#
96
# Distributed under the terms of the GNU General Public License (GPL)
97
# as published by the Free Software Foundation; either version 2 of
98
# the License, or (at your option) any later version.
99
# http://www.gnu.org/licenses/
100
#*****************************************************************************
101
102
from itertools import combinations
103
import sage.matrix.matrix_space as matrix_space
104
from sage.matrix.constructor import Matrix
105
from sage.graphs.all import Graph, graphs
106
import sage.matrix.matrix
107
from sage.rings.all import ZZ, QQ, FiniteField, GF
108
import sage.matroids.matroid
109
import sage.matroids.basis_exchange_matroid
110
from minor_matroid import MinorMatroid
111
from dual_matroid import DualMatroid
112
from rank_matroid import RankMatroid
113
from circuit_closures_matroid import CircuitClosuresMatroid
114
from basis_matroid import BasisMatroid
115
from linear_matroid import LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid
116
import sage.matroids.utilities
117
from networkx import NetworkXError
118
119
120
def Matroid(*args, **kwds):
121
r"""
122
Construct a matroid.
123
124
Matroids are combinatorial structures that capture the abstract properties
125
of (linear/algebraic/...) dependence. Formally, a matroid is a pair
126
`M = (E, I)` of a finite set `E`, the *groundset*, and a collection of
127
subsets `I`, the independent sets, subject to the following axioms:
128
129
* `I` contains the empty set
130
* If `X` is a set in `I`, then each subset of `X` is in `I`
131
* If two subsets `X`, `Y` are in `I`, and `|X| > |Y|`, then there exists
132
`x \in X - Y` such that `Y + \{x\}` is in `I`.
133
134
See the :wikipedia:`Wikipedia article on matroids <Matroid>` for more
135
theory and examples. Matroids can be obtained from many types of
136
mathematical structures, and Sage supports a number of them.
137
138
There are two main entry points to Sage's matroid functionality. For
139
built-in matroids, do the following:
140
141
* Within a Sage session, type "matroids." (Do not press "Enter", and do
142
not forget the final period ".")
143
* Hit "tab".
144
145
You will see a list of methods which will construct matroids. For
146
example::
147
148
sage: F7 = matroids.named_matroids.Fano()
149
sage: len(F7.nonspanning_circuits())
150
7
151
152
or::
153
154
sage: U36 = matroids.Uniform(3, 6)
155
sage: U36.equals(U36.dual())
156
True
157
158
To define your own matroid, use the function ``Matroid()``.
159
This function attempts to interpret its arguments to create an appropriate
160
matroid. The following named arguments are supported:
161
162
INPUT:
163
164
- ``groundset`` -- If provided, the groundset of the matroid.
165
If not provided, the function attempts to determine a groundset from the
166
data.
167
- ``bases`` -- The list of bases (maximal independent sets) of the
168
matroid.
169
- ``independent_sets`` -- The list of independent sets of the matroid.
170
- ``circuits`` -- The list of circuits of the matroid.
171
- ``graph`` -- A graph, whose edges form the elements of the matroid.
172
- ``matrix`` -- A matrix representation of the matroid.
173
- ``reduced_matrix`` -- A reduced representation of the matroid: if
174
``reduced_matrix = A``
175
then the matroid is represented by `[I\ \ A]` where `I` is an
176
appropriately sized identity matrix.
177
- ``rank_function`` -- A function that computes the rank of each subset.
178
Can only be provided together with a groundset.
179
- ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C``
180
the closure of a circuit, and ``k`` the rank of ``C``, or a dictionary
181
``D`` with ``D[k]`` the set of closures of rank-``k`` circuits.
182
- ``matroid`` -- An object that is already a matroid. Useful only with the
183
``regular`` option.
184
185
Up to two unnamed arguments are allowed.
186
187
- One unnamed argument, no named arguments other than ``regular`` -- the
188
input should be either a graph, or a matrix, or a list of independent
189
sets containing all bases, or a matroid.
190
- Two unnamed arguments: the first is the groundset, the second a graph,
191
or a matrix, or a list of independent sets containing all bases, or a
192
matroid.
193
- One unnamed argument, at least one named argument: the unnamed argument
194
is the groundset, the named argument is as above (but must be different
195
from ``groundset``).
196
197
The examples section details how each of the input types deals with
198
explicit or implicit groundset arguments.
199
200
OPTIONS:
201
202
- ``regular`` -- (default: ``False``) boolean. If ``True``,
203
output a
204
:class:`RegularMatroid <sage.matroids.linear_matroid.RegularMatroid>`
205
instance such that, *if* the input defines a valid regular matroid, then
206
the output represents this matroid. Note that this option can be
207
combined with any type of input.
208
- ``ring`` -- any ring. If provided, and the input is a ``matrix`` or
209
``reduced_matrix``, output will be a linear matroid over the ring or
210
field ``ring``.
211
- ``field`` -- any field. Same as ``ring``, but only fields are allowed.
212
- ``check`` -- (default: ``True``) boolean. If ``True`` and
213
``regular`` is true, the output is checked to make sure it is a valid
214
regular matroid.
215
216
.. WARNING::
217
218
Except for regular matroids, the input is not checked for validity. If
219
your data does not correspond to an actual matroid, the behavior of
220
the methods is undefined and may cause strange errors. To ensure you
221
have a matroid, run
222
:meth:`M.is_valid() <sage.matroids.matroid.Matroid.is_valid>`.
223
224
.. NOTE::
225
226
The ``Matroid()`` method will return instances of type
227
:class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`,
228
:class:`CircuitClosuresMatroid <sage.matroids.circuit_closures_matroid.CircuitClosuresMatroid>`,
229
:class:`LinearMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
230
:class:`BinaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
231
:class:`TernaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
232
:class:`QuaternaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
233
:class:`RegularMatroid <sage.matroids.linear_matroid.LinearMatroid>`, or
234
:class:`RankMatroid <sage.matroids.rank_matroid.RankMatroid>`. To
235
import these classes (and other useful functions) directly into Sage's
236
main namespace, type::
237
238
sage: from sage.matroids.advanced import *
239
240
See :mod:`sage.matroids.advanced <sage.matroids.advanced>`.
241
242
EXAMPLES:
243
244
Note that in these examples we will often use the fact that strings are
245
iterable in these examples. So we type ``'abcd'`` to denote the list
246
``['a', 'b', 'c', 'd']``.
247
248
#. List of bases:
249
250
All of the following inputs are allowed, and equivalent::
251
252
sage: M1 = Matroid(groundset='abcd', bases=['ab', 'ac', 'ad',
253
....: 'bc', 'bd', 'cd'])
254
sage: M2 = Matroid(bases=['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
255
sage: M3 = Matroid(['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
256
sage: M4 = Matroid('abcd', ['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
257
sage: M5 = Matroid('abcd', bases=[['a', 'b'], ['a', 'c'],
258
....: ['a', 'd'], ['b', 'c'],
259
....: ['b', 'd'], ['c', 'd']])
260
sage: M1 == M2
261
True
262
sage: M1 == M3
263
True
264
sage: M1 == M4
265
True
266
sage: M1 == M5
267
True
268
269
We do not check if the provided input forms an actual matroid::
270
271
sage: M1 = Matroid(groundset='abcd', bases=['ab', 'cd'])
272
sage: M1.full_rank()
273
2
274
sage: M1.is_valid()
275
False
276
277
Bases may be repeated::
278
279
sage: M1 = Matroid(['ab', 'ac'])
280
sage: M2 = Matroid(['ab', 'ac', 'ab'])
281
sage: M1 == M2
282
True
283
284
#. List of independent sets:
285
286
::
287
288
sage: M1 = Matroid(groundset='abcd',
289
....: independent_sets=['', 'a', 'b', 'c', 'd', 'ab',
290
....: 'ac', 'ad', 'bc', 'bd', 'cd'])
291
292
We only require that the list of independent sets contains each basis
293
of the matroid; omissions of smaller independent sets and
294
repetitions are allowed::
295
296
sage: M1 = Matroid(bases=['ab', 'ac'])
297
sage: M2 = Matroid(independent_sets=['a', 'ab', 'b', 'ab', 'a',
298
....: 'b', 'ac'])
299
sage: M1 == M2
300
True
301
302
#. List of circuits:
303
304
::
305
306
sage: M1 = Matroid(groundset='abc', circuits=['bc'])
307
sage: M2 = Matroid(bases=['ab', 'ac'])
308
sage: M1 == M2
309
True
310
311
A matroid specified by a list of circuits gets converted to a
312
:class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`
313
internally::
314
315
sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd',
316
....: 'bcd'])
317
sage: type(M)
318
<type 'sage.matroids.basis_matroid.BasisMatroid'>
319
320
Strange things can happen if the input does not satisfy the circuit
321
axioms, and these are not always caught by the
322
:meth:`is_valid() <sage.matroids.matroid.Matroid.is_valid>` method. So
323
always check whether your input makes sense!
324
325
::
326
327
sage: M = Matroid('abcd', circuits=['ab', 'acd'])
328
sage: M.is_valid()
329
True
330
sage: [sorted(C) for C in M.circuits()]
331
[['a']]
332
333
334
335
#. Graph:
336
337
Sage has great support for graphs, see :mod:`sage.graphs.graph`.
338
339
::
340
341
sage: G = graphs.PetersenGraph()
342
sage: Matroid(G)
343
Regular matroid of rank 9 on 15 elements with 2000 bases
344
345
346
Note: if a groundset is specified, we assume it is in the same order
347
as
348
:meth:`G.edge_iterator() <sage.graphs.generic_graph.GenericGraph.edge_iterator>`
349
provides::
350
351
sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)])
352
sage: M = Matroid('abcd', G)
353
sage: M.rank(['b', 'c'])
354
1
355
356
If no groundset is provided, we attempt to use the edge labels::
357
358
sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'c')])
359
sage: M = Matroid(G)
360
sage: sorted(M.groundset())
361
['a', 'b', 'c']
362
363
If no edge labels are present and the graph is simple, we use the
364
tuples ``(i, j)`` of endpoints. If that fails, we simply use a list
365
``[0..m-1]`` ::
366
367
sage: G = Graph([(0, 1), (0, 2), (1, 2)])
368
sage: M = Matroid(G)
369
sage: sorted(M.groundset())
370
[(0, 1), (0, 2), (1, 2)]
371
372
sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)])
373
sage: M = Matroid(G)
374
sage: sorted(M.groundset())
375
[0, 1, 2, 3]
376
377
When the ``graph`` keyword is used, a variety of inputs can be
378
converted to a graph automatically. The following uses a graph6 string
379
(see the :class:`Graph <sage.graphs.graph.Graph>` method's
380
documentation)::
381
382
sage: Matroid(graph=':I`AKGsaOs`cI]Gb~')
383
Regular matroid of rank 9 on 17 elements with 4004 bases
384
385
However, this method is no more clever than ``Graph()``::
386
387
sage: Matroid(graph=41/2)
388
Traceback (most recent call last):
389
...
390
ValueError: input does not seem to represent a graph.
391
392
393
#. Matrix:
394
395
The basic input is a
396
:mod:`Sage matrix <sage.matrix.constructor>`::
397
398
sage: A = Matrix(GF(2), [[1, 0, 0, 1, 1, 0],
399
....: [0, 1, 0, 1, 0, 1],
400
....: [0, 0, 1, 0, 1, 1]])
401
sage: M = Matroid(matrix=A)
402
sage: M.is_isomorphic(matroids.CompleteGraphic(4))
403
True
404
405
Various shortcuts are possible::
406
407
sage: M1 = Matroid(matrix=[[1, 0, 0, 1, 1, 0],
408
....: [0, 1, 0, 1, 0, 1],
409
....: [0, 0, 1, 0, 1, 1]], ring=GF(2))
410
sage: M2 = Matroid(reduced_matrix=[[1, 1, 0],
411
....: [1, 0, 1],
412
....: [0, 1, 1]], ring=GF(2))
413
sage: M3 = Matroid(groundset=[0, 1, 2, 3, 4, 5],
414
....: matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
415
....: ring=GF(2))
416
sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
417
sage: M4 = Matroid([0, 1, 2, 3, 4, 5], A)
418
sage: M1 == M2
419
True
420
sage: M1 == M3
421
True
422
sage: M1 == M4
423
True
424
425
However, with unnamed arguments the input has to be a ``Matrix``
426
instance, or the function will try to interpret it as a set of bases::
427
428
sage: Matroid([0, 1, 2], [[1, 0, 1], [0, 1, 1]])
429
Traceback (most recent call last):
430
...
431
ValueError: basis has wrong cardinality.
432
433
434
If the groundset size equals number of rows plus number of columns, an
435
identity matrix is prepended. Otherwise the groundset size must equal
436
the number of columns::
437
438
sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
439
sage: M = Matroid([0, 1, 2], A)
440
sage: N = Matroid([0, 1, 2, 3, 4, 5], A)
441
sage: M.rank()
442
2
443
sage: N.rank()
444
3
445
446
We automatically create an optimized subclass, if available::
447
448
sage: Matroid([0, 1, 2, 3, 4, 5],
449
....: matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
450
....: field=GF(2))
451
Binary matroid of rank 3 on 6 elements, type (2, 7)
452
sage: Matroid([0, 1, 2, 3, 4, 5],
453
....: matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
454
....: field=GF(3))
455
Ternary matroid of rank 3 on 6 elements, type 0-
456
sage: Matroid([0, 1, 2, 3, 4, 5],
457
....: matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
458
....: field=GF(4, 'x'))
459
Quaternary matroid of rank 3 on 6 elements
460
sage: Matroid([0, 1, 2, 3, 4, 5],
461
....: matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
462
....: field=GF(2), regular=True)
463
Regular matroid of rank 3 on 6 elements with 16 bases
464
465
Otherwise the generic LinearMatroid class is used::
466
467
sage: Matroid([0, 1, 2, 3, 4, 5],
468
....: matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
469
....: field=GF(83))
470
Linear matroid of rank 3 on 6 elements represented over the Finite
471
Field of size 83
472
473
An integer matrix is automatically converted to a matrix over `\QQ`.
474
If you really want integers, you can specify the ring explicitly::
475
476
sage: A = Matrix([[1, 1, 0], [1, 0, 1], [0, 1, -1]])
477
sage: A.base_ring()
478
Integer Ring
479
sage: M = Matroid([0, 1, 2, 3, 4, 5], A)
480
sage: M.base_ring()
481
Rational Field
482
sage: M = Matroid([0, 1, 2, 3, 4, 5], A, ring=ZZ)
483
sage: M.base_ring()
484
Integer Ring
485
486
#. Rank function:
487
488
Any function mapping subsets to integers can be used as input::
489
490
sage: def f(X):
491
....: return min(len(X), 2)
492
....:
493
sage: M = Matroid('abcd', rank_function=f)
494
sage: M
495
Matroid of rank 2 on 4 elements
496
sage: M.is_isomorphic(matroids.Uniform(2, 4))
497
True
498
499
#. Circuit closures:
500
501
This is often a really concise way to specify a matroid. The usual way
502
is a dictionary of lists::
503
504
sage: M = Matroid(circuit_closures={3: ['edfg', 'acdg', 'bcfg',
505
....: 'cefh', 'afgh', 'abce', 'abdf', 'begh', 'bcdh', 'adeh'],
506
....: 4: ['abcdefgh']})
507
sage: M.equals(matroids.named_matroids.P8())
508
True
509
510
You can also input tuples `(k, X)` where `X` is the closure of a
511
circuit, and `k` the rank of `X`::
512
513
sage: M = Matroid(circuit_closures=[(2, 'abd'), (3, 'abcdef'),
514
....: (2, 'bce')])
515
sage: M.equals(matroids.named_matroids.Q6())
516
True
517
518
#. Matroid:
519
520
Most of the time, the matroid itself is returned::
521
522
sage: M = matroids.named_matroids.Fano()
523
sage: N = Matroid(M)
524
sage: N is M
525
True
526
527
But it can be useful with the ``regular`` option::
528
529
sage: M = Matroid(circuit_closures={2:['adb', 'bec', 'cfa',
530
....: 'def'], 3:['abcdef']})
531
sage: N = Matroid(M, regular=True)
532
sage: N
533
Regular matroid of rank 3 on 6 elements with 16 bases
534
sage: Matrix(N)
535
[1 0 0 1 1 0]
536
[0 1 0 1 1 1]
537
[0 0 1 0 1 1]
538
539
The ``regular`` option:
540
541
::
542
543
sage: M = Matroid(reduced_matrix=[[1, 1, 0],
544
....: [1, 0, 1],
545
....: [0, 1, 1]], regular=True)
546
sage: M
547
Regular matroid of rank 3 on 6 elements with 16 bases
548
549
sage: M.is_isomorphic(matroids.CompleteGraphic(4))
550
True
551
552
By default we check if the resulting matroid is actually regular. To
553
increase speed, this check can be skipped::
554
555
sage: M = matroids.named_matroids.Fano()
556
sage: N = Matroid(M, regular=True)
557
Traceback (most recent call last):
558
...
559
ValueError: input does not correspond to a valid regular matroid.
560
sage: N = Matroid(M, regular=True, check=False)
561
sage: N
562
Regular matroid of rank 3 on 7 elements with 32 bases
563
564
sage: N.is_valid()
565
False
566
567
Sometimes the output is regular, but represents a different matroid
568
from the one you intended::
569
570
sage: M = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]))
571
sage: N = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]),
572
....: regular=True)
573
sage: N.is_valid()
574
True
575
sage: N.is_isomorphic(M)
576
False
577
578
"""
579
# These are the valid arguments:
580
inputS = set(['groundset', 'bases', 'independent_sets', 'circuits', 'graph', 'matrix', 'reduced_matrix', 'rank_function', 'circuit_closures', 'matroid'])
581
582
# process options
583
if 'regular' in kwds:
584
want_regular = kwds['regular']
585
kwds.pop('regular')
586
else:
587
want_regular = False
588
if 'check' in kwds:
589
want_check = kwds['check']
590
kwds.pop('check')
591
else:
592
want_check = True
593
594
base_ring = None
595
have_field = False
596
if 'field' in kwds:
597
base_ring = kwds['field']
598
kwds.pop('field')
599
have_field = True
600
if 'ring' in kwds:
601
raise ValueError("only one of ring and field can be specified.")
602
try:
603
if not base_ring.is_field():
604
raise TypeError("specified ``field`` is not a field.")
605
except AttributeError:
606
raise TypeError("specified ``field`` is not a field.")
607
if 'ring' in kwds:
608
base_ring = kwds['ring']
609
kwds.pop('ring')
610
try:
611
if not base_ring.is_ring():
612
raise TypeError("specified ``ring`` is not a ring.")
613
except AttributeError:
614
raise TypeError("specified ``ring`` is not a ring.")
615
616
# Process unnamed arguments
617
if len(args) > 0:
618
if 'groundset' in kwds:
619
raise ValueError('when using unnamed arguments, groundset must be the first unnamed argument or be implicit.')
620
if len(args) > 2:
621
raise ValueError('at most two unnamed arguments are allowed.')
622
if len(args) == 2 or len(kwds) > 0:
623
# First argument should be the groundset
624
kwds['groundset'] = args[0]
625
# Check for unnamed data
626
dataindex = -1
627
if len(args) == 2:
628
dataindex = 1
629
if len(args) == 1 and len(kwds) == 0:
630
dataindex = 0
631
if dataindex > -1:
632
# One unnamed argument, no named arguments
633
if isinstance(args[dataindex], sage.graphs.graph.Graph):
634
kwds['graph'] = args[dataindex]
635
elif isinstance(args[dataindex], sage.matrix.matrix.Matrix):
636
kwds['matrix'] = args[dataindex]
637
elif isinstance(args[dataindex], sage.matroids.matroid.Matroid):
638
kwds['matroid'] = args[dataindex]
639
else:
640
kwds['independent_sets'] = args[dataindex]
641
642
# Check for multiple types of input
643
if len(set(kwds).difference(inputS)) > 0:
644
raise ValueError("unknown input argument")
645
if ('grondset' in kwds and len(kwds) != 2) or ('groundset' not in kwds and len(kwds) > 1):
646
raise ValueError("only one type of input may be specified.")
647
648
# Bases:
649
if 'bases' in kwds:
650
if 'groundset' not in kwds:
651
gs = set()
652
for B in kwds['bases']:
653
gs.update(B)
654
kwds['groundset'] = gs
655
M = BasisMatroid(groundset=kwds['groundset'], bases=kwds['bases'])
656
657
# Independent sets:
658
if 'independent_sets' in kwds:
659
# Convert to list of bases first
660
rk = -1
661
bases = []
662
for I in kwds['independent_sets']:
663
if len(I) == rk:
664
bases.append(I)
665
elif len(I) > rk:
666
bases = [I]
667
rk = len(I)
668
if 'groundset' not in kwds:
669
gs = set()
670
for B in bases:
671
gs.update(B)
672
kwds['groundset'] = gs
673
M = BasisMatroid(groundset=kwds['groundset'], bases=bases)
674
675
# Circuits:
676
if 'circuits' in kwds:
677
# Convert to list of bases first
678
# Determine groundset (note that this cannot detect coloops)
679
if 'groundset' not in kwds:
680
gs = set()
681
for C in kwds['circuits']:
682
gs.update(C)
683
kwds['groundset'] = gs
684
# determine the rank by computing a basis
685
B = set(kwds['groundset'])
686
for C in kwds['circuits']:
687
I = B.intersection(C)
688
if len(I) >= len(C):
689
B.discard(I.pop())
690
rk = len(B)
691
# Construct the basis matroid of appropriate rank. Note: slow!
692
BB = [frozenset(B) for B in combinations(kwds['groundset'], rk) if not any([frozenset(C).issubset(B) for C in kwds['circuits']])]
693
M = BasisMatroid(groundset=kwds['groundset'], bases=BB)
694
695
# Graphs:
696
if 'graph' in kwds:
697
# Construct the incidence matrix
698
# NOTE: we are not using Sage's built-in method because
699
# 1) we would need to fix the loops anyway
700
# 2) Sage will sort the columns, making it impossible to keep labels!
701
G = kwds['graph']
702
if not isinstance(G, sage.graphs.generic_graph.GenericGraph):
703
try:
704
G = Graph(G)
705
except (ValueError, TypeError, NetworkXError):
706
raise ValueError("input does not seem to represent a graph.")
707
V = G.vertices()
708
E = G.edges()
709
n = G.num_verts()
710
m = G.num_edges()
711
A = Matrix(ZZ, n, m, 0)
712
mm = 0
713
for i, j, k in G.edge_iterator():
714
A[V.index(i), mm] = -1
715
A[V.index(j), mm] += 1 # So loops get 0
716
mm += 1
717
# Decide on the groundset
718
if 'groundset' not in kwds:
719
# 1. Attempt to use edge labels.
720
sl = G.edge_labels()
721
if len(sl) == len(set(sl)):
722
kwds['groundset'] = sl
723
# 2. If simple, use vertex tuples
724
elif not G.has_multiple_edges():
725
kwds['groundset'] = [(i, j) for i, j, k in G.edge_iterator()]
726
else:
727
# 3. Use numbers
728
kwds['groundset'] = range(m)
729
M = RegularMatroid(matrix=A, groundset=kwds['groundset'])
730
want_regular = False # Save some time, since result is already regular
731
732
# Matrices:
733
if 'matrix' in kwds or 'reduced_matrix' in kwds:
734
if 'matrix' in kwds:
735
A = kwds['matrix']
736
if 'reduced_matrix' in kwds:
737
A = kwds['reduced_matrix']
738
739
# Fix the representation
740
if not isinstance(A, sage.matrix.matrix.Matrix):
741
try:
742
if base_ring is not None:
743
A = Matrix(base_ring, A)
744
else:
745
A = Matrix(A)
746
except ValueError:
747
raise ValueError("input does not seem to contain a matrix.")
748
749
# Fix the ring
750
if base_ring is not None:
751
if A.base_ring() != base_ring:
752
A = A.change_ring(base_ring)
753
elif A.base_ring() == ZZ and not want_regular: # Usually a rational matrix is intended, we presume.
754
A = A.change_ring(QQ)
755
base_ring = QQ
756
else:
757
base_ring = A.base_ring()
758
759
# Determine groundset:
760
if 'matrix' in kwds:
761
if 'groundset' in kwds:
762
if len(kwds['groundset']) == A.nrows() + A.ncols():
763
kwds['reduced_matrix'] = A
764
kwds.pop('matrix')
765
else:
766
if len(kwds['groundset']) != A.ncols():
767
raise ValueError("groundset size does not correspond to matrix size.")
768
else:
769
kwds['groundset'] = range(A.ncols())
770
if 'reduced_matrix' in kwds:
771
if 'groundset' in kwds:
772
if len(kwds['groundset']) != A.nrows() + A.ncols():
773
raise ValueError("groundset size does not correspond to matrix size.")
774
else:
775
kwds['groundset'] = range(A.nrows() + A.ncols())
776
if 'matrix' in kwds:
777
if base_ring == GF(2):
778
M = BinaryMatroid(groundset=kwds['groundset'], matrix=A)
779
elif base_ring == GF(3):
780
M = TernaryMatroid(groundset=kwds['groundset'], matrix=A)
781
elif base_ring.is_field() and base_ring.order() == 4: # GF(4) can have different generators.
782
M = QuaternaryMatroid(groundset=kwds['groundset'], matrix=A)
783
else:
784
M = LinearMatroid(groundset=kwds['groundset'], matrix=A, ring=base_ring)
785
786
if 'reduced_matrix' in kwds:
787
if A.base_ring() == GF(2):
788
M = BinaryMatroid(groundset=kwds['groundset'], reduced_matrix=A)
789
elif A.base_ring() == GF(3):
790
M = TernaryMatroid(groundset=kwds['groundset'], reduced_matrix=A)
791
elif A.base_ring().is_field() and A.base_ring().order() == 4: # GF(4) can have different generators.
792
M = QuaternaryMatroid(groundset=kwds['groundset'], reduced_matrix=A)
793
else:
794
M = LinearMatroid(groundset=kwds['groundset'], reduced_matrix=A, ring=base_ring)
795
796
# Rank functions:
797
if 'rank_function' in kwds:
798
if 'groundset' not in kwds:
799
raise ValueError('for rank functions, groundset needs to be specified.')
800
M = RankMatroid(groundset=kwds['groundset'], rank_function=kwds['rank_function'])
801
802
# Circuit closures:
803
if 'circuit_closures' in kwds:
804
if 'groundset' not in kwds:
805
E = set()
806
if isinstance(kwds['circuit_closures'], dict):
807
for X in kwds['circuit_closures'].itervalues():
808
for Y in X:
809
E.update(Y)
810
else:
811
for X in kwds['circuit_closures']:
812
E.update(X[1])
813
else:
814
E = kwds['groundset']
815
if not isinstance(kwds['circuit_closures'], dict):
816
# Convert to dictionary
817
CC = {}
818
for X in kwds['circuit_closures']:
819
if X[0] not in CC:
820
CC[X[0]] = []
821
CC[X[0]].append(X[1])
822
else:
823
CC = kwds['circuit_closures']
824
M = CircuitClosuresMatroid(groundset=E, circuit_closures=CC)
825
826
# Matroids:
827
if 'matroid' in kwds:
828
M = kwds['matroid']
829
if not isinstance(M, sage.matroids.matroid.Matroid):
830
raise ValueError("input does not appear to be of Matroid type.")
831
# Regular option:
832
if want_regular:
833
M = sage.matroids.utilities.make_regular_matroid_from_matroid(M)
834
if want_check:
835
if not M.is_valid():
836
raise ValueError('input does not correspond to a valid regular matroid.')
837
return M
838
839