passagemath-cmr 10.6.33__cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_cmr-10.6.33.dist-info/METADATA +177 -0
- passagemath_cmr-10.6.33.dist-info/RECORD +19 -0
- passagemath_cmr-10.6.33.dist-info/WHEEL +7 -0
- passagemath_cmr-10.6.33.dist-info/top_level.txt +2 -0
- passagemath_cmr.libs/libcmr-a6a18ee8.so +0 -0
- passagemath_cmr.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
- sage/all__sagemath_cmr.py +5 -0
- sage/libs/all__sagemath_cmr.py +1 -0
- sage/libs/cmr/__init__.py +1 -0
- sage/libs/cmr/cmr.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/cmr/cmr.pxd +600 -0
- sage/libs/cmr/cmr.pyx +24 -0
- sage/matrix/all__sagemath_cmr.py +1 -0
- sage/matrix/matrix_cmr_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_cmr_sparse.pxd +33 -0
- sage/matrix/matrix_cmr_sparse.pyx +5749 -0
- sage/matrix/seymour_decomposition.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/seymour_decomposition.pxd +46 -0
- sage/matrix/seymour_decomposition.pyx +4526 -0
|
@@ -0,0 +1,4526 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-cmr
|
|
2
|
+
# sage.doctest: optional - sage.libs.cmr
|
|
3
|
+
r"""
|
|
4
|
+
Seymour's decomposition of totally unimodular matrices and regular matroids
|
|
5
|
+
|
|
6
|
+
This module is provided by the pip-installable package :ref:`passagemath-cmr <spkg_sagemath_cmr>`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# ****************************************************************************
|
|
10
|
+
# Copyright (C) 2023 Javier Santillan
|
|
11
|
+
# 2023-2024 Matthias Koeppe
|
|
12
|
+
# 2023-2024 Luze Xu
|
|
13
|
+
#
|
|
14
|
+
# This program is free software: you can redistribute it and/or modify
|
|
15
|
+
# it under the terms of the GNU General Public License as published by
|
|
16
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
17
|
+
# (at your option) any later version.
|
|
18
|
+
# https://www.gnu.org/licenses/
|
|
19
|
+
# ****************************************************************************
|
|
20
|
+
|
|
21
|
+
from libc.stdint cimport SIZE_MAX
|
|
22
|
+
|
|
23
|
+
from cysignals.signals cimport sig_on, sig_off
|
|
24
|
+
|
|
25
|
+
from sage.libs.cmr.cmr cimport *
|
|
26
|
+
from sage.misc.cachefunc import cached_method
|
|
27
|
+
from sage.rings.integer cimport Integer
|
|
28
|
+
from sage.rings.integer_ring import ZZ
|
|
29
|
+
from sage.structure.sage_object cimport SageObject
|
|
30
|
+
|
|
31
|
+
from sage.matrix.constructor import Matrix
|
|
32
|
+
from sage.matrix.matrix_cmr_sparse cimport Matrix_cmr_chr_sparse, _sage_edges, _sage_graph, _set_cmr_seymour_parameters
|
|
33
|
+
from sage.matrix.matrix_cmr_sparse cimport _sage_arcs, _sage_digraph
|
|
34
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
cdef class DecompositionNode(SageObject):
|
|
38
|
+
r"""
|
|
39
|
+
Base class for nodes in Seymour's decomposition
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __cinit__(self, *args, **kwds):
|
|
43
|
+
r"""
|
|
44
|
+
Initialize the internal decomposition, a ``CMR_SEYMOUR_NODE``.
|
|
45
|
+
"""
|
|
46
|
+
self._dec = NULL
|
|
47
|
+
|
|
48
|
+
def __init__(self, matrix=None, row_keys=None, column_keys=None, base_ring=None):
|
|
49
|
+
r"""
|
|
50
|
+
Create a node in Seymour's decomposition.
|
|
51
|
+
|
|
52
|
+
INPUT:
|
|
53
|
+
|
|
54
|
+
- ``matrix`` -- the internal matrix representing the node.
|
|
55
|
+
Convert to a :class:`Matrix_cmr_chr_sparse`.
|
|
56
|
+
|
|
57
|
+
- ``row_keys`` -- a finite or enumerated family of arbitrary objects
|
|
58
|
+
that index the rows of the matrix
|
|
59
|
+
|
|
60
|
+
- ``column_keys`` -- a finite or enumerated family of arbitrary objects
|
|
61
|
+
that index the columns of the matrix
|
|
62
|
+
|
|
63
|
+
- ``base_ring`` -- the base ring of ``matrix`` representing the node.
|
|
64
|
+
For Seymour decomposition node, the base ring is `\GF{2}` or `\GF{3}` or ``ZZ``.
|
|
65
|
+
If the base ring is `\GF{2}`, the node and the matrix deal with
|
|
66
|
+
the underlying binary linear matroid;
|
|
67
|
+
if the base ring is `\GF(3)` or ``ZZ``, the node deals with
|
|
68
|
+
the matrix decomposition.
|
|
69
|
+
|
|
70
|
+
A :class:`DecompositionNode` is usually created with an internal decomposition,
|
|
71
|
+
``self._dec``, see :meth:create_DecompositionNode
|
|
72
|
+
Such decomposition comes from the certificate of the
|
|
73
|
+
totally unimodularity test, see
|
|
74
|
+
:meth:`matrix_cmr_sparse.Matrix_cmr_chr_sparse.is_totally_unimodular`
|
|
75
|
+
|
|
76
|
+
Another usage is to create a :class:`UnknownNode` from a matrix.
|
|
77
|
+
A root dummy decomposition is created before completing
|
|
78
|
+
the decomposition, see :meth:_set_root_dec, :meth:complete_decomposition
|
|
79
|
+
|
|
80
|
+
EXAMPLES::
|
|
81
|
+
|
|
82
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
83
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
84
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
85
|
+
[ 1 0]
|
|
86
|
+
[-1 1]
|
|
87
|
+
[ 0 1]
|
|
88
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
89
|
+
sage: result, certificate
|
|
90
|
+
(True, GraphicNode (3×2))
|
|
91
|
+
|
|
92
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
93
|
+
sage: node = UnknownNode([[1, 1], [0, 1]]); node
|
|
94
|
+
UnknownNode (2×2)
|
|
95
|
+
"""
|
|
96
|
+
if matrix is None:
|
|
97
|
+
self._matrix = None
|
|
98
|
+
elif isinstance(matrix, Matrix_cmr_chr_sparse):
|
|
99
|
+
self._matrix = matrix
|
|
100
|
+
else:
|
|
101
|
+
try:
|
|
102
|
+
self._matrix = matrix._matrix_cmr()
|
|
103
|
+
except (AttributeError, ImportError, TypeError):
|
|
104
|
+
if base_ring is not None:
|
|
105
|
+
matrix = Matrix(matrix, ring=base_ring)
|
|
106
|
+
else:
|
|
107
|
+
matrix = Matrix(matrix)
|
|
108
|
+
self._matrix = Matrix_cmr_chr_sparse(matrix.parent(), matrix)
|
|
109
|
+
else:
|
|
110
|
+
if row_keys is None:
|
|
111
|
+
try:
|
|
112
|
+
row_keys = matrix.codomain().basis().keys()
|
|
113
|
+
except:
|
|
114
|
+
row_keys = None
|
|
115
|
+
if column_keys is None:
|
|
116
|
+
try:
|
|
117
|
+
column_keys = matrix.domain().basis().keys()
|
|
118
|
+
except:
|
|
119
|
+
column_keys = None
|
|
120
|
+
if row_keys is not None:
|
|
121
|
+
self._set_row_keys(row_keys)
|
|
122
|
+
if column_keys is not None:
|
|
123
|
+
self._set_column_keys(column_keys)
|
|
124
|
+
if base_ring is None:
|
|
125
|
+
if self._matrix is not None:
|
|
126
|
+
base_ring = self._matrix.parent().base_ring()
|
|
127
|
+
self._base_ring = base_ring
|
|
128
|
+
|
|
129
|
+
cdef _set_dec(self, CMR_SEYMOUR_NODE *dec):
|
|
130
|
+
r"""
|
|
131
|
+
Set the decomposition ``self._dec`` to ``dec``.
|
|
132
|
+
If the value was previously set, then free it first.
|
|
133
|
+
"""
|
|
134
|
+
if self._dec != NULL:
|
|
135
|
+
# We own it, so we have to free it.
|
|
136
|
+
CMR_CALL(CMRseymourRelease(cmr, &self._dec))
|
|
137
|
+
if dec != NULL:
|
|
138
|
+
CMR_CALL(CMRseymourCapture(cmr, dec))
|
|
139
|
+
self._dec = dec
|
|
140
|
+
|
|
141
|
+
cdef _set_root_dec(self):
|
|
142
|
+
r"""
|
|
143
|
+
Set the decomposition by creating a root ``CMR_SEYMOUR_NODE``
|
|
144
|
+
based on the internal matrix representation ``self._matrix``.
|
|
145
|
+
"""
|
|
146
|
+
cdef CMR_SEYMOUR_NODE *root
|
|
147
|
+
cdef Matrix_cmr_chr_sparse matrix
|
|
148
|
+
try:
|
|
149
|
+
matrix = self.matrix()
|
|
150
|
+
except Exception:
|
|
151
|
+
raise ValueError('no Matrix_cmr_chr_sparse matrix')
|
|
152
|
+
base_ring = self.base_ring()
|
|
153
|
+
if base_ring.characteristic() not in [0, 2, 3] :
|
|
154
|
+
raise ValueError(f'only defined over binary or ternary, got {base_ring}')
|
|
155
|
+
isTernary = base_ring.characteristic() != 2
|
|
156
|
+
cdef CMR_CHRMAT *mat = matrix._mat
|
|
157
|
+
|
|
158
|
+
sig_on()
|
|
159
|
+
try:
|
|
160
|
+
CMR_CALL(CMRseymourCreate(cmr, &root, isTernary, mat, True))
|
|
161
|
+
finally:
|
|
162
|
+
sig_off()
|
|
163
|
+
self._set_dec(root)
|
|
164
|
+
|
|
165
|
+
cdef _set_row_keys(self, row_keys):
|
|
166
|
+
"""
|
|
167
|
+
Set the row keys with consistency checking: if the
|
|
168
|
+
value was previously set, it must remain the same.
|
|
169
|
+
"""
|
|
170
|
+
if row_keys is not None:
|
|
171
|
+
row_keys = tuple(row_keys)
|
|
172
|
+
if self._row_keys is not None and self._row_keys != row_keys:
|
|
173
|
+
raise ValueError(f"inconsistent row keys: should be {self._row_keys} "
|
|
174
|
+
f"but got {row_keys}")
|
|
175
|
+
if row_keys is not None and self._dec != NULL and self.nrows() != len(row_keys):
|
|
176
|
+
raise ValueError(f"inconsistent row keys: should be of cardinality {self.nrows()} "
|
|
177
|
+
f"but got {row_keys}")
|
|
178
|
+
self._row_keys = row_keys
|
|
179
|
+
|
|
180
|
+
cdef _set_column_keys(self, column_keys):
|
|
181
|
+
"""
|
|
182
|
+
Set the column keys with consistency checking: if the
|
|
183
|
+
value was previously set, it must remain the same.
|
|
184
|
+
"""
|
|
185
|
+
if column_keys is not None:
|
|
186
|
+
column_keys = tuple(column_keys)
|
|
187
|
+
if self._column_keys is not None and self._column_keys != column_keys:
|
|
188
|
+
raise ValueError(f"inconsistent column keys: should be {self._column_keys} "
|
|
189
|
+
f"but got {column_keys}")
|
|
190
|
+
if column_keys is not None and self._dec != NULL and self.ncols() != len(column_keys):
|
|
191
|
+
raise ValueError(f"inconsistent column keys: should be of cardinality {self.ncols()} "
|
|
192
|
+
f"but got {column_keys}")
|
|
193
|
+
self._column_keys = column_keys
|
|
194
|
+
|
|
195
|
+
def __dealloc__(self):
|
|
196
|
+
"""
|
|
197
|
+
Frees all the memory allocated for this node.
|
|
198
|
+
"""
|
|
199
|
+
self._set_dec(NULL)
|
|
200
|
+
|
|
201
|
+
def __hash__(self):
|
|
202
|
+
"""
|
|
203
|
+
Return a hash of this node. It is the hash of the decomposition.
|
|
204
|
+
"""
|
|
205
|
+
return <int>self._dec
|
|
206
|
+
|
|
207
|
+
def nrows(self):
|
|
208
|
+
r"""
|
|
209
|
+
Return the number of rows of the internal matrix representing this node.
|
|
210
|
+
|
|
211
|
+
EXAMPLES::
|
|
212
|
+
|
|
213
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
214
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
215
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
216
|
+
[ 1 0]
|
|
217
|
+
[-1 1]
|
|
218
|
+
[ 0 1]
|
|
219
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
220
|
+
sage: result, certificate
|
|
221
|
+
(True, GraphicNode (3×2))
|
|
222
|
+
sage: certificate.nrows()
|
|
223
|
+
3
|
|
224
|
+
sage: certificate.ncols()
|
|
225
|
+
2
|
|
226
|
+
|
|
227
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
228
|
+
sage: node = UnknownNode([[1, 1], [0, 1]]); node
|
|
229
|
+
UnknownNode (2×2)
|
|
230
|
+
sage: node.nrows()
|
|
231
|
+
2
|
|
232
|
+
sage: node.ncols()
|
|
233
|
+
2
|
|
234
|
+
"""
|
|
235
|
+
if self._row_keys is not None:
|
|
236
|
+
return len(self._row_keys)
|
|
237
|
+
if self._dec != NULL:
|
|
238
|
+
return CMRseymourNumRows(self._dec)
|
|
239
|
+
if self._matrix is not None:
|
|
240
|
+
return self._matrix.nrows()
|
|
241
|
+
raise RuntimeError('nrows undefined')
|
|
242
|
+
|
|
243
|
+
def ncols(self):
|
|
244
|
+
r"""
|
|
245
|
+
Return the number of columns of the internal matrix representing this node.
|
|
246
|
+
|
|
247
|
+
EXAMPLES::
|
|
248
|
+
|
|
249
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
250
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
251
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
252
|
+
[ 1 0]
|
|
253
|
+
[-1 1]
|
|
254
|
+
[ 0 1]
|
|
255
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
256
|
+
sage: result, certificate
|
|
257
|
+
(True, GraphicNode (3×2))
|
|
258
|
+
sage: certificate.nrows()
|
|
259
|
+
3
|
|
260
|
+
sage: certificate.ncols()
|
|
261
|
+
2
|
|
262
|
+
|
|
263
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
264
|
+
sage: node = UnknownNode([[1, 1], [0, 1]]); node
|
|
265
|
+
UnknownNode (2×2)
|
|
266
|
+
sage: node.nrows()
|
|
267
|
+
2
|
|
268
|
+
sage: node.ncols()
|
|
269
|
+
2
|
|
270
|
+
"""
|
|
271
|
+
if self._column_keys is not None:
|
|
272
|
+
return len(self._column_keys)
|
|
273
|
+
if self._dec != NULL:
|
|
274
|
+
return CMRseymourNumColumns(self._dec)
|
|
275
|
+
if self._matrix is not None:
|
|
276
|
+
return self._matrix.ncols()
|
|
277
|
+
raise RuntimeError('ncols undefined')
|
|
278
|
+
|
|
279
|
+
def dimensions(self):
|
|
280
|
+
r"""
|
|
281
|
+
Return the number of rows and columns of this node.
|
|
282
|
+
|
|
283
|
+
EXAMPLES::
|
|
284
|
+
|
|
285
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
286
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
287
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
288
|
+
[ 1 0]
|
|
289
|
+
[-1 1]
|
|
290
|
+
[ 0 1]
|
|
291
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
292
|
+
sage: result, certificate
|
|
293
|
+
(True, GraphicNode (3×2))
|
|
294
|
+
sage: certificate.dimensions()
|
|
295
|
+
(3, 2)
|
|
296
|
+
|
|
297
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
298
|
+
sage: node = UnknownNode([[1, 1], [0, 1]]); node
|
|
299
|
+
UnknownNode (2×2)
|
|
300
|
+
sage: node.dimensions()
|
|
301
|
+
(2, 2)
|
|
302
|
+
"""
|
|
303
|
+
return self.nrows(), self.ncols()
|
|
304
|
+
|
|
305
|
+
def base_ring(self):
|
|
306
|
+
r"""
|
|
307
|
+
Return the base ring of the matrix representing the node.
|
|
308
|
+
"""
|
|
309
|
+
return self._base_ring
|
|
310
|
+
|
|
311
|
+
def matrix(self):
|
|
312
|
+
r"""
|
|
313
|
+
Return a :class:`Matrix`.
|
|
314
|
+
|
|
315
|
+
EXAMPLES::
|
|
316
|
+
|
|
317
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
318
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
319
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
320
|
+
[ 1 0]
|
|
321
|
+
[-1 1]
|
|
322
|
+
[ 0 1]
|
|
323
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
324
|
+
sage: result, certificate
|
|
325
|
+
(True, GraphicNode (3×2))
|
|
326
|
+
sage: certificate.matrix()
|
|
327
|
+
[ 1 0]
|
|
328
|
+
[-1 1]
|
|
329
|
+
[ 0 1]
|
|
330
|
+
|
|
331
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
332
|
+
sage: node = UnknownNode([[1, 1], [0, 1]]); node
|
|
333
|
+
UnknownNode (2×2)
|
|
334
|
+
sage: node.matrix()
|
|
335
|
+
[1 1]
|
|
336
|
+
[0 1]
|
|
337
|
+
"""
|
|
338
|
+
if self._matrix is not None:
|
|
339
|
+
return self._matrix
|
|
340
|
+
if self._dec is NULL:
|
|
341
|
+
if isinstance(self, SumNode):
|
|
342
|
+
return self.block_matrix_form()
|
|
343
|
+
raise ValueError('Matrix and decomposition are both missing')
|
|
344
|
+
cdef Matrix_cmr_chr_sparse result
|
|
345
|
+
cdef CMR_CHRMAT *mat = CMRseymourGetMatrix(self._dec)
|
|
346
|
+
if mat == NULL:
|
|
347
|
+
return None
|
|
348
|
+
ms = MatrixSpace(self.base_ring(), mat.numRows, mat.numColumns, sparse=True)
|
|
349
|
+
result = Matrix_cmr_chr_sparse.__new__(Matrix_cmr_chr_sparse, ms)
|
|
350
|
+
result._mat = mat
|
|
351
|
+
result._root = self # Matrix is owned by us
|
|
352
|
+
self._matrix = result
|
|
353
|
+
return result
|
|
354
|
+
|
|
355
|
+
def row_keys(self):
|
|
356
|
+
r"""
|
|
357
|
+
Return the row keys of this node.
|
|
358
|
+
|
|
359
|
+
OUTPUT: a tuple or ``None``
|
|
360
|
+
|
|
361
|
+
EXAMPLES::
|
|
362
|
+
|
|
363
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
364
|
+
sage: node = UnknownNode(matrix(ZZ, [[1, 0, 1], [0, 1, 1]]),
|
|
365
|
+
....: row_keys='ab',
|
|
366
|
+
....: column_keys=range(3)); node
|
|
367
|
+
UnknownNode (2×3)
|
|
368
|
+
sage: node.row_keys()
|
|
369
|
+
('a', 'b')
|
|
370
|
+
"""
|
|
371
|
+
return self._row_keys
|
|
372
|
+
|
|
373
|
+
def column_keys(self):
|
|
374
|
+
r"""
|
|
375
|
+
Return the column keys of this node.
|
|
376
|
+
|
|
377
|
+
OUTPUT: a tuple or ``None``
|
|
378
|
+
|
|
379
|
+
EXAMPLES::
|
|
380
|
+
|
|
381
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
382
|
+
sage: node = UnknownNode(matrix(ZZ, [[1, 0, 1], [0, 1, 1]]),
|
|
383
|
+
....: row_keys='ab',
|
|
384
|
+
....: column_keys=range(3)); node
|
|
385
|
+
UnknownNode (2×3)
|
|
386
|
+
sage: node.column_keys()
|
|
387
|
+
(0, 1, 2)
|
|
388
|
+
"""
|
|
389
|
+
return self._column_keys
|
|
390
|
+
|
|
391
|
+
def set_default_keys(self):
|
|
392
|
+
r"""
|
|
393
|
+
Set default row and column keys.
|
|
394
|
+
|
|
395
|
+
.. SEEALSO:: :class:`ElementKey`
|
|
396
|
+
|
|
397
|
+
EXAMPLES::
|
|
398
|
+
|
|
399
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
400
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
401
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
402
|
+
[ 1 0]
|
|
403
|
+
[-1 1]
|
|
404
|
+
[ 0 1]
|
|
405
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
406
|
+
sage: result, certificate
|
|
407
|
+
(True, GraphicNode (3×2))
|
|
408
|
+
sage: certificate.row_keys() is None
|
|
409
|
+
True
|
|
410
|
+
sage: certificate.set_default_keys()
|
|
411
|
+
sage: certificate.row_keys()
|
|
412
|
+
(r0, r1, r2)
|
|
413
|
+
"""
|
|
414
|
+
row_keys = self.row_keys()
|
|
415
|
+
column_keys = self.column_keys()
|
|
416
|
+
if row_keys is None or column_keys is None:
|
|
417
|
+
row_keys = tuple(ElementKey(f"r{i}") for i in range(self.nrows()))
|
|
418
|
+
column_keys = tuple(ElementKey(f"c{i}") for i in range(self.ncols()))
|
|
419
|
+
elif not isinstance(row_keys[0], ElementKey):
|
|
420
|
+
row_keys = tuple(ElementKey(key) for key in row_keys)
|
|
421
|
+
column_keys = tuple(ElementKey(key) for key in column_keys)
|
|
422
|
+
self._row_keys = row_keys
|
|
423
|
+
self._column_keys = column_keys
|
|
424
|
+
|
|
425
|
+
@cached_method
|
|
426
|
+
def morphism(self):
|
|
427
|
+
r"""
|
|
428
|
+
Create the matrix in the distinguished bases of the domain and codomain
|
|
429
|
+
to build the module morphism.
|
|
430
|
+
|
|
431
|
+
See also: :class:`sage.modules.with_basis.morphism.ModuleMorphismFromMatrix`.
|
|
432
|
+
|
|
433
|
+
EXAMPLES::
|
|
434
|
+
|
|
435
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
436
|
+
sage: node = UnknownNode(matrix(ZZ, [[1, 0, 1], [0, 1, 1]]),
|
|
437
|
+
....: row_keys='ab',
|
|
438
|
+
....: column_keys=range(3)); node
|
|
439
|
+
UnknownNode (2×3)
|
|
440
|
+
sage: node.matrix()
|
|
441
|
+
[1 0 1]
|
|
442
|
+
[0 1 1]
|
|
443
|
+
sage: node.morphism()._unicode_art_matrix()
|
|
444
|
+
0 1 2
|
|
445
|
+
a⎛1 0 1⎞
|
|
446
|
+
b⎝0 1 1⎠
|
|
447
|
+
"""
|
|
448
|
+
return Matrix(self.matrix(),
|
|
449
|
+
row_keys=self.row_keys(),
|
|
450
|
+
column_keys=self.column_keys())
|
|
451
|
+
|
|
452
|
+
def as_ordered_tree(self):
|
|
453
|
+
r"""
|
|
454
|
+
Return the decomposition tree rooted at ``self``.
|
|
455
|
+
|
|
456
|
+
EXAMPLES::
|
|
457
|
+
|
|
458
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
459
|
+
sage: M = matrix([[1, 0], [-1, 1], [0, 1]], sparse=True)
|
|
460
|
+
sage: M2 = block_diagonal_matrix([M, M], sparse=True)
|
|
461
|
+
sage: M2cmr = Matrix_cmr_chr_sparse(M2.parent(), M2); M2cmr
|
|
462
|
+
[ 1 0 0 0]
|
|
463
|
+
[-1 1 0 0]
|
|
464
|
+
[ 0 1 0 0]
|
|
465
|
+
[ 0 0 1 0]
|
|
466
|
+
[ 0 0 -1 1]
|
|
467
|
+
[ 0 0 0 1]
|
|
468
|
+
sage: result, certificate = M2cmr.is_totally_unimodular(certificate=True)
|
|
469
|
+
sage: T = certificate.as_ordered_tree(); T
|
|
470
|
+
OneSumNode (6×4) with 2 children[GraphicNode (3×2)[], GraphicNode (3×2)[]]
|
|
471
|
+
sage: unicode_art(T)
|
|
472
|
+
╭───────────OneSumNode (6×4) with 2 children
|
|
473
|
+
│ │
|
|
474
|
+
GraphicNode (3×2) GraphicNode (3×2)
|
|
475
|
+
"""
|
|
476
|
+
from sage.combinat.ordered_tree import LabelledOrderedTree
|
|
477
|
+
return LabelledOrderedTree([child.as_ordered_tree() for child in self.child_nodes()],
|
|
478
|
+
label=self)
|
|
479
|
+
|
|
480
|
+
def plot(self, **kwds):
|
|
481
|
+
r"""
|
|
482
|
+
Plot the decomposition tree rooted at ``self``.
|
|
483
|
+
|
|
484
|
+
EXAMPLES::
|
|
485
|
+
|
|
486
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
487
|
+
sage: M = matrix([[1, 0], [-1, 1], [0, 1]], sparse=True)
|
|
488
|
+
sage: M2MT = block_diagonal_matrix([M, M, M.T], sparse=True)
|
|
489
|
+
sage: M2MTcmr = Matrix_cmr_chr_sparse(M2MT.parent(), M2MT)
|
|
490
|
+
sage: result, certificate = M2MTcmr.is_totally_unimodular(certificate=True)
|
|
491
|
+
sage: T = certificate.as_ordered_tree()
|
|
492
|
+
sage: T.plot() # needs sage.plot
|
|
493
|
+
Graphics object consisting of 8 graphics primitives
|
|
494
|
+
"""
|
|
495
|
+
return self.as_ordered_tree().plot(**kwds)
|
|
496
|
+
|
|
497
|
+
def is_ternary(self):
|
|
498
|
+
r"""
|
|
499
|
+
Return whether the decomposition is over `\GF{3}`.
|
|
500
|
+
|
|
501
|
+
EXAMPLES::
|
|
502
|
+
|
|
503
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
504
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
505
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
506
|
+
[ 1 0]
|
|
507
|
+
[-1 1]
|
|
508
|
+
[ 0 1]
|
|
509
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
510
|
+
sage: result, certificate
|
|
511
|
+
(True, GraphicNode (3×2))
|
|
512
|
+
sage: certificate.is_ternary()
|
|
513
|
+
True
|
|
514
|
+
|
|
515
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
516
|
+
....: [[1, 0], [1, 1], [0, 1]]); M
|
|
517
|
+
[1 0]
|
|
518
|
+
[1 1]
|
|
519
|
+
[0 1]
|
|
520
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True)
|
|
521
|
+
sage: result, certificate
|
|
522
|
+
(True, GraphicNode (3×2))
|
|
523
|
+
sage: certificate.is_ternary()
|
|
524
|
+
False
|
|
525
|
+
"""
|
|
526
|
+
return <bint> CMRseymourIsTernary(self._dec)
|
|
527
|
+
|
|
528
|
+
def nchildren(self):
|
|
529
|
+
r"""
|
|
530
|
+
Return the number of children of the node.
|
|
531
|
+
|
|
532
|
+
EXAMPLES::
|
|
533
|
+
|
|
534
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
535
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
536
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
537
|
+
[ 1 0]
|
|
538
|
+
[-1 1]
|
|
539
|
+
[ 0 1]
|
|
540
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
541
|
+
sage: result, certificate
|
|
542
|
+
(True, GraphicNode (3×2))
|
|
543
|
+
sage: certificate.nchildren()
|
|
544
|
+
0
|
|
545
|
+
"""
|
|
546
|
+
if self._child_nodes is not None:
|
|
547
|
+
return len(self._child_nodes)
|
|
548
|
+
if self._dec == NULL:
|
|
549
|
+
return 0
|
|
550
|
+
return CMRseymourNumChildren(self._dec)
|
|
551
|
+
|
|
552
|
+
cdef _CMRelement_to_key(self, CMR_ELEMENT element):
|
|
553
|
+
r"""
|
|
554
|
+
Transform a ``CMRelement`` (row or column index implemented in cmr)
|
|
555
|
+
to a row key or a column key.
|
|
556
|
+
"""
|
|
557
|
+
if not CMRelementIsValid(element):
|
|
558
|
+
raise ValueError('CMRelement index not valid. Extra row or column is detected.')
|
|
559
|
+
if self.row_keys() is None or self.column_keys() is None:
|
|
560
|
+
raise ValueError('row_keys and column_keys are required')
|
|
561
|
+
if CMRelementIsRow(element):
|
|
562
|
+
return self.row_keys()[CMRelementToRowIndex(element)]
|
|
563
|
+
else:
|
|
564
|
+
return self.column_keys()[CMRelementToColumnIndex(element)]
|
|
565
|
+
|
|
566
|
+
def _create_child_node(self, index):
|
|
567
|
+
r"""
|
|
568
|
+
Return the child node of ``self`` corresponding to the ``index``,
|
|
569
|
+
and the corresponding row and column keys in the parent node.
|
|
570
|
+
|
|
571
|
+
OUTPUT: a tuple of (child node, child row keys, child column keys)
|
|
572
|
+
"""
|
|
573
|
+
row_keys = self.row_keys()
|
|
574
|
+
column_keys = self.column_keys()
|
|
575
|
+
cdef CMR_SEYMOUR_NODE *child_dec = CMRseymourChild(self._dec, index)
|
|
576
|
+
cdef CMR_ELEMENT *parent_rows = CMRseymourChildRowsToParent(self._dec, index)
|
|
577
|
+
cdef CMR_ELEMENT *parent_columns = CMRseymourChildColumnsToParent(self._dec, index)
|
|
578
|
+
child_nrows = CMRseymourNumRows(child_dec)
|
|
579
|
+
child_ncols = CMRseymourNumColumns(child_dec)
|
|
580
|
+
|
|
581
|
+
if parent_rows == NULL or all(parent_rows[i] == 0 for i in range(child_nrows)):
|
|
582
|
+
raise ValueError(f"Child {index} does not have parents rows")
|
|
583
|
+
parent_rows_tuple = tuple(parent_rows[i] for i in range(child_nrows))
|
|
584
|
+
|
|
585
|
+
if parent_columns == NULL or all(parent_columns[i] == 0 for i in range(child_ncols)):
|
|
586
|
+
raise ValueError(f"Child {index} does not have parents columns")
|
|
587
|
+
parent_columns_tuple = tuple(parent_columns[i] for i in range(child_ncols))
|
|
588
|
+
|
|
589
|
+
if row_keys is not None and column_keys is not None:
|
|
590
|
+
child_row_keys = tuple(self._CMRelement_to_key(element)
|
|
591
|
+
for element in parent_rows_tuple)
|
|
592
|
+
child_column_keys = tuple(self._CMRelement_to_key(element)
|
|
593
|
+
for element in parent_columns_tuple)
|
|
594
|
+
child = create_DecompositionNode(child_dec, matrix=None,
|
|
595
|
+
row_keys=child_row_keys,
|
|
596
|
+
column_keys=child_column_keys,
|
|
597
|
+
base_ring=self.base_ring())
|
|
598
|
+
else:
|
|
599
|
+
child_row_keys = tuple(CMRelementToRowIndex(element)
|
|
600
|
+
for element in parent_rows_tuple)
|
|
601
|
+
child_column_keys = tuple(CMRelementToColumnIndex(element)
|
|
602
|
+
for element in parent_columns_tuple)
|
|
603
|
+
child = create_DecompositionNode(child_dec, matrix=None,
|
|
604
|
+
row_keys=child_row_keys,
|
|
605
|
+
column_keys=child_column_keys,
|
|
606
|
+
base_ring=self.base_ring())
|
|
607
|
+
return child, child_row_keys, child_column_keys
|
|
608
|
+
|
|
609
|
+
def _children(self):
|
|
610
|
+
r"""
|
|
611
|
+
Return a tuple of the tuples of children and their row and column keys.
|
|
612
|
+
The underlying implementation of :meth:`child_nodes`
|
|
613
|
+
and :meth:`child_keys`.
|
|
614
|
+
"""
|
|
615
|
+
if self._child_nodes is not None:
|
|
616
|
+
return self._child_nodes
|
|
617
|
+
children_tuple = tuple(self._create_child_node(index)
|
|
618
|
+
for index in range(self.nchildren()))
|
|
619
|
+
self._child_nodes = children_tuple
|
|
620
|
+
return self._child_nodes
|
|
621
|
+
|
|
622
|
+
def child_nodes(self):
|
|
623
|
+
r"""
|
|
624
|
+
Return a tuple of the children.
|
|
625
|
+
|
|
626
|
+
The children are sorted by the ordering inherited from cmr, which
|
|
627
|
+
is their appearance in the parent.
|
|
628
|
+
|
|
629
|
+
In the case of :class:`SumNode`, this is the same as :meth:`~SumNode.summands`.
|
|
630
|
+
|
|
631
|
+
For graphic or leaf nodes, it returns the empty tuple.
|
|
632
|
+
|
|
633
|
+
EXAMPLES::
|
|
634
|
+
|
|
635
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
636
|
+
sage: M = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
637
|
+
....: [[1, 1], [-1, 0]],
|
|
638
|
+
....: [[1, 0], [0,1]]); M
|
|
639
|
+
[ 1 0| 0 0| 0 0]
|
|
640
|
+
[-1 1| 0 0| 0 0]
|
|
641
|
+
[-----+-----+-----]
|
|
642
|
+
[ 0 0| 1 1| 0 0]
|
|
643
|
+
[ 0 0|-1 0| 0 0]
|
|
644
|
+
[-----+-----+-----]
|
|
645
|
+
[ 0 0| 0 0| 1 0]
|
|
646
|
+
[ 0 0| 0 0| 0 1]
|
|
647
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True); certificate
|
|
648
|
+
OneSumNode (6×6) with 4 children
|
|
649
|
+
sage: certificate.child_nodes()
|
|
650
|
+
(GraphicNode (2×2), GraphicNode (2×2), GraphicNode (1×1), GraphicNode (1×1))
|
|
651
|
+
|
|
652
|
+
sage: M2 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 2, 2, sparse=True),
|
|
653
|
+
....: [[1, 1], [-1, 0]]); M2
|
|
654
|
+
[ 1 1]
|
|
655
|
+
[-1 0]
|
|
656
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True); certificate
|
|
657
|
+
GraphicNode (2×2)
|
|
658
|
+
sage: certificate.child_nodes()
|
|
659
|
+
()
|
|
660
|
+
"""
|
|
661
|
+
return tuple(child[0] for child in self._children())
|
|
662
|
+
|
|
663
|
+
def child_keys(self):
|
|
664
|
+
r"""
|
|
665
|
+
Return a tuple of the tuples of the row and column keys of children
|
|
666
|
+
in the parent node.
|
|
667
|
+
|
|
668
|
+
OUTPUT: a tuple of (row keys, column keys)
|
|
669
|
+
|
|
670
|
+
If the number of children is 1, then output (row keys, column keys).
|
|
671
|
+
|
|
672
|
+
EXAMPLES::
|
|
673
|
+
|
|
674
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
675
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
676
|
+
....: [[1, 0], [-1, 1], [0, 1]]); M
|
|
677
|
+
[ 1 0]
|
|
678
|
+
[-1 1]
|
|
679
|
+
[ 0 1]
|
|
680
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
681
|
+
sage: certificate.child_keys()
|
|
682
|
+
()
|
|
683
|
+
|
|
684
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
685
|
+
sage: M = matrix([[1, 0], [-1, 1], [0, 1]], sparse=True)
|
|
686
|
+
sage: M2 = block_diagonal_matrix([M, M], sparse=True)
|
|
687
|
+
sage: M2cmr = Matrix_cmr_chr_sparse(M2.parent(), M2); M2cmr
|
|
688
|
+
[ 1 0 0 0]
|
|
689
|
+
[-1 1 0 0]
|
|
690
|
+
[ 0 1 0 0]
|
|
691
|
+
[ 0 0 1 0]
|
|
692
|
+
[ 0 0 -1 1]
|
|
693
|
+
[ 0 0 0 1]
|
|
694
|
+
sage: result, certificate = M2cmr.is_totally_unimodular(certificate=True)
|
|
695
|
+
sage: result, certificate
|
|
696
|
+
(True, OneSumNode (6×4) with 2 children)
|
|
697
|
+
sage: C = certificate.summands(); C
|
|
698
|
+
(GraphicNode (3×2), GraphicNode (3×2))
|
|
699
|
+
sage: certificate.child_keys()[0]
|
|
700
|
+
((0, 1, 2), (0, 1))
|
|
701
|
+
sage: certificate.child_keys()[1]
|
|
702
|
+
((3, 4, 5), (2, 3))
|
|
703
|
+
|
|
704
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
705
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
706
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
707
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
708
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
709
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
710
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
711
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
712
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
713
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
714
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
715
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
716
|
+
....: decompose_strategy="delta_pivot",
|
|
717
|
+
....: row_keys=['r1', 'r2', 'r3', 'r4', 'r5',
|
|
718
|
+
....: 'r6', 'r7', 'r8', 'r9'],
|
|
719
|
+
....: column_keys=['a','b','c','d','e','f',
|
|
720
|
+
....: 'g','h','i','j','k','l'])
|
|
721
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
722
|
+
DeltaSumNode (9×12)
|
|
723
|
+
sage: C1, C2 = C.child_nodes()
|
|
724
|
+
sage: C1.matrix()
|
|
725
|
+
[ 0 0 1 1 1 1 1]
|
|
726
|
+
[ 1 1 0 0 0 -1 -1]
|
|
727
|
+
[ 1 0 -1 0 -1 -1 -1]
|
|
728
|
+
[ 0 1 1 0 1 0 0]
|
|
729
|
+
[ 0 0 0 -1 -1 0 -1]
|
|
730
|
+
sage: C2.matrix()
|
|
731
|
+
[-1 0 1 -1 0 0 0 0 -1]
|
|
732
|
+
[ 0 0 -1 1 0 1 -1 0 1]
|
|
733
|
+
[ 1 1 0 1 1 0 0 0 1]
|
|
734
|
+
[ 1 1 0 1 1 0 0 0 0]
|
|
735
|
+
[ 1 1 -1 1 0 1 0 1 1]
|
|
736
|
+
[ 1 1 0 0 0 0 1 1 0]
|
|
737
|
+
sage: certificate.child_keys()
|
|
738
|
+
((i, r2, r3, r4, r5, r6, r7, r8, r9), (a, b, c, d, e, f, g, h, r1, j, k, l))
|
|
739
|
+
sage: C.matrix()
|
|
740
|
+
[ 1 -1 0 0 0 0 0 0 -1 1 1 1]
|
|
741
|
+
[-1 1 0 1 -1 0 0 0 1 0 0 0]
|
|
742
|
+
[-1 1 0 0 0 0 1 1 1 0 0 0]
|
|
743
|
+
[ 0 1 1 0 0 0 0 0 1 0 -1 -1]
|
|
744
|
+
[ 0 1 1 0 0 0 0 0 0 0 -1 -1]
|
|
745
|
+
[-1 1 0 1 0 1 0 0 1 0 -1 -1]
|
|
746
|
+
[ 0 0 0 0 1 1 0 0 0 0 -1 -1]
|
|
747
|
+
[-1 1 0 0 0 0 1 0 1 -1 0 -1]
|
|
748
|
+
[ 0 0 0 0 0 0 0 1 0 1 0 1]
|
|
749
|
+
sage: C.child_keys()
|
|
750
|
+
(((i, r3, r8, r9, r4), (g, h, j, k, l, a, +a-r4)),
|
|
751
|
+
((i, r2, r4, r5, r6, r7), (-i+k, k, a, b, c, d, e, f, r1)))
|
|
752
|
+
|
|
753
|
+
sage: M2 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 2, 2, sparse=True),
|
|
754
|
+
....: [[1, 1], [-1, 0]]); M2
|
|
755
|
+
[ 1 1]
|
|
756
|
+
[-1 0]
|
|
757
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True); certificate
|
|
758
|
+
GraphicNode (2×2)
|
|
759
|
+
sage: certificate.child_keys()
|
|
760
|
+
()
|
|
761
|
+
"""
|
|
762
|
+
if self.nchildren() == 1:
|
|
763
|
+
child = self._children()[0]
|
|
764
|
+
return (child[1], child[2])
|
|
765
|
+
return tuple((child[1], child[2]) for child in self._children())
|
|
766
|
+
|
|
767
|
+
def _repr_(self):
|
|
768
|
+
r"""
|
|
769
|
+
Return a string representation of ``self``.
|
|
770
|
+
|
|
771
|
+
EXAMPLES::
|
|
772
|
+
|
|
773
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
774
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
775
|
+
....: [[1,0,1,1,0,0], [0,1,1,1,0,0], [1,0,1,0,1,1],
|
|
776
|
+
....: [0,-1,0,-1,1,1], [1,0,1,0,1,0], [0,-1,0,-1,0,1]])
|
|
777
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
778
|
+
....: decompose_strategy="delta_pivot",
|
|
779
|
+
....: row_keys=range(6),
|
|
780
|
+
....: column_keys='abcdef')
|
|
781
|
+
sage: print(certificate)
|
|
782
|
+
PivotsNode (6×6)
|
|
783
|
+
"""
|
|
784
|
+
nrows, ncols = self.dimensions()
|
|
785
|
+
return f'{self.__class__.__name__} ({nrows}×{ncols})'
|
|
786
|
+
|
|
787
|
+
def _unicode_art_(self):
|
|
788
|
+
r"""
|
|
789
|
+
Return a unicode art representation of the decomposition tree rooted at ``self``.
|
|
790
|
+
|
|
791
|
+
EXAMPLES::
|
|
792
|
+
|
|
793
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
794
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
795
|
+
....: [[1,0,1,1,0,0], [0,1,1,1,0,0], [1,0,1,0,1,1],
|
|
796
|
+
....: [0,-1,0,-1,1,1], [1,0,1,0,1,0], [0,-1,0,-1,0,1]])
|
|
797
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
798
|
+
....: decompose_strategy="delta_pivot",
|
|
799
|
+
....: row_keys=range(6),
|
|
800
|
+
....: column_keys='abcdef')
|
|
801
|
+
sage: unicode_art(certificate)
|
|
802
|
+
PivotsNode (6×6)
|
|
803
|
+
│
|
|
804
|
+
╭DeltaSumNode (6×6)─╮
|
|
805
|
+
│ │
|
|
806
|
+
CographicNode (4×5) GraphicNode (4×5)
|
|
807
|
+
"""
|
|
808
|
+
return self.as_ordered_tree()._unicode_art_()
|
|
809
|
+
|
|
810
|
+
def _ascii_art_(self):
|
|
811
|
+
r"""
|
|
812
|
+
Return a ascii art representation of the decomposition tree rooted at ``self``.
|
|
813
|
+
|
|
814
|
+
EXAMPLES::
|
|
815
|
+
|
|
816
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
817
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
818
|
+
....: [[1,0,1,1,0,0], [0,1,1,1,0,0], [1,0,1,0,1,1],
|
|
819
|
+
....: [0,-1,0,-1,1,1], [1,0,1,0,1,0], [0,-1,0,-1,0,1]])
|
|
820
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
821
|
+
....: decompose_strategy="delta_pivot",
|
|
822
|
+
....: row_keys=range(6),
|
|
823
|
+
....: column_keys='abcdef')
|
|
824
|
+
sage: ascii_art(certificate)
|
|
825
|
+
PivotsNode (6×6)
|
|
826
|
+
|
|
|
827
|
+
_________DeltaSumNode (6×6)
|
|
828
|
+
/ /
|
|
829
|
+
CographicNode (4×5) GraphicNode (4×5)
|
|
830
|
+
"""
|
|
831
|
+
return self.as_ordered_tree()._ascii_art_()
|
|
832
|
+
|
|
833
|
+
def one_sum(*summands, **kwds):
|
|
834
|
+
r"""
|
|
835
|
+
Return a :class:`OneSumNode` constructed from the given nodes (summands).
|
|
836
|
+
|
|
837
|
+
INPUT:
|
|
838
|
+
|
|
839
|
+
- ``summands`` -- decomposition nodes :class:`DecompositionNode`
|
|
840
|
+
|
|
841
|
+
- ``summand_ids`` -- a tuple or list of ids for summands
|
|
842
|
+
|
|
843
|
+
- ``row_keys`` -- a finite or enumerated family of arbitrary objects
|
|
844
|
+
that index the rows of the result.
|
|
845
|
+
Must be consistent with the row keys of the summands
|
|
846
|
+
|
|
847
|
+
- ``column_keys`` -- a finite or enumerated family of arbitrary objects
|
|
848
|
+
that index the columns of the result.
|
|
849
|
+
Must be consistent with the column keys of the summands
|
|
850
|
+
|
|
851
|
+
Note that ``row_keys``, ``column_keys`` of ``summands`` are disjoint
|
|
852
|
+
|
|
853
|
+
OUTPUT: A :class:`OneSumNode`
|
|
854
|
+
|
|
855
|
+
The terminology "1-sum" is used in the context of Seymour's decomposition
|
|
856
|
+
of totally unimodular matrices and regular matroids, see [Sch1986]_.
|
|
857
|
+
|
|
858
|
+
.. SEEALSO:: :meth:`two_sum`
|
|
859
|
+
:meth:`delta_sum`, :meth:`three_sum`, :meth:`y_sum`
|
|
860
|
+
|
|
861
|
+
EXAMPLES::
|
|
862
|
+
|
|
863
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
864
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
865
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
866
|
+
....: [[1, 1], [-1, 0]])
|
|
867
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
868
|
+
....: row_keys=range(4),
|
|
869
|
+
....: column_keys='abcd')
|
|
870
|
+
sage: certificate
|
|
871
|
+
OneSumNode (4×4) with 2 children
|
|
872
|
+
sage: certificate.summand_matrices()
|
|
873
|
+
(
|
|
874
|
+
[ 1 0] [ 1 1]
|
|
875
|
+
[-1 1], [-1 0]
|
|
876
|
+
)
|
|
877
|
+
sage: certificate.child_keys()
|
|
878
|
+
(((0, 1), ('a', 'b')), ((2, 3), ('c', 'd')))
|
|
879
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
880
|
+
sage: node.summand_matrices()
|
|
881
|
+
(
|
|
882
|
+
[ 1 0] [ 1 1]
|
|
883
|
+
[-1 1], [-1 0]
|
|
884
|
+
)
|
|
885
|
+
sage: node.child_keys()
|
|
886
|
+
(((0, 1), ('a', 'b')), ((2, 3), ('c', 'd')))
|
|
887
|
+
|
|
888
|
+
sage: M3 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
889
|
+
....: [[1]], [[-1]])
|
|
890
|
+
sage: result, certificate = M3.is_totally_unimodular(certificate=True,
|
|
891
|
+
....: row_keys=range(4),
|
|
892
|
+
....: column_keys='abcd')
|
|
893
|
+
sage: certificate
|
|
894
|
+
OneSumNode (4×4) with 3 children
|
|
895
|
+
sage: certificate.summand_matrices()
|
|
896
|
+
(
|
|
897
|
+
[ 1 0]
|
|
898
|
+
[-1 1], [1], [-1]
|
|
899
|
+
)
|
|
900
|
+
sage: certificate.child_keys()
|
|
901
|
+
(((0, 1), ('a', 'b')), ((2,), ('c',)), ((3,), ('d',)))
|
|
902
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
903
|
+
sage: node.summand_matrices()
|
|
904
|
+
(
|
|
905
|
+
[ 1 0]
|
|
906
|
+
[-1 1], [1], [-1]
|
|
907
|
+
)
|
|
908
|
+
sage: node.child_keys()
|
|
909
|
+
(((0, 1), ('a', 'b')), ((2,), ('c',)), ((3,), ('d',)))
|
|
910
|
+
|
|
911
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
912
|
+
sage: node = UnknownNode(matrix(ZZ, [[1, 0, 1], [0, 1, 1]]),
|
|
913
|
+
....: row_keys='ab',
|
|
914
|
+
....: column_keys=range(3)); node
|
|
915
|
+
UnknownNode (2×3)
|
|
916
|
+
sage: node = DecompositionNode.one_sum(certificate, node, summand_ids=range(2))
|
|
917
|
+
sage: node.summand_matrices()
|
|
918
|
+
(
|
|
919
|
+
[ 1 0| 0| 0]
|
|
920
|
+
[-1 1| 0| 0]
|
|
921
|
+
[-----+--+--]
|
|
922
|
+
[ 0 0| 1| 0]
|
|
923
|
+
[-----+--+--] [1 0 1]
|
|
924
|
+
[ 0 0| 0|-1], [0 1 1]
|
|
925
|
+
)
|
|
926
|
+
sage: node.child_keys()
|
|
927
|
+
((((0, 0), (0, 1), (0, 2), (0, 3)), ((0, 'a'), (0, 'b'), (0, 'c'), (0, 'd'))),
|
|
928
|
+
(((1, 'a'), (1, 'b')), ((1, 0), (1, 1), (1, 2))))
|
|
929
|
+
|
|
930
|
+
``row_keys``, ``column_keys`` of ``summands`` are disjoint::
|
|
931
|
+
|
|
932
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
933
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
934
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
935
|
+
....: [[1, 1], [-1, 0]])
|
|
936
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
937
|
+
....: row_keys='aefg',
|
|
938
|
+
....: column_keys='abcd')
|
|
939
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
940
|
+
Traceback (most recent call last):
|
|
941
|
+
...
|
|
942
|
+
ValueError: keys must be disjoint, got
|
|
943
|
+
summand_row_keys=('a', 'e'), summand_column_keys=('a', 'b')
|
|
944
|
+
|
|
945
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
946
|
+
....: row_keys=range(4),
|
|
947
|
+
....: column_keys='abcd')
|
|
948
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes(),
|
|
949
|
+
....: row_keys=range(4),
|
|
950
|
+
....: column_keys='abce')
|
|
951
|
+
Traceback (most recent call last):
|
|
952
|
+
...
|
|
953
|
+
ValueError: inconsistent column_keys, got column_keys=('a', 'b', 'c', 'e'),
|
|
954
|
+
should be a permutation of ['a', 'b', 'c', 'd']
|
|
955
|
+
"""
|
|
956
|
+
summand_ids = kwds.pop('summand_ids', None)
|
|
957
|
+
row_keys = kwds.pop('row_keys', None)
|
|
958
|
+
column_keys = kwds.pop('column_keys', None)
|
|
959
|
+
if kwds:
|
|
960
|
+
raise ValueError(f'unknown keywords: {sorted(kwds)}')
|
|
961
|
+
|
|
962
|
+
result = OneSumNode()
|
|
963
|
+
summands = tuple(summands)
|
|
964
|
+
if summand_ids is not None:
|
|
965
|
+
summand_ids = tuple(summand_ids)
|
|
966
|
+
else:
|
|
967
|
+
summand_ids = tuple(None for summand in summands)
|
|
968
|
+
# TODO: Make summands DecompositionNodes if not already
|
|
969
|
+
# Check row_keys, column_keys of summands are disjoint. Otherwise error
|
|
970
|
+
summands_row_keys = []
|
|
971
|
+
summands_column_keys = []
|
|
972
|
+
row_key_list = []
|
|
973
|
+
column_key_list = []
|
|
974
|
+
key_set = set()
|
|
975
|
+
for summand, id in zip(summands, summand_ids):
|
|
976
|
+
summand_row_keys = summand.row_keys()
|
|
977
|
+
summand_column_keys = summand.column_keys()
|
|
978
|
+
if id is not None:
|
|
979
|
+
summand_row_keys = tuple((id, key) for key in summand_row_keys)
|
|
980
|
+
summand_column_keys = tuple((id, key) for key in summand_column_keys)
|
|
981
|
+
|
|
982
|
+
old_num_keys = len(key_set)
|
|
983
|
+
row_key_list.extend(summand_row_keys)
|
|
984
|
+
column_key_list.extend(summand_column_keys)
|
|
985
|
+
key_set.update(summand_row_keys)
|
|
986
|
+
key_set.update(summand_column_keys)
|
|
987
|
+
if old_num_keys + len(summand_row_keys) + len(summand_column_keys) != len(key_set):
|
|
988
|
+
raise ValueError(f'keys must be disjoint, '
|
|
989
|
+
f'got {summand_row_keys=}, {summand_column_keys=}')
|
|
990
|
+
summands_row_keys.append(summand_row_keys)
|
|
991
|
+
summands_column_keys.append(summand_column_keys)
|
|
992
|
+
|
|
993
|
+
if row_keys is not None:
|
|
994
|
+
row_keys = tuple(row_keys)
|
|
995
|
+
if set(row_keys) != set(row_key_list) or len(row_keys) != len(row_key_list):
|
|
996
|
+
raise ValueError(f'inconsistent row_keys, '
|
|
997
|
+
f'got {row_keys=}, should be a permutation of {row_key_list}')
|
|
998
|
+
else:
|
|
999
|
+
row_keys = tuple(row_key_list)
|
|
1000
|
+
if column_keys is not None:
|
|
1001
|
+
column_keys = tuple(column_keys)
|
|
1002
|
+
if set(column_keys) != set(column_key_list) or len(column_keys) != len(column_key_list):
|
|
1003
|
+
raise ValueError(f'inconsistent column_keys, '
|
|
1004
|
+
f'got {column_keys=}, should be a permutation of {column_key_list}')
|
|
1005
|
+
else:
|
|
1006
|
+
column_keys = tuple(column_key_list)
|
|
1007
|
+
|
|
1008
|
+
result._child_nodes = tuple(zip(summands, summands_row_keys, summands_column_keys))
|
|
1009
|
+
result._row_keys = row_keys
|
|
1010
|
+
result._column_keys = column_keys
|
|
1011
|
+
return result
|
|
1012
|
+
|
|
1013
|
+
def _regularity(self):
|
|
1014
|
+
r"""
|
|
1015
|
+
Return whether the decomposition node is regular (binary) or TU (ternary).
|
|
1016
|
+
If it is not determined, raise ValueError.
|
|
1017
|
+
"""
|
|
1018
|
+
cdef int8_t regularity
|
|
1019
|
+
if self._dec != NULL:
|
|
1020
|
+
regularity = CMRseymourRegularity(self._dec)
|
|
1021
|
+
if regularity:
|
|
1022
|
+
return regularity > 0
|
|
1023
|
+
raise ValueError('It is not determined whether the decomposition node is regular/TU')
|
|
1024
|
+
|
|
1025
|
+
def _graphicness(self):
|
|
1026
|
+
r"""
|
|
1027
|
+
Return whether the decomposition node is graphic (binary) or network (ternary).
|
|
1028
|
+
If it is not determined, raise ValueError.
|
|
1029
|
+
"""
|
|
1030
|
+
cdef int8_t graphicness
|
|
1031
|
+
if self._dec != NULL:
|
|
1032
|
+
graphicness = CMRseymourGraphicness(self._dec)
|
|
1033
|
+
if graphicness:
|
|
1034
|
+
return graphicness > 0
|
|
1035
|
+
raise ValueError('It is not determined whether the decomposition node is graphic/network')
|
|
1036
|
+
|
|
1037
|
+
def _cographicness(self):
|
|
1038
|
+
r"""
|
|
1039
|
+
Return whether the decomposition node is cographic (binary) or conetwork (ternary).
|
|
1040
|
+
If it is not determined, raise ValueError.
|
|
1041
|
+
"""
|
|
1042
|
+
cdef int8_t cographicness
|
|
1043
|
+
if self._dec != NULL:
|
|
1044
|
+
cographicness = CMRseymourCographicness(self._dec)
|
|
1045
|
+
if cographicness:
|
|
1046
|
+
return cographicness > 0
|
|
1047
|
+
raise ValueError('It is not determined whether the decomposition node is cographic/conetwork')
|
|
1048
|
+
|
|
1049
|
+
def _is_binary_linear_matroid_graphic(self, *, decomposition=False, **kwds):
|
|
1050
|
+
r"""
|
|
1051
|
+
Return whether the linear matroid of ``self`` over `\GF{2}` is graphic.
|
|
1052
|
+
If there is some entry not in `\{0, 1\}`, return ``False``.
|
|
1053
|
+
|
|
1054
|
+
This method is based on Seymour's decomposition.
|
|
1055
|
+
The decomposition will stop once nongraphicness is detected.
|
|
1056
|
+
For direct graphicness check,
|
|
1057
|
+
|
|
1058
|
+
.. SEEALSO::
|
|
1059
|
+
|
|
1060
|
+
- :meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse._is_binary_linear_matroid_graphic`
|
|
1061
|
+
- :meth:`UnknownNode._is_binary_linear_matroid_graphic`
|
|
1062
|
+
- :meth:`_binary_linear_matroid_complete_decomposition`
|
|
1063
|
+
|
|
1064
|
+
EXAMPLES::
|
|
1065
|
+
|
|
1066
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1067
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
1068
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [1, 1], [0, 1]],
|
|
1069
|
+
....: [[1, 1, 0], [0, 1, 1]])
|
|
1070
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
1071
|
+
....: row_keys=range(5),
|
|
1072
|
+
....: column_keys='abcde')
|
|
1073
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1074
|
+
sage: node._graphicness()
|
|
1075
|
+
Traceback (most recent call last):
|
|
1076
|
+
...
|
|
1077
|
+
ValueError: It is not determined whether the decomposition node is graphic/network
|
|
1078
|
+
sage: result, decomposition = node._is_binary_linear_matroid_graphic(decomposition=True)
|
|
1079
|
+
sage: result
|
|
1080
|
+
True
|
|
1081
|
+
sage: unicode_art(decomposition)
|
|
1082
|
+
╭──────────OneSumNode (5×5) with 2 children
|
|
1083
|
+
│ │
|
|
1084
|
+
PlanarNode (3×2) PlanarNode (2×3)
|
|
1085
|
+
sage: decomposition._graphicness()
|
|
1086
|
+
True
|
|
1087
|
+
sage: node._is_binary_linear_matroid_graphic(certificate=True)
|
|
1088
|
+
Traceback (most recent call last):
|
|
1089
|
+
...
|
|
1090
|
+
NotImplementedError
|
|
1091
|
+
|
|
1092
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(GF(2), 9, 9, sparse=True),
|
|
1093
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1094
|
+
....: [1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1095
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1096
|
+
....: [0, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1097
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1098
|
+
....: [0, 0, 0, 0, 1, 1, 1, 0, 0],
|
|
1099
|
+
....: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
|
1100
|
+
....: [0, 0, 0, 0, 0, 1, 0, 1, 1],
|
|
1101
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1]])
|
|
1102
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True,
|
|
1103
|
+
....: row_keys=[f'r{i}' for i in range(9)],
|
|
1104
|
+
....: column_keys=[f'c{i}' for i in range(9)])
|
|
1105
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1106
|
+
sage: result, decomposition = node._is_binary_linear_matroid_graphic(decomposition=True)
|
|
1107
|
+
sage: result
|
|
1108
|
+
False
|
|
1109
|
+
sage: unicode_art(decomposition)
|
|
1110
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1111
|
+
│ │
|
|
1112
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1113
|
+
sage: decomposition.child_nodes()[1]._graphicness()
|
|
1114
|
+
False
|
|
1115
|
+
"""
|
|
1116
|
+
certificate = kwds.get('certificate', False)
|
|
1117
|
+
try:
|
|
1118
|
+
result = self._graphicness()
|
|
1119
|
+
if not decomposition and not certificate:
|
|
1120
|
+
return result
|
|
1121
|
+
result = [result]
|
|
1122
|
+
if decomposition:
|
|
1123
|
+
result.append(self)
|
|
1124
|
+
if certificate:
|
|
1125
|
+
raise NotImplementedError
|
|
1126
|
+
return result
|
|
1127
|
+
except ValueError:
|
|
1128
|
+
# compute it... wait for CMR functions
|
|
1129
|
+
full_dec = self._binary_linear_matroid_complete_decomposition(
|
|
1130
|
+
stop_when_nongraphic=True,
|
|
1131
|
+
check_graphic_minors_planar=True)
|
|
1132
|
+
return full_dec._is_binary_linear_matroid_graphic(
|
|
1133
|
+
decomposition=decomposition,
|
|
1134
|
+
certificate=certificate)
|
|
1135
|
+
|
|
1136
|
+
def _is_binary_linear_matroid_cographic(self, *, decomposition=False, **kwds):
|
|
1137
|
+
r"""
|
|
1138
|
+
Return whether the linear matroid of ``self`` over `\GF{2}` is cographic.
|
|
1139
|
+
If there is some entry not in `\{0, 1\}`, return ``False``.
|
|
1140
|
+
|
|
1141
|
+
This method is based on Seymour's decomposition.
|
|
1142
|
+
The decomposition will stop once noncographicness is detected.
|
|
1143
|
+
For direct cographicness check,
|
|
1144
|
+
|
|
1145
|
+
.. SEEALSO::
|
|
1146
|
+
|
|
1147
|
+
- :meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse._is_binary_linear_matroid_cographic`
|
|
1148
|
+
- :meth:`UnknownNode._is_binary_linear_matroid_cographic`
|
|
1149
|
+
- :meth:`_binary_linear_matroid_complete_decomposition`
|
|
1150
|
+
|
|
1151
|
+
EXAMPLES::
|
|
1152
|
+
|
|
1153
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1154
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
1155
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [1, 1], [0, 1]],
|
|
1156
|
+
....: [[1, 1, 0], [0, 1, 1]])
|
|
1157
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
1158
|
+
....: row_keys=range(5),
|
|
1159
|
+
....: column_keys='abcde')
|
|
1160
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1161
|
+
sage: node._cographicness()
|
|
1162
|
+
Traceback (most recent call last):
|
|
1163
|
+
...
|
|
1164
|
+
ValueError: It is not determined whether the decomposition node is cographic/conetwork
|
|
1165
|
+
sage: result, decomposition = node._is_binary_linear_matroid_cographic(decomposition=True)
|
|
1166
|
+
sage: result
|
|
1167
|
+
True
|
|
1168
|
+
sage: unicode_art(decomposition)
|
|
1169
|
+
╭──────────OneSumNode (5×5) with 2 children
|
|
1170
|
+
│ │
|
|
1171
|
+
PlanarNode (3×2) PlanarNode (2×3)
|
|
1172
|
+
sage: decomposition._cographicness()
|
|
1173
|
+
True
|
|
1174
|
+
sage: node._is_binary_linear_matroid_cographic(certificate=True)
|
|
1175
|
+
Traceback (most recent call last):
|
|
1176
|
+
...
|
|
1177
|
+
NotImplementedError
|
|
1178
|
+
|
|
1179
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(GF(2), 9, 9, sparse=True),
|
|
1180
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1181
|
+
....: [1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1182
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1183
|
+
....: [0, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1184
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1185
|
+
....: [0, 0, 0, 0, 1, 1, 1, 0, 0],
|
|
1186
|
+
....: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
|
1187
|
+
....: [0, 0, 0, 0, 0, 1, 0, 1, 1],
|
|
1188
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1]])
|
|
1189
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True,
|
|
1190
|
+
....: row_keys=[f'r{i}' for i in range(9)],
|
|
1191
|
+
....: column_keys=[f'c{i}' for i in range(9)])
|
|
1192
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1193
|
+
sage: result, decomposition = node._is_binary_linear_matroid_cographic(decomposition=True)
|
|
1194
|
+
sage: result
|
|
1195
|
+
False
|
|
1196
|
+
sage: unicode_art(decomposition)
|
|
1197
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1198
|
+
│ │
|
|
1199
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1200
|
+
"""
|
|
1201
|
+
certificate = kwds.get('certificate', False)
|
|
1202
|
+
try:
|
|
1203
|
+
result = self._cographicness()
|
|
1204
|
+
if not decomposition and not certificate:
|
|
1205
|
+
return result
|
|
1206
|
+
result = [result]
|
|
1207
|
+
if decomposition:
|
|
1208
|
+
result.append(self)
|
|
1209
|
+
if certificate:
|
|
1210
|
+
raise NotImplementedError
|
|
1211
|
+
return result
|
|
1212
|
+
except ValueError:
|
|
1213
|
+
# compute it... wait for CMR functions
|
|
1214
|
+
full_dec = self._binary_linear_matroid_complete_decomposition(
|
|
1215
|
+
stop_when_noncographic=True,
|
|
1216
|
+
check_graphic_minors_planar=True)
|
|
1217
|
+
return full_dec._is_binary_linear_matroid_cographic(
|
|
1218
|
+
decomposition=decomposition,
|
|
1219
|
+
certificate=certificate)
|
|
1220
|
+
|
|
1221
|
+
def _is_binary_linear_matroid_regular(self, *, decomposition=False, **kwds):
|
|
1222
|
+
r"""
|
|
1223
|
+
Return whether the linear matroid of ``self`` over `\GF{2}` is regular.
|
|
1224
|
+
If there is some entry not in `\{0, 1\}`, return ``False``.
|
|
1225
|
+
|
|
1226
|
+
.. SEEALSO::
|
|
1227
|
+
|
|
1228
|
+
- :meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse._is_binary_linear_matroid_regular`
|
|
1229
|
+
- :meth:`_binary_linear_matroid_complete_decomposition`
|
|
1230
|
+
|
|
1231
|
+
EXAMPLES::
|
|
1232
|
+
|
|
1233
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1234
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(GF(2), 9, 9, sparse=True),
|
|
1235
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1236
|
+
....: [1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1237
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1238
|
+
....: [0, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1239
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1240
|
+
....: [0, 0, 0, 0, 1, 1, 1, 0, 0],
|
|
1241
|
+
....: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
|
1242
|
+
....: [0, 0, 0, 0, 0, 1, 0, 1, 1],
|
|
1243
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1]])
|
|
1244
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
1245
|
+
sage: node = UnknownNode(M)
|
|
1246
|
+
sage: node._regularity()
|
|
1247
|
+
Traceback (most recent call last):
|
|
1248
|
+
...
|
|
1249
|
+
ValueError: It is not determined whether the decomposition node is regular/TU
|
|
1250
|
+
sage: node._is_binary_linear_matroid_regular()
|
|
1251
|
+
True
|
|
1252
|
+
sage: C0 = node._binary_linear_matroid_complete_decomposition()
|
|
1253
|
+
sage: C0
|
|
1254
|
+
OneSumNode (9×9) with 2 children
|
|
1255
|
+
sage: result, decomposition = C0._is_binary_linear_matroid_graphic(decomposition=True)
|
|
1256
|
+
sage: result
|
|
1257
|
+
False
|
|
1258
|
+
sage: unicode_art(decomposition)
|
|
1259
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1260
|
+
│ │
|
|
1261
|
+
GraphicNode (5×4) CographicNode (4×5)
|
|
1262
|
+
sage: decomposition.child_nodes()[1]._graphicness()
|
|
1263
|
+
False
|
|
1264
|
+
sage: result, decomposition = C0._is_binary_linear_matroid_cographic(decomposition=True)
|
|
1265
|
+
sage: result
|
|
1266
|
+
False
|
|
1267
|
+
sage: unicode_art(decomposition)
|
|
1268
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1269
|
+
│ │
|
|
1270
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1271
|
+
sage: decomposition.child_nodes()[1]._graphicness()
|
|
1272
|
+
Traceback (most recent call last):
|
|
1273
|
+
...
|
|
1274
|
+
ValueError: It is not determined whether the decomposition node is graphic/network
|
|
1275
|
+
sage: result, decomposition = C0._is_binary_linear_matroid_regular(decomposition=True)
|
|
1276
|
+
sage: result
|
|
1277
|
+
True
|
|
1278
|
+
sage: unicode_art(decomposition)
|
|
1279
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1280
|
+
│ │
|
|
1281
|
+
GraphicNode (5×4) CographicNode (4×5)
|
|
1282
|
+
"""
|
|
1283
|
+
certificate = kwds.get('certificate', False)
|
|
1284
|
+
try:
|
|
1285
|
+
result = self._regularity()
|
|
1286
|
+
if not decomposition and not certificate:
|
|
1287
|
+
return result
|
|
1288
|
+
result = [result]
|
|
1289
|
+
if decomposition:
|
|
1290
|
+
result.append(self)
|
|
1291
|
+
if certificate:
|
|
1292
|
+
raise NotImplementedError
|
|
1293
|
+
return result
|
|
1294
|
+
except ValueError:
|
|
1295
|
+
# compute it... wait for CMR functions
|
|
1296
|
+
full_dec = self._binary_linear_matroid_complete_decomposition(
|
|
1297
|
+
stop_when_irregular=True,
|
|
1298
|
+
check_graphic_minors_planar=True)
|
|
1299
|
+
return full_dec._is_binary_linear_matroid_regular(
|
|
1300
|
+
decomposition=decomposition,
|
|
1301
|
+
certificate=certificate)
|
|
1302
|
+
|
|
1303
|
+
def _binary_linear_matroid_complete_decomposition(self, *,
|
|
1304
|
+
time_limit=60.0,
|
|
1305
|
+
use_direct_graphicness_test=True,
|
|
1306
|
+
prefer_graphicness=True,
|
|
1307
|
+
series_parallel_ok=True,
|
|
1308
|
+
check_graphic_minors_planar=False,
|
|
1309
|
+
stop_when_irregular=False,
|
|
1310
|
+
stop_when_nongraphic=False,
|
|
1311
|
+
stop_when_noncographic=False,
|
|
1312
|
+
stop_when_nongraphic_and_noncographic=False,
|
|
1313
|
+
decompose_strategy='delta_three',
|
|
1314
|
+
construct_leaf_graphs=False,
|
|
1315
|
+
construct_all_graphs=False):
|
|
1316
|
+
r"""
|
|
1317
|
+
Complete the Seymour's decomposition of ``self`` over `\GF{2}`.
|
|
1318
|
+
|
|
1319
|
+
INPUT:
|
|
1320
|
+
|
|
1321
|
+
- ``stop_when_irregular`` -- boolean;
|
|
1322
|
+
whether to stop decomposing once irregularity is determined.
|
|
1323
|
+
|
|
1324
|
+
- ``stop_when_nongraphic`` -- boolean;
|
|
1325
|
+
whether to stop decomposing once non-graphicness is determined.
|
|
1326
|
+
|
|
1327
|
+
- ``stop_when_noncographic`` -- boolean;
|
|
1328
|
+
whether to stop decomposing once non-cographicness is determined.
|
|
1329
|
+
|
|
1330
|
+
- ``stop_when_nongraphic_and_noncographic`` -- boolean;
|
|
1331
|
+
whether to stop decomposing once non-graphicness and non-cographicness
|
|
1332
|
+
is determined.
|
|
1333
|
+
|
|
1334
|
+
For a description of other parameters, see
|
|
1335
|
+
:meth:`sage.matrix.matrix_cmr_sparse._set_cmr_seymour_parameters`
|
|
1336
|
+
|
|
1337
|
+
EXAMPLES::
|
|
1338
|
+
|
|
1339
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1340
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(GF(2), 9, 9, sparse=True),
|
|
1341
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1342
|
+
....: [1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1343
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1344
|
+
....: [0, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1345
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1346
|
+
....: [0, 0, 0, 0, 1, 1, 1, 0, 0],
|
|
1347
|
+
....: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
|
1348
|
+
....: [0, 0, 0, 0, 0, 1, 0, 1, 1],
|
|
1349
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1]])
|
|
1350
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
1351
|
+
sage: node = UnknownNode(M); node
|
|
1352
|
+
UnknownNode (9×9)
|
|
1353
|
+
sage: C0 = node._binary_linear_matroid_complete_decomposition()
|
|
1354
|
+
sage: C0
|
|
1355
|
+
OneSumNode (9×9) with 2 children
|
|
1356
|
+
sage: unicode_art(C0)
|
|
1357
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1358
|
+
│ │
|
|
1359
|
+
GraphicNode (5×4) CographicNode (4×5)
|
|
1360
|
+
sage: C1, C2 = C0.child_nodes()
|
|
1361
|
+
sage: C11 = C1._binary_linear_matroid_complete_decomposition(); C11
|
|
1362
|
+
GraphicNode (5×4)
|
|
1363
|
+
sage: unicode_art(C11)
|
|
1364
|
+
GraphicNode (5×4)
|
|
1365
|
+
sage: C1.matrix()
|
|
1366
|
+
[1 1 0 0]
|
|
1367
|
+
[1 1 1 0]
|
|
1368
|
+
[0 1 1 1]
|
|
1369
|
+
[1 0 0 1]
|
|
1370
|
+
[0 0 1 1]
|
|
1371
|
+
sage: C11.matrix()
|
|
1372
|
+
[1 1 0 0]
|
|
1373
|
+
[1 1 1 0]
|
|
1374
|
+
[0 1 1 1]
|
|
1375
|
+
[1 0 0 1]
|
|
1376
|
+
[0 0 1 1]
|
|
1377
|
+
sage: C22 = C2._binary_linear_matroid_complete_decomposition(); C22
|
|
1378
|
+
CographicNode (4×5)
|
|
1379
|
+
sage: unicode_art(C22)
|
|
1380
|
+
CographicNode (4×5)
|
|
1381
|
+
sage: C2.matrix()
|
|
1382
|
+
[1 1 1 0 0]
|
|
1383
|
+
[0 0 1 1 1]
|
|
1384
|
+
[0 1 0 1 1]
|
|
1385
|
+
[1 1 0 1 0]
|
|
1386
|
+
sage: C22.matrix()
|
|
1387
|
+
[1 1 1 0 0]
|
|
1388
|
+
[0 0 1 1 1]
|
|
1389
|
+
[0 1 0 1 1]
|
|
1390
|
+
[1 1 0 1 0]
|
|
1391
|
+
|
|
1392
|
+
This is test ``TreeFlagsStopNoncographic`` in CMR's ``test_regular.cpp``.
|
|
1393
|
+
The default settings will not do the planarity check::
|
|
1394
|
+
|
|
1395
|
+
sage: unicode_art(node)
|
|
1396
|
+
UnknownNode (9×9)
|
|
1397
|
+
sage: certificate1 = node._binary_linear_matroid_complete_decomposition(
|
|
1398
|
+
....: stop_when_noncographic=True,
|
|
1399
|
+
....: check_graphic_minors_planar=True)
|
|
1400
|
+
sage: unicode_art(certificate1)
|
|
1401
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1402
|
+
│ │
|
|
1403
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1404
|
+
sage: certificate2 = node._binary_linear_matroid_complete_decomposition(
|
|
1405
|
+
....: stop_when_noncographic=True)
|
|
1406
|
+
sage: unicode_art(certificate2)
|
|
1407
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1408
|
+
│ │
|
|
1409
|
+
GraphicNode (5×4) CographicNode (4×5)
|
|
1410
|
+
|
|
1411
|
+
sage: certificate1 = node._binary_linear_matroid_complete_decomposition(
|
|
1412
|
+
....: stop_when_nongraphic=True,
|
|
1413
|
+
....: check_graphic_minors_planar=True)
|
|
1414
|
+
sage: unicode_art(certificate1)
|
|
1415
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1416
|
+
│ │
|
|
1417
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1418
|
+
sage: C1, C2 = certificate1.child_nodes()
|
|
1419
|
+
sage: C1._cographicness()
|
|
1420
|
+
False
|
|
1421
|
+
sage: C2._graphicness()
|
|
1422
|
+
False
|
|
1423
|
+
sage: certificate2 = node._binary_linear_matroid_complete_decomposition(
|
|
1424
|
+
....: stop_when_nongraphic=True)
|
|
1425
|
+
sage: unicode_art(certificate2)
|
|
1426
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1427
|
+
│ │
|
|
1428
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1429
|
+
sage: C1, C2 = certificate2.child_nodes()
|
|
1430
|
+
sage: C1._cographicness()
|
|
1431
|
+
Traceback (most recent call last):
|
|
1432
|
+
...
|
|
1433
|
+
ValueError: It is not determined whether the decomposition node is cographic/conetwork
|
|
1434
|
+
sage: C2._graphicness()
|
|
1435
|
+
False
|
|
1436
|
+
"""
|
|
1437
|
+
cdef CMR_REGULAR_PARAMS params
|
|
1438
|
+
cdef CMR_REGULAR_STATS stats
|
|
1439
|
+
cdef CMR_SEYMOUR_NODE *clone = NULL
|
|
1440
|
+
|
|
1441
|
+
cdef CMR_SEYMOUR_NODE **pclone = &clone
|
|
1442
|
+
|
|
1443
|
+
if self._dec == NULL:
|
|
1444
|
+
base_ring = self.base_ring()
|
|
1445
|
+
if base_ring is None:
|
|
1446
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
|
1447
|
+
self._base_ring = GF(2)
|
|
1448
|
+
elif base_ring.characteristic() != 2:
|
|
1449
|
+
raise ValueError(f'only defined over binary, got {base_ring}')
|
|
1450
|
+
self._set_root_dec()
|
|
1451
|
+
|
|
1452
|
+
cdef dict kwds = dict(use_direct_graphicness_test=use_direct_graphicness_test,
|
|
1453
|
+
prefer_graphicness=prefer_graphicness,
|
|
1454
|
+
series_parallel_ok=series_parallel_ok,
|
|
1455
|
+
check_graphic_minors_planar=check_graphic_minors_planar,
|
|
1456
|
+
stop_when_irregular=stop_when_irregular,
|
|
1457
|
+
stop_when_nongraphic=stop_when_nongraphic,
|
|
1458
|
+
stop_when_noncographic=stop_when_noncographic,
|
|
1459
|
+
stop_when_nongraphic_and_noncographic=stop_when_nongraphic_and_noncographic,
|
|
1460
|
+
decompose_strategy=decompose_strategy,
|
|
1461
|
+
construct_leaf_graphs=construct_leaf_graphs,
|
|
1462
|
+
construct_all_graphs=construct_all_graphs)
|
|
1463
|
+
_set_cmr_seymour_parameters(¶ms.seymour, kwds)
|
|
1464
|
+
|
|
1465
|
+
sig_on()
|
|
1466
|
+
try:
|
|
1467
|
+
CMR_CALL(CMRseymourCloneUnknown(cmr, self._dec, pclone))
|
|
1468
|
+
CMR_CALL(CMRregularCompleteDecomposition(cmr, clone, ¶ms, &stats, time_limit))
|
|
1469
|
+
finally:
|
|
1470
|
+
sig_off()
|
|
1471
|
+
node = create_DecompositionNode(clone, matrix=self.matrix(),
|
|
1472
|
+
row_keys=self.row_keys(),
|
|
1473
|
+
column_keys=self.column_keys(),
|
|
1474
|
+
base_ring=self.base_ring())
|
|
1475
|
+
return node
|
|
1476
|
+
|
|
1477
|
+
def is_network_matrix(self, *, decomposition=False, **kwds):
|
|
1478
|
+
r"""
|
|
1479
|
+
Return whether the matrix ``self`` over `\GF{3}` or `\QQ` is a network matrix.
|
|
1480
|
+
If there is some entry not in `\{-1, 0, 1\}`, return ``False``.
|
|
1481
|
+
|
|
1482
|
+
This method is based on Seymour's decomposition.
|
|
1483
|
+
The decomposition will stop once being nonnetwork is detected.
|
|
1484
|
+
For direct network matrix check,
|
|
1485
|
+
|
|
1486
|
+
.. SEEALSO::
|
|
1487
|
+
|
|
1488
|
+
- :meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.is_network_matrix`
|
|
1489
|
+
- :meth:`UnknownNode.is_network_matrix`
|
|
1490
|
+
- :meth:`complete_decomposition`
|
|
1491
|
+
|
|
1492
|
+
EXAMPLES::
|
|
1493
|
+
|
|
1494
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1495
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
1496
|
+
sage: A = matrix(ZZ, [[-1, 0, 0, 0, 1,-1, 0],
|
|
1497
|
+
....: [ 1, 0, 0, 1,-1, 1, 0],
|
|
1498
|
+
....: [ 0,-1, 0,-1, 1,-1, 0],
|
|
1499
|
+
....: [ 0, 1, 0, 0, 0, 0, 1],
|
|
1500
|
+
....: [ 0, 0, 1,-1, 1, 0, 1],
|
|
1501
|
+
....: [ 0, 0,-1, 1,-1, 0, 0]])
|
|
1502
|
+
sage: M1 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 7, sparse=True), A)
|
|
1503
|
+
sage: M2 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 7, 6, sparse=True), A.transpose())
|
|
1504
|
+
sage: M = Matrix_cmr_chr_sparse.one_sum(M1, M2)
|
|
1505
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
1506
|
+
....: row_keys=[f'r{i}' for i in range(13)],
|
|
1507
|
+
....: column_keys=[f'c{i}' for i in range(13)])
|
|
1508
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1509
|
+
|
|
1510
|
+
sage: result, decomposition = node.is_network_matrix(decomposition=True)
|
|
1511
|
+
sage: unicode_art(decomposition)
|
|
1512
|
+
╭─────────OneSumNode (13×13) with 2 children
|
|
1513
|
+
│ │
|
|
1514
|
+
PlanarNode (6×7) PlanarNode (7×6)
|
|
1515
|
+
sage: node._graphicness()
|
|
1516
|
+
Traceback (most recent call last):
|
|
1517
|
+
...
|
|
1518
|
+
ValueError: It is not determined whether the decomposition node is graphic/network
|
|
1519
|
+
|
|
1520
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 9, sparse=True),
|
|
1521
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1522
|
+
....: [-1,-1, 1, 0, 0, 0, 0, 0, 0],
|
|
1523
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1524
|
+
....: [0, 1,-1,-1, 0, 0, 0, 0, 0],
|
|
1525
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1526
|
+
....: [0, 0, 0, 0, 1,-1, 1, 0, 0],
|
|
1527
|
+
....: [0, 0, 0, 0, 1,-1, 0, 1, 0],
|
|
1528
|
+
....: [0, 0, 0, 0, 0, 1, 0,-1, 1],
|
|
1529
|
+
....: [0, 0, 0, 0, 0, 0, 1,-1, 1]])
|
|
1530
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
1531
|
+
....: row_keys=[f'r{i}' for i in range(9)],
|
|
1532
|
+
....: column_keys=[f'c{i}' for i in range(9)])
|
|
1533
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1534
|
+
sage: result, decomposition = node.is_network_matrix(decomposition=True)
|
|
1535
|
+
sage: result
|
|
1536
|
+
False
|
|
1537
|
+
sage: unicode_art(decomposition)
|
|
1538
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1539
|
+
│ │
|
|
1540
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1541
|
+
sage: decomposition.child_nodes()[1]._graphicness()
|
|
1542
|
+
False
|
|
1543
|
+
"""
|
|
1544
|
+
certificate = kwds.get('certificate', False)
|
|
1545
|
+
try:
|
|
1546
|
+
result = self._graphicness()
|
|
1547
|
+
if not decomposition and not certificate:
|
|
1548
|
+
return result
|
|
1549
|
+
result = [result]
|
|
1550
|
+
if decomposition:
|
|
1551
|
+
result.append(self)
|
|
1552
|
+
if certificate:
|
|
1553
|
+
raise NotImplementedError
|
|
1554
|
+
return result
|
|
1555
|
+
except ValueError:
|
|
1556
|
+
# compute it... wait for CMR functions
|
|
1557
|
+
full_dec = self.complete_decomposition(
|
|
1558
|
+
stop_when_nonnetwork=True,
|
|
1559
|
+
check_graphic_minors_planar=True)
|
|
1560
|
+
return full_dec.is_network_matrix(
|
|
1561
|
+
decomposition=decomposition,
|
|
1562
|
+
certificate=certificate)
|
|
1563
|
+
|
|
1564
|
+
def is_conetwork_matrix(self, *, decomposition=False, **kwds):
|
|
1565
|
+
r"""
|
|
1566
|
+
Return whether the matrix ``self`` over `\GF{3}` or `\QQ` is a conetwork matrix.
|
|
1567
|
+
If there is some entry not in `\{-1, 0, 1\}`, return ``False``.
|
|
1568
|
+
|
|
1569
|
+
This method is based on Seymour's decomposition.
|
|
1570
|
+
The decomposition will stop once being nonconetwork is detected.
|
|
1571
|
+
For direct conetwork matrix check,
|
|
1572
|
+
|
|
1573
|
+
.. SEEALSO::
|
|
1574
|
+
|
|
1575
|
+
- :meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.is_conetwork_matrix`
|
|
1576
|
+
- :meth:`UnknownNode.is_conetwork_matrix`
|
|
1577
|
+
- :meth:`complete_decomposition`
|
|
1578
|
+
|
|
1579
|
+
EXAMPLES::
|
|
1580
|
+
|
|
1581
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1582
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
1583
|
+
sage: A = matrix(ZZ, [[-1, 0, 0, 0, 1,-1, 0],
|
|
1584
|
+
....: [ 1, 0, 0, 1,-1, 1, 0],
|
|
1585
|
+
....: [ 0,-1, 0,-1, 1,-1, 0],
|
|
1586
|
+
....: [ 0, 1, 0, 0, 0, 0, 1],
|
|
1587
|
+
....: [ 0, 0, 1,-1, 1, 0, 1],
|
|
1588
|
+
....: [ 0, 0,-1, 1,-1, 0, 0]])
|
|
1589
|
+
sage: M1 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 7, sparse=True), A)
|
|
1590
|
+
sage: M2 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 7, 6, sparse=True), A.transpose())
|
|
1591
|
+
sage: M = Matrix_cmr_chr_sparse.one_sum(M1, M2)
|
|
1592
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
1593
|
+
....: row_keys=[f'r{i}' for i in range(13)],
|
|
1594
|
+
....: column_keys=[f'c{i}' for i in range(13)])
|
|
1595
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1596
|
+
|
|
1597
|
+
sage: result, decomposition = node.is_conetwork_matrix(decomposition=True)
|
|
1598
|
+
sage: unicode_art(decomposition)
|
|
1599
|
+
╭─────────OneSumNode (13×13) with 2 children
|
|
1600
|
+
│ │
|
|
1601
|
+
PlanarNode (6×7) PlanarNode (7×6)
|
|
1602
|
+
sage: node._cographicness()
|
|
1603
|
+
Traceback (most recent call last):
|
|
1604
|
+
...
|
|
1605
|
+
ValueError: It is not determined whether the decomposition node is cographic/conetwork
|
|
1606
|
+
|
|
1607
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 9, sparse=True),
|
|
1608
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1609
|
+
....: [-1,-1, 1, 0, 0, 0, 0, 0, 0],
|
|
1610
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1611
|
+
....: [0, 1,-1,-1, 0, 0, 0, 0, 0],
|
|
1612
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1613
|
+
....: [0, 0, 0, 0, 1,-1, 1, 0, 0],
|
|
1614
|
+
....: [0, 0, 0, 0, 1,-1, 0, 1, 0],
|
|
1615
|
+
....: [0, 0, 0, 0, 0, 1, 0,-1, 1],
|
|
1616
|
+
....: [0, 0, 0, 0, 0, 0, 1,-1, 1]])
|
|
1617
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
1618
|
+
....: row_keys=[f'r{i}' for i in range(9)],
|
|
1619
|
+
....: column_keys=[f'c{i}' for i in range(9)])
|
|
1620
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1621
|
+
sage: result, decomposition = node.is_conetwork_matrix(decomposition=True)
|
|
1622
|
+
sage: result
|
|
1623
|
+
False
|
|
1624
|
+
sage: unicode_art(decomposition)
|
|
1625
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1626
|
+
│ │
|
|
1627
|
+
GraphicNode (5×4) UnknownNode (4×5)
|
|
1628
|
+
sage: decomposition.child_nodes()[1]._graphicness()
|
|
1629
|
+
Traceback (most recent call last):
|
|
1630
|
+
...
|
|
1631
|
+
ValueError: It is not determined whether the decomposition node is graphic/network
|
|
1632
|
+
"""
|
|
1633
|
+
certificate = kwds.get('certificate', False)
|
|
1634
|
+
try:
|
|
1635
|
+
result = self._cographicness()
|
|
1636
|
+
if not decomposition and not certificate:
|
|
1637
|
+
return result
|
|
1638
|
+
result = [result]
|
|
1639
|
+
if decomposition:
|
|
1640
|
+
result.append(self)
|
|
1641
|
+
if certificate:
|
|
1642
|
+
raise NotImplementedError
|
|
1643
|
+
return result
|
|
1644
|
+
except ValueError:
|
|
1645
|
+
# compute it... wait for CMR functions
|
|
1646
|
+
full_dec = self.complete_decomposition(
|
|
1647
|
+
stop_when_nonconetwork=True,
|
|
1648
|
+
check_graphic_minors_planar=True)
|
|
1649
|
+
return full_dec.is_conetwork_matrix(
|
|
1650
|
+
decomposition=decomposition,
|
|
1651
|
+
certificate=certificate)
|
|
1652
|
+
|
|
1653
|
+
def is_totally_unimodular(self, *, decomposition=False, **kwds):
|
|
1654
|
+
r"""
|
|
1655
|
+
Return whether ``self`` is a totally unimodular matrix.
|
|
1656
|
+
|
|
1657
|
+
A matrix is totally unimodular if every subdeterminant is `0`, `1`, or `-1`.
|
|
1658
|
+
|
|
1659
|
+
.. SEEALSO::
|
|
1660
|
+
|
|
1661
|
+
- :meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.is_totally_unimodular`
|
|
1662
|
+
- :meth:`complete_decomposition`
|
|
1663
|
+
|
|
1664
|
+
EXAMPLES::
|
|
1665
|
+
|
|
1666
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1667
|
+
sage: from sage.matrix.seymour_decomposition import DecompositionNode
|
|
1668
|
+
sage: A = matrix(ZZ, [[-1, 0, 0, 0, 1,-1, 0],
|
|
1669
|
+
....: [ 1, 0, 0, 1,-1, 1, 0],
|
|
1670
|
+
....: [ 0,-1, 0,-1, 1,-1, 0],
|
|
1671
|
+
....: [ 0, 1, 0, 0, 0, 0, 1],
|
|
1672
|
+
....: [ 0, 0, 1,-1, 1, 0, 1],
|
|
1673
|
+
....: [ 0, 0,-1, 1,-1, 0, 0]])
|
|
1674
|
+
sage: M1 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 7, sparse=True), A)
|
|
1675
|
+
sage: M2 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 7, 6, sparse=True), A.transpose())
|
|
1676
|
+
sage: M = Matrix_cmr_chr_sparse.one_sum(M1, M2)
|
|
1677
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
1678
|
+
....: row_keys=[f'r{i}' for i in range(13)],
|
|
1679
|
+
....: column_keys=[f'c{i}' for i in range(13)])
|
|
1680
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1681
|
+
|
|
1682
|
+
sage: result, decomposition = node.is_totally_unimodular(decomposition=True)
|
|
1683
|
+
sage: unicode_art(decomposition)
|
|
1684
|
+
╭─────────OneSumNode (13×13) with 2 children
|
|
1685
|
+
│ │
|
|
1686
|
+
PlanarNode (6×7) PlanarNode (7×6)
|
|
1687
|
+
sage: node._regularity()
|
|
1688
|
+
Traceback (most recent call last):
|
|
1689
|
+
...
|
|
1690
|
+
ValueError: It is not determined whether the decomposition node is regular/TU
|
|
1691
|
+
|
|
1692
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 9, sparse=True),
|
|
1693
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1694
|
+
....: [-1,-1, 1, 0, 0, 0, 0, 0, 0],
|
|
1695
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1696
|
+
....: [0, 1,-1,-1, 0, 0, 0, 0, 0],
|
|
1697
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1698
|
+
....: [0, 0, 0, 0, 1,-1, 1, 0, 0],
|
|
1699
|
+
....: [0, 0, 0, 0, 1,-1, 0, 1, 0],
|
|
1700
|
+
....: [0, 0, 0, 0, 0, 1, 0,-1, 1],
|
|
1701
|
+
....: [0, 0, 0, 0, 0, 0, 1,-1, 1]])
|
|
1702
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
1703
|
+
....: row_keys=[f'r{i}' for i in range(9)],
|
|
1704
|
+
....: column_keys=[f'c{i}' for i in range(9)])
|
|
1705
|
+
sage: node = DecompositionNode.one_sum(*certificate.child_nodes())
|
|
1706
|
+
sage: result, decomposition = node.is_totally_unimodular(decomposition=True)
|
|
1707
|
+
sage: result
|
|
1708
|
+
True
|
|
1709
|
+
sage: unicode_art(decomposition)
|
|
1710
|
+
╭───────────OneSumNode (9×9) with 2 children
|
|
1711
|
+
│ │
|
|
1712
|
+
GraphicNode (5×4) CographicNode (4×5)
|
|
1713
|
+
"""
|
|
1714
|
+
certificate = kwds.get('certificate', False)
|
|
1715
|
+
try:
|
|
1716
|
+
result = self._regularity()
|
|
1717
|
+
if not decomposition and not certificate:
|
|
1718
|
+
return result
|
|
1719
|
+
result = [result]
|
|
1720
|
+
if decomposition:
|
|
1721
|
+
result.append(self)
|
|
1722
|
+
if certificate:
|
|
1723
|
+
raise NotImplementedError
|
|
1724
|
+
return result
|
|
1725
|
+
except ValueError:
|
|
1726
|
+
# compute it... wait for CMR functions
|
|
1727
|
+
full_dec = self.complete_decomposition(
|
|
1728
|
+
stop_when_nonTU=True,
|
|
1729
|
+
check_graphic_minors_planar=True)
|
|
1730
|
+
return full_dec.is_totally_unimodular(
|
|
1731
|
+
decomposition=decomposition,
|
|
1732
|
+
certificate=certificate)
|
|
1733
|
+
|
|
1734
|
+
def nminors(self):
|
|
1735
|
+
r"""
|
|
1736
|
+
Return the number of minors of the node.
|
|
1737
|
+
"""
|
|
1738
|
+
if self._minors is not None:
|
|
1739
|
+
return len(self._minors)
|
|
1740
|
+
if self._dec == NULL:
|
|
1741
|
+
return 0
|
|
1742
|
+
return CMRseymourNumMinors(self._dec)
|
|
1743
|
+
|
|
1744
|
+
def _create_minor(self, index):
|
|
1745
|
+
r"""
|
|
1746
|
+
A minor of a represented matroid is another one obtained
|
|
1747
|
+
by deleting or contracting elements. In the reduced matrix representation,
|
|
1748
|
+
an element associated with a row (resp. column) can be contracted (resp. deleted)
|
|
1749
|
+
by removing the corresponding matrix row (resp. column).
|
|
1750
|
+
In order to delete a row element or contract a column element,
|
|
1751
|
+
one must first pivot such that the element type (row/column) is changed.
|
|
1752
|
+
|
|
1753
|
+
A minor of a matroid represented by matrix `M` is represented
|
|
1754
|
+
by means of a ``CMR_MINOR`` object.
|
|
1755
|
+
It consists of a (potentially empty) array of pivots and a ``CMR_SUBMAT`` object
|
|
1756
|
+
indicating a submatrix `M'` of the matrix obtained from `M` after applying the pivots.
|
|
1757
|
+
Moreover, the type field indicates a certain structure of `M'`:
|
|
1758
|
+
|
|
1759
|
+
- A determinant ``CMR_MINOR_TYPE_DETERMINANT``
|
|
1760
|
+
indicates that `M'` is a submatrix of `M` with `|\det(M')| \geq 2`.
|
|
1761
|
+
In particular, no pivots are applied.
|
|
1762
|
+
- A Fano ``CMR_MINOR_TYPE_FANO``
|
|
1763
|
+
indicates that `M'` represents the Fano matroid `F_7`.
|
|
1764
|
+
- A Fano-dual ``CMR_MINOR_TYPE_FANO_DUAL``
|
|
1765
|
+
indicates that `M'` represents the dual `F_7^\star` of the Fano matroid.
|
|
1766
|
+
- A K5 ``CMR_MINOR_TYPE_K5``
|
|
1767
|
+
indicates that `M'` represents the graphic matroid `M(K_5)` of the complete graph `K_5`.
|
|
1768
|
+
- A K5-dual ``CMR_MINOR_TYPE_K5_DUAL``
|
|
1769
|
+
indicates that `M'` represents the dual matroid `M(K_5)^\star` of the complete graph `K_5`.
|
|
1770
|
+
- A K33 ``CMR_MINOR_TYPE_K33``
|
|
1771
|
+
indicates that `M'` represents the graphic matroid `M(K_{3,3})`
|
|
1772
|
+
of the complete bipartite graph `K_{3,3}`.
|
|
1773
|
+
- A K33-dual ``CMR_MINOR_TYPE_K33_DUAL``
|
|
1774
|
+
indicates that `M'` represents the dual matroid `M(K_{3,3})^\star`
|
|
1775
|
+
of the complete bipartite graph `K_{3,3}`.
|
|
1776
|
+
"""
|
|
1777
|
+
cdef CMR_MINOR * minor = CMRseymourMinor(self._dec, index)
|
|
1778
|
+
cdef CMR_MINOR_TYPE typ = CMRminorType(minor)
|
|
1779
|
+
cdef size_t npivots = CMRminorNumPivots(minor)
|
|
1780
|
+
cdef size_t *pivot_rows = CMRminorPivotRows(minor)
|
|
1781
|
+
cdef size_t *pivot_columns = CMRminorPivotColumns(minor)
|
|
1782
|
+
cdef CMR_SUBMAT *submat = CMRminorSubmatrix(minor)
|
|
1783
|
+
# TODO: consider the pivots information and the corresponding keys?
|
|
1784
|
+
pivots_tuple = tuple((pivot_rows[i], pivot_columns[i]) for i in range(npivots))
|
|
1785
|
+
submat_tuple = (tuple(submat.rows[i] for i in range(submat.numRows)),
|
|
1786
|
+
tuple(submat.columns[i] for i in range(submat.numColumns)))
|
|
1787
|
+
import sage.matroids.matroids_catalog as matroids
|
|
1788
|
+
from sage.graphs.graph_generators import graphs
|
|
1789
|
+
from sage.matroids.matroid import Matroid
|
|
1790
|
+
|
|
1791
|
+
if typ == CMR_MINOR_TYPE_FANO:
|
|
1792
|
+
return matroids.catalog.Fano()
|
|
1793
|
+
if typ == CMR_MINOR_TYPE_FANO_DUAL:
|
|
1794
|
+
return matroids.catalog.Fano().dual()
|
|
1795
|
+
if typ == CMR_MINOR_TYPE_K5:
|
|
1796
|
+
return matroids.CompleteGraphic(5)
|
|
1797
|
+
if typ == CMR_MINOR_TYPE_K5_DUAL:
|
|
1798
|
+
return matroids.CompleteGraphic(5).dual()
|
|
1799
|
+
if typ == CMR_MINOR_TYPE_K33:
|
|
1800
|
+
E = 'abcdefghi'
|
|
1801
|
+
G = graphs.CompleteBipartiteGraph(3, 3)
|
|
1802
|
+
return Matroid(groundset=E, graph=G, regular=True)
|
|
1803
|
+
if typ == CMR_MINOR_TYPE_K33_DUAL:
|
|
1804
|
+
return matroids.catalog.K33dual()
|
|
1805
|
+
if typ == CMR_MINOR_TYPE_DETERMINANT:
|
|
1806
|
+
return '|det| = 2 submatrix', submat_tuple
|
|
1807
|
+
if typ == CMR_MINOR_TYPE_ENTRY:
|
|
1808
|
+
return 'bad entry'
|
|
1809
|
+
if typ == CMR_MINOR_TYPE_CUSTOM:
|
|
1810
|
+
return 'custom'
|
|
1811
|
+
|
|
1812
|
+
def minors(self):
|
|
1813
|
+
r"""
|
|
1814
|
+
Return a tuple of minors of ``self``.
|
|
1815
|
+
|
|
1816
|
+
Currently, it only handles determinant 2 submatrix.
|
|
1817
|
+
|
|
1818
|
+
EXAMPLES::
|
|
1819
|
+
|
|
1820
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1821
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 9, sparse=True),
|
|
1822
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1823
|
+
....: [1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1824
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1825
|
+
....: [0, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1826
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1827
|
+
....: [0, 0, 0, 0, 1, 1, 1, 0, 0],
|
|
1828
|
+
....: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
|
1829
|
+
....: [0, 0, 0, 0, 0, 1, 0, 1, 1],
|
|
1830
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1]])
|
|
1831
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
1832
|
+
sage: C0 = UnknownNode(M).complete_decomposition()
|
|
1833
|
+
sage: C1, C2 = C0.child_nodes()
|
|
1834
|
+
sage: C1.minors()
|
|
1835
|
+
(('|det| = 2 submatrix', ((4, 3, 1), (2, 3, 0))),)
|
|
1836
|
+
sage: C2.minors()
|
|
1837
|
+
(('|det| = 2 submatrix', ((2, 1, 0), (4, 2, 1))),)
|
|
1838
|
+
"""
|
|
1839
|
+
if self._minors is not None:
|
|
1840
|
+
return self._minors
|
|
1841
|
+
minors_tuple = tuple(self._create_minor(index)
|
|
1842
|
+
for index in range(self.nminors()))
|
|
1843
|
+
self._minors = minors_tuple
|
|
1844
|
+
return self._minors
|
|
1845
|
+
|
|
1846
|
+
def complete_decomposition(self, *, time_limit=60.0,
|
|
1847
|
+
use_direct_graphicness_test=True,
|
|
1848
|
+
prefer_graphicness=True,
|
|
1849
|
+
series_parallel_ok=True,
|
|
1850
|
+
check_graphic_minors_planar=False,
|
|
1851
|
+
stop_when_nonTU=False,
|
|
1852
|
+
stop_when_nonnetwork=False,
|
|
1853
|
+
stop_when_nonconetwork=False,
|
|
1854
|
+
stop_when_nonnetwork_and_nonconetwork=False,
|
|
1855
|
+
decompose_strategy='delta_three',
|
|
1856
|
+
construct_leaf_graphs=False,
|
|
1857
|
+
construct_all_graphs=False):
|
|
1858
|
+
r"""
|
|
1859
|
+
Complete the Seymour's decomposition of ``self`` over `\GF{3}` or `\ZZ`.
|
|
1860
|
+
|
|
1861
|
+
INPUT:
|
|
1862
|
+
|
|
1863
|
+
- ``stop_when_nonTU`` -- boolean;
|
|
1864
|
+
whether to stop decomposing once being non-TU is determined.
|
|
1865
|
+
|
|
1866
|
+
- ``stop_when_nonnetwork`` -- boolean;
|
|
1867
|
+
whether to stop decomposing once being non-network is determined.
|
|
1868
|
+
|
|
1869
|
+
- ``stop_when_nonconetwork`` -- boolean;
|
|
1870
|
+
whether to stop decomposing once being non-conetwork is determined.
|
|
1871
|
+
|
|
1872
|
+
- ``stop_when_nonnetwork_and_nonconetwork`` -- boolean;
|
|
1873
|
+
whether to stop decomposing once not being network
|
|
1874
|
+
and not being conetwork is determined.
|
|
1875
|
+
|
|
1876
|
+
For a description of other parameters, see
|
|
1877
|
+
:meth:`sage.matrix.matrix_cmr_sparse._set_cmr_seymour_parameters`.
|
|
1878
|
+
|
|
1879
|
+
EXAMPLES::
|
|
1880
|
+
|
|
1881
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
1882
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 9, sparse=True),
|
|
1883
|
+
....: [[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1884
|
+
....: [1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1885
|
+
....: [1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1886
|
+
....: [0, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1887
|
+
....: [0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1888
|
+
....: [0, 0, 0, 0, 1, 1, 1, 0, 0],
|
|
1889
|
+
....: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
|
1890
|
+
....: [0, 0, 0, 0, 0, 1, 0, 1, 1],
|
|
1891
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1]])
|
|
1892
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
1893
|
+
sage: node = UnknownNode(M); node
|
|
1894
|
+
UnknownNode (9×9)
|
|
1895
|
+
sage: C0 = node.complete_decomposition()
|
|
1896
|
+
sage: unicode_art(C0)
|
|
1897
|
+
╭OneSumNode (9×9) with 2 children─╮
|
|
1898
|
+
│ │
|
|
1899
|
+
ThreeConnectedIrregularNode (5×4) ThreeConnectedIrregularNode (4×5)
|
|
1900
|
+
sage: C1, C2 = C0.child_nodes()
|
|
1901
|
+
sage: C11 = C1.complete_decomposition(); C11
|
|
1902
|
+
ThreeConnectedIrregularNode (5×4)
|
|
1903
|
+
sage: unicode_art(C11)
|
|
1904
|
+
ThreeConnectedIrregularNode (5×4)
|
|
1905
|
+
sage: C1.matrix()
|
|
1906
|
+
[1 1 0 0]
|
|
1907
|
+
[1 1 1 0]
|
|
1908
|
+
[0 1 1 1]
|
|
1909
|
+
[1 0 0 1]
|
|
1910
|
+
[0 0 1 1]
|
|
1911
|
+
sage: C11.matrix()
|
|
1912
|
+
[1 1 0 0]
|
|
1913
|
+
[1 1 1 0]
|
|
1914
|
+
[0 1 1 1]
|
|
1915
|
+
[1 0 0 1]
|
|
1916
|
+
[0 0 1 1]
|
|
1917
|
+
sage: C22 = C2.complete_decomposition(); C22
|
|
1918
|
+
ThreeConnectedIrregularNode (4×5)
|
|
1919
|
+
sage: unicode_art(C22)
|
|
1920
|
+
ThreeConnectedIrregularNode (4×5)
|
|
1921
|
+
sage: C2.matrix()
|
|
1922
|
+
[1 1 1 0 0]
|
|
1923
|
+
[0 0 1 1 1]
|
|
1924
|
+
[0 1 0 1 1]
|
|
1925
|
+
[1 1 0 1 0]
|
|
1926
|
+
sage: C22.matrix()
|
|
1927
|
+
[1 1 1 0 0]
|
|
1928
|
+
[0 0 1 1 1]
|
|
1929
|
+
[0 1 0 1 1]
|
|
1930
|
+
[1 1 0 1 0]
|
|
1931
|
+
"""
|
|
1932
|
+
cdef CMR_TU_PARAMS params
|
|
1933
|
+
cdef CMR_TU_STATS stats
|
|
1934
|
+
cdef CMR_SEYMOUR_NODE *clone = NULL
|
|
1935
|
+
|
|
1936
|
+
cdef CMR_SEYMOUR_NODE **pclone = &clone
|
|
1937
|
+
|
|
1938
|
+
if self._dec == NULL:
|
|
1939
|
+
if self.base_ring() is None:
|
|
1940
|
+
self._base_ring = ZZ
|
|
1941
|
+
self._set_root_dec()
|
|
1942
|
+
|
|
1943
|
+
cdef dict kwds = dict(use_direct_graphicness_test=use_direct_graphicness_test,
|
|
1944
|
+
prefer_graphicness=prefer_graphicness,
|
|
1945
|
+
series_parallel_ok=series_parallel_ok,
|
|
1946
|
+
check_graphic_minors_planar=check_graphic_minors_planar,
|
|
1947
|
+
stop_when_irregular=stop_when_nonTU,
|
|
1948
|
+
stop_when_nongraphic=stop_when_nonnetwork,
|
|
1949
|
+
stop_when_noncographic=stop_when_nonconetwork,
|
|
1950
|
+
stop_when_nongraphic_and_noncographic=stop_when_nonnetwork_and_nonconetwork,
|
|
1951
|
+
decompose_strategy=decompose_strategy,
|
|
1952
|
+
construct_leaf_graphs=construct_leaf_graphs,
|
|
1953
|
+
construct_all_graphs=construct_all_graphs)
|
|
1954
|
+
params.algorithm = CMR_TU_ALGORITHM_DECOMPOSITION
|
|
1955
|
+
params.ternary = True
|
|
1956
|
+
params.camionFirst = False
|
|
1957
|
+
_set_cmr_seymour_parameters(¶ms.seymour, kwds)
|
|
1958
|
+
|
|
1959
|
+
sig_on()
|
|
1960
|
+
try:
|
|
1961
|
+
CMR_CALL(CMRseymourCloneUnknown(cmr, self._dec, pclone))
|
|
1962
|
+
CMR_CALL(CMRtuCompleteDecomposition(cmr, clone, ¶ms, &stats, time_limit))
|
|
1963
|
+
finally:
|
|
1964
|
+
sig_off()
|
|
1965
|
+
node = create_DecompositionNode(clone, matrix=self.matrix(),
|
|
1966
|
+
row_keys=self.row_keys(),
|
|
1967
|
+
column_keys=self.column_keys(),
|
|
1968
|
+
base_ring=self.base_ring())
|
|
1969
|
+
return node
|
|
1970
|
+
|
|
1971
|
+
|
|
1972
|
+
cdef class ThreeConnectedIrregularNode(DecompositionNode):
|
|
1973
|
+
|
|
1974
|
+
pass
|
|
1975
|
+
|
|
1976
|
+
|
|
1977
|
+
cdef class UnknownNode(DecompositionNode):
|
|
1978
|
+
r"""
|
|
1979
|
+
EXAMPLES::
|
|
1980
|
+
|
|
1981
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
1982
|
+
sage: node = UnknownNode([[1, 1], [0, 1]]); node
|
|
1983
|
+
UnknownNode (2×2)
|
|
1984
|
+
sage: node.matrix()
|
|
1985
|
+
[1 1]
|
|
1986
|
+
[0 1]
|
|
1987
|
+
sage: node = UnknownNode(matrix(ZZ, [[1, 0, 1], [0, 1, 1]])); node
|
|
1988
|
+
UnknownNode (2×3)
|
|
1989
|
+
sage: node.matrix()
|
|
1990
|
+
[1 0 1]
|
|
1991
|
+
[0 1 1]
|
|
1992
|
+
sage: node = UnknownNode(matrix(ZZ, [[1, 0, 1], [0, 1, 1]]),
|
|
1993
|
+
....: row_keys='ab',
|
|
1994
|
+
....: column_keys=range(3)); node
|
|
1995
|
+
UnknownNode (2×3)
|
|
1996
|
+
sage: node.matrix()
|
|
1997
|
+
[1 0 1]
|
|
1998
|
+
[0 1 1]
|
|
1999
|
+
sage: node.morphism()._unicode_art_matrix()
|
|
2000
|
+
0 1 2
|
|
2001
|
+
a⎛1 0 1⎞
|
|
2002
|
+
b⎝0 1 1⎠
|
|
2003
|
+
|
|
2004
|
+
From a module morphism::
|
|
2005
|
+
|
|
2006
|
+
sage: phi = matrix(ZZ, [[1, 0, 1], [0, 1, 1]],
|
|
2007
|
+
....: row_keys='ab', column_keys=range(3)); phi
|
|
2008
|
+
Generic morphism:
|
|
2009
|
+
From: Free module generated by {0, 1, 2} over Integer Ring
|
|
2010
|
+
To: Free module generated by {'a', 'b'} over Integer Ring
|
|
2011
|
+
sage: node = UnknownNode(phi); node
|
|
2012
|
+
UnknownNode (2×3)
|
|
2013
|
+
sage: node.matrix()
|
|
2014
|
+
[1 0 1]
|
|
2015
|
+
[0 1 1]
|
|
2016
|
+
"""
|
|
2017
|
+
|
|
2018
|
+
def _is_binary_linear_matroid_graphic(self, *, decomposition=False, certificate=False, **kwds):
|
|
2019
|
+
r"""
|
|
2020
|
+
Return whether the linear matroid of ``self`` over `\GF{2}` is graphic.
|
|
2021
|
+
If there is some entry not in `\{0, 1\}`, return ``False``.
|
|
2022
|
+
|
|
2023
|
+
This is an internal method because it should really be exposed
|
|
2024
|
+
as a method of :class:`Matroid`.
|
|
2025
|
+
|
|
2026
|
+
.. SEEALSO::
|
|
2027
|
+
|
|
2028
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse._is_binary_linear_matroid_graphic`
|
|
2029
|
+
|
|
2030
|
+
EXAMPLES::
|
|
2031
|
+
|
|
2032
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
2033
|
+
sage: node = UnknownNode([[1, 0], [1, 1], [0, 1]]); node
|
|
2034
|
+
UnknownNode (3×2)
|
|
2035
|
+
sage: node.matrix()
|
|
2036
|
+
[1 0]
|
|
2037
|
+
[1 1]
|
|
2038
|
+
[0 1]
|
|
2039
|
+
sage: node._is_binary_linear_matroid_graphic()
|
|
2040
|
+
True
|
|
2041
|
+
sage: result, certificate = node._is_binary_linear_matroid_graphic(certificate=True)
|
|
2042
|
+
sage: graph, forest_edges, coforest_edges = certificate
|
|
2043
|
+
sage: graph.vertices(sort=True) # the numbers have no meaning
|
|
2044
|
+
[1, 2, 7, 12]
|
|
2045
|
+
sage: graph.edges(sort=True, labels=False)
|
|
2046
|
+
[(1, 2), (1, 7), (1, 12), (2, 7), (7, 12)]
|
|
2047
|
+
sage: forest_edges # indexed by rows of M
|
|
2048
|
+
((1, 2), (7, 1), (12, 7))
|
|
2049
|
+
sage: coforest_edges # indexed by cols of M
|
|
2050
|
+
((2, 7), (1, 12))
|
|
2051
|
+
"""
|
|
2052
|
+
matrix = self.matrix()
|
|
2053
|
+
if not decomposition and not certificate:
|
|
2054
|
+
return matrix._is_binary_linear_matroid_graphic(**kwds)
|
|
2055
|
+
result, cert = matrix._is_binary_linear_matroid_graphic(certificate=True,
|
|
2056
|
+
row_keys=self.row_keys(),
|
|
2057
|
+
column_keys=self.column_keys(), **kwds)
|
|
2058
|
+
result = [result]
|
|
2059
|
+
if decomposition:
|
|
2060
|
+
graph, forest_edges, coforest_edges = cert
|
|
2061
|
+
node = GraphicNode(matrix, graph, forest_edges, coforest_edges)
|
|
2062
|
+
result.append(node)
|
|
2063
|
+
if certificate:
|
|
2064
|
+
result.append(cert)
|
|
2065
|
+
return result
|
|
2066
|
+
|
|
2067
|
+
def _is_binary_linear_matroid_cographic(self, *, decomposition=False, certificate=False, **kwds):
|
|
2068
|
+
r"""
|
|
2069
|
+
Return whether the linear matroid of ``self`` over `\GF{2}` is cographic.
|
|
2070
|
+
If there is some entry not in `\{0, 1\}`, return ``False``.
|
|
2071
|
+
|
|
2072
|
+
This is an internal method because it should really be exposed
|
|
2073
|
+
as a method of :class:`Matroid`.
|
|
2074
|
+
|
|
2075
|
+
.. SEEALSO::
|
|
2076
|
+
|
|
2077
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse._is_binary_linear_matroid_cographic`
|
|
2078
|
+
|
|
2079
|
+
EXAMPLES::
|
|
2080
|
+
|
|
2081
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
2082
|
+
sage: node = UnknownNode([[1, 1, 0], [0, 1, 1]]); node
|
|
2083
|
+
UnknownNode (2×3)
|
|
2084
|
+
sage: node.matrix()
|
|
2085
|
+
[1 1 0]
|
|
2086
|
+
[0 1 1]
|
|
2087
|
+
sage: node._is_binary_linear_matroid_cographic()
|
|
2088
|
+
True
|
|
2089
|
+
sage: result, certificate = node._is_binary_linear_matroid_cographic(certificate=True)
|
|
2090
|
+
sage: graph, forest_edges, coforest_edges = certificate
|
|
2091
|
+
sage: graph.vertices(sort=True) # the numbers have no meaning
|
|
2092
|
+
[1, 2, 7, 12]
|
|
2093
|
+
sage: graph.edges(sort=True, labels=False)
|
|
2094
|
+
[(1, 2), (1, 7), (1, 12), (2, 7), (7, 12)]
|
|
2095
|
+
sage: forest_edges # indexed by rows of M
|
|
2096
|
+
((1, 2), (7, 1))
|
|
2097
|
+
sage: coforest_edges # indexed by cols of M
|
|
2098
|
+
((2, 7), (1, 12), (1, 2))
|
|
2099
|
+
"""
|
|
2100
|
+
matrix = self.matrix()
|
|
2101
|
+
if not decomposition and not certificate:
|
|
2102
|
+
return matrix._is_binary_linear_matroid_cographic(**kwds)
|
|
2103
|
+
result, cert = matrix._is_binary_linear_matroid_cographic(certificate=True,
|
|
2104
|
+
row_keys=self.row_keys(),
|
|
2105
|
+
column_keys=self.column_keys(), **kwds)
|
|
2106
|
+
result = [result]
|
|
2107
|
+
if decomposition:
|
|
2108
|
+
graph, forest_edges, coforest_edges = cert
|
|
2109
|
+
node = CographicNode(matrix, graph, forest_edges, coforest_edges)
|
|
2110
|
+
result.append(node)
|
|
2111
|
+
if certificate:
|
|
2112
|
+
result.append(cert)
|
|
2113
|
+
return result
|
|
2114
|
+
|
|
2115
|
+
def is_network_matrix(self, *, decomposition=False, certificate=False, **kwds):
|
|
2116
|
+
r"""
|
|
2117
|
+
Return whether the matrix ``self`` over `\GF{3}` or `\QQ` is a network matrix.
|
|
2118
|
+
If there is some entry not in `\{-1, 0, 1\}`, return ``False``.
|
|
2119
|
+
|
|
2120
|
+
.. SEEALSO::
|
|
2121
|
+
|
|
2122
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.is_network_matrix`
|
|
2123
|
+
|
|
2124
|
+
EXAMPLES::
|
|
2125
|
+
|
|
2126
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
2127
|
+
sage: node = UnknownNode([[1, 0], [-1, 1], [0, -1]]); node
|
|
2128
|
+
UnknownNode (3×2)
|
|
2129
|
+
sage: node.matrix()
|
|
2130
|
+
[ 1 0]
|
|
2131
|
+
[-1 1]
|
|
2132
|
+
[ 0 -1]
|
|
2133
|
+
sage: node.is_network_matrix()
|
|
2134
|
+
True
|
|
2135
|
+
sage: result, certificate = node.is_network_matrix(certificate=True)
|
|
2136
|
+
sage: graph, forest_edges, coforest_edges = certificate
|
|
2137
|
+
sage: graph
|
|
2138
|
+
Digraph on 4 vertices
|
|
2139
|
+
sage: graph.vertices(sort=True) # the numbers have no meaning
|
|
2140
|
+
[1, 2, 7, 12]
|
|
2141
|
+
sage: graph.edges(sort=True, labels=False)
|
|
2142
|
+
[(2, 1), (2, 7), (7, 1), (7, 12), (12, 1)]
|
|
2143
|
+
sage: forest_edges # indexed by rows of M
|
|
2144
|
+
((2, 1), (7, 1), (7, 12))
|
|
2145
|
+
sage: coforest_edges # indexed by cols of M
|
|
2146
|
+
((2, 7), (12, 1))
|
|
2147
|
+
"""
|
|
2148
|
+
matrix = self.matrix()
|
|
2149
|
+
if not decomposition and not certificate:
|
|
2150
|
+
return matrix.is_network_matrix(**kwds)
|
|
2151
|
+
result, cert = matrix.is_network_matrix(certificate=True,
|
|
2152
|
+
row_keys=self.row_keys(),
|
|
2153
|
+
column_keys=self.column_keys(), **kwds)
|
|
2154
|
+
result = [result]
|
|
2155
|
+
if decomposition:
|
|
2156
|
+
graph, forest_edges, coforest_edges = cert
|
|
2157
|
+
node = GraphicNode(matrix, graph, forest_edges, coforest_edges)
|
|
2158
|
+
result.append(node)
|
|
2159
|
+
if certificate:
|
|
2160
|
+
result.append(cert)
|
|
2161
|
+
return result
|
|
2162
|
+
|
|
2163
|
+
def is_conetwork_matrix(self, *, decomposition=False, certificate=False, **kwds):
|
|
2164
|
+
r"""
|
|
2165
|
+
Return whether the matrix ``self`` over `\GF{3}` or `\QQ` is a conetwork matrix.
|
|
2166
|
+
If there is some entry not in `\{-1, 0, 1\}`, return ``False``.
|
|
2167
|
+
|
|
2168
|
+
.. SEEALSO::
|
|
2169
|
+
|
|
2170
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.is_conetwork_matrix`
|
|
2171
|
+
|
|
2172
|
+
EXAMPLES::
|
|
2173
|
+
|
|
2174
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
2175
|
+
sage: node = UnknownNode([[1, -1, 0], [0, 1, -1]]); node
|
|
2176
|
+
UnknownNode (2×3)
|
|
2177
|
+
sage: node.matrix()
|
|
2178
|
+
[ 1 -1 0]
|
|
2179
|
+
[ 0 1 -1]
|
|
2180
|
+
sage: node.is_conetwork_matrix()
|
|
2181
|
+
True
|
|
2182
|
+
sage: result, certificate = node.is_conetwork_matrix(certificate=True)
|
|
2183
|
+
sage: graph, forest_edges, coforest_edges = certificate
|
|
2184
|
+
sage: graph
|
|
2185
|
+
Digraph on 4 vertices
|
|
2186
|
+
sage: graph.vertices(sort=True) # the numbers have no meaning
|
|
2187
|
+
[1, 2, 7, 12]
|
|
2188
|
+
sage: graph.edges(sort=True, labels=False)
|
|
2189
|
+
[(2, 1), (2, 7), (7, 1), (7, 12), (12, 1)]
|
|
2190
|
+
sage: forest_edges # indexed by cols of M
|
|
2191
|
+
((2, 1), (7, 1), (7, 12))
|
|
2192
|
+
sage: coforest_edges # indexed by rows of M
|
|
2193
|
+
((2, 7), (12, 1))
|
|
2194
|
+
"""
|
|
2195
|
+
matrix = self.matrix()
|
|
2196
|
+
if not decomposition and not certificate:
|
|
2197
|
+
return matrix.is_conetwork_matrix(**kwds)
|
|
2198
|
+
result, cert = matrix.is_conetwork_matrix(certificate=True,
|
|
2199
|
+
row_keys=self.row_keys(),
|
|
2200
|
+
column_keys=self.column_keys(), **kwds)
|
|
2201
|
+
result = [result]
|
|
2202
|
+
if decomposition:
|
|
2203
|
+
graph, forest_edges, coforest_edges = cert
|
|
2204
|
+
node = CographicNode(matrix, graph, forest_edges, coforest_edges)
|
|
2205
|
+
result.append(node)
|
|
2206
|
+
if certificate:
|
|
2207
|
+
result.append(cert)
|
|
2208
|
+
return result
|
|
2209
|
+
|
|
2210
|
+
|
|
2211
|
+
cdef class SumNode(DecompositionNode):
|
|
2212
|
+
r"""
|
|
2213
|
+
Base class for 1-sum, 2-sum, and 3-sums (`\Delta`-sum, 3-sum, Y-sum) nodes
|
|
2214
|
+
in Seymour's decomposition
|
|
2215
|
+
"""
|
|
2216
|
+
|
|
2217
|
+
def _repr_(self):
|
|
2218
|
+
r"""
|
|
2219
|
+
Return a string representation of ``self``.
|
|
2220
|
+
|
|
2221
|
+
EXAMPLES::
|
|
2222
|
+
|
|
2223
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2224
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
2225
|
+
....: [[1, 1], [-1, 0]])
|
|
2226
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
2227
|
+
....: row_keys=range(4),
|
|
2228
|
+
....: column_keys='abcd')
|
|
2229
|
+
sage: print(certificate)
|
|
2230
|
+
OneSumNode (4×4) with 2 children
|
|
2231
|
+
"""
|
|
2232
|
+
result = super()._repr_()
|
|
2233
|
+
if isinstance(self, OneSumNode):
|
|
2234
|
+
result += f' with {self.nchildren()} children'
|
|
2235
|
+
return result
|
|
2236
|
+
|
|
2237
|
+
def permuted_block_matrix(self):
|
|
2238
|
+
r"""
|
|
2239
|
+
Return the permutation matrices between the matrix
|
|
2240
|
+
and the block matrix form.
|
|
2241
|
+
|
|
2242
|
+
OUTPUT: a tuple ``(Prow, BlockMatrix, Pcolumn)``, where
|
|
2243
|
+
``self.matrix() == Prow * BlockMatrix * Pcolumn``, and
|
|
2244
|
+
``BlockMatrix == self.block_matrix_form()``.
|
|
2245
|
+
|
|
2246
|
+
EXAMPLES::
|
|
2247
|
+
|
|
2248
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2249
|
+
|
|
2250
|
+
sage: M = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
2251
|
+
....: [[1, 1], [-1, 0]],
|
|
2252
|
+
....: [[1, 0], [0, 1]]); M
|
|
2253
|
+
[ 1 0| 0 0| 0 0]
|
|
2254
|
+
[-1 1| 0 0| 0 0]
|
|
2255
|
+
[-----+-----+-----]
|
|
2256
|
+
[ 0 0| 1 1| 0 0]
|
|
2257
|
+
[ 0 0|-1 0| 0 0]
|
|
2258
|
+
[-----+-----+-----]
|
|
2259
|
+
[ 0 0| 0 0| 1 0]
|
|
2260
|
+
[ 0 0| 0 0| 0 1]
|
|
2261
|
+
sage: M_perm = M.matrix_from_rows_and_columns([2, 4, 3, 0, 5, 1],
|
|
2262
|
+
....: [0, 1, 3, 5, 4, 2]); M_perm
|
|
2263
|
+
[ 0 0 1 0 0 1]
|
|
2264
|
+
[ 0 0 0 0 1 0]
|
|
2265
|
+
[ 0 0 0 0 0 -1]
|
|
2266
|
+
[ 1 0 0 0 0 0]
|
|
2267
|
+
[ 0 0 0 1 0 0]
|
|
2268
|
+
[-1 1 0 0 0 0]
|
|
2269
|
+
sage: result, certificate = M_perm.is_totally_unimodular(certificate=True)
|
|
2270
|
+
sage: certificate
|
|
2271
|
+
OneSumNode (6×6) with 4 children
|
|
2272
|
+
sage: P_row, block_matrix, P_column = certificate.permuted_block_matrix()
|
|
2273
|
+
sage: P_row^(-1) * M_perm * P_column^(-1) == block_matrix
|
|
2274
|
+
True
|
|
2275
|
+
"""
|
|
2276
|
+
from sage.combinat.permutation import Permutation
|
|
2277
|
+
cdef CMR_SEYMOUR_NODE *child_dec
|
|
2278
|
+
cdef CMR_ELEMENT *parent_rows
|
|
2279
|
+
cdef CMR_ELEMENT *parent_columns
|
|
2280
|
+
children_row = tuple()
|
|
2281
|
+
children_column = tuple()
|
|
2282
|
+
for index in range(self.nchildren()):
|
|
2283
|
+
child_dec = CMRseymourChild(self._dec, index)
|
|
2284
|
+
parent_rows = CMRseymourChildRowsToParent(self._dec, index)
|
|
2285
|
+
parent_columns = CMRseymourChildColumnsToParent(self._dec, index)
|
|
2286
|
+
child_nrows = CMRseymourNumRows(child_dec)
|
|
2287
|
+
child_ncols = CMRseymourNumColumns(child_dec)
|
|
2288
|
+
|
|
2289
|
+
if parent_rows == NULL or all(parent_rows[i] == 0 for i in range(child_nrows)):
|
|
2290
|
+
raise ValueError(f"Child {index} does not have parents rows")
|
|
2291
|
+
parent_rows_tuple = tuple(parent_rows[i] for i in range(child_nrows))
|
|
2292
|
+
|
|
2293
|
+
if parent_columns == NULL or all(parent_columns[i] == 0 for i in range(child_ncols)):
|
|
2294
|
+
raise ValueError(f"Child {index} does not have parents columns")
|
|
2295
|
+
parent_columns_tuple = tuple(parent_columns[i] for i in range(child_ncols))
|
|
2296
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
2297
|
+
for element in parent_rows_tuple)
|
|
2298
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
2299
|
+
for element in parent_columns_tuple)
|
|
2300
|
+
children_row += child_row
|
|
2301
|
+
children_column += child_column
|
|
2302
|
+
P_row = Permutation(list(children_row)).to_matrix()
|
|
2303
|
+
P_column = Permutation(list(children_column)).to_matrix().transpose()
|
|
2304
|
+
return (P_row, self.block_matrix_form(), P_column)
|
|
2305
|
+
|
|
2306
|
+
summands = DecompositionNode.child_nodes
|
|
2307
|
+
|
|
2308
|
+
def summand_matrices(self):
|
|
2309
|
+
r"""
|
|
2310
|
+
Return a tuple of matrices representing the child nodes.
|
|
2311
|
+
|
|
2312
|
+
EXAMPLES::
|
|
2313
|
+
|
|
2314
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2315
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
2316
|
+
....: [[1, 1], [-1, 0]])
|
|
2317
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True,
|
|
2318
|
+
....: row_keys=range(4),
|
|
2319
|
+
....: column_keys='abcd')
|
|
2320
|
+
sage: certificate.summand_matrices()
|
|
2321
|
+
(
|
|
2322
|
+
[ 1 0] [ 1 1]
|
|
2323
|
+
[-1 1], [-1 0]
|
|
2324
|
+
)
|
|
2325
|
+
"""
|
|
2326
|
+
return tuple(s.matrix() for s in self.summands())
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
cdef class OneSumNode(SumNode):
|
|
2330
|
+
|
|
2331
|
+
def block_matrix_form(self):
|
|
2332
|
+
r"""
|
|
2333
|
+
Return the block matrix representing the one sum node.
|
|
2334
|
+
|
|
2335
|
+
EXAMPLES::
|
|
2336
|
+
|
|
2337
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2338
|
+
sage: M = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]], [[1, 1], [-1, 0]])
|
|
2339
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True); certificate
|
|
2340
|
+
OneSumNode (4×4) with 2 children
|
|
2341
|
+
sage: certificate.summand_matrices()
|
|
2342
|
+
(
|
|
2343
|
+
[ 1 0] [ 1 1]
|
|
2344
|
+
[-1 1], [-1 0]
|
|
2345
|
+
)
|
|
2346
|
+
sage: certificate.block_matrix_form()
|
|
2347
|
+
[ 1 0| 0 0]
|
|
2348
|
+
[-1 1| 0 0]
|
|
2349
|
+
[-----+-----]
|
|
2350
|
+
[ 0 0| 1 1]
|
|
2351
|
+
[ 0 0|-1 0]
|
|
2352
|
+
|
|
2353
|
+
sage: M3 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
2354
|
+
....: [[1, 1], [-1, 0]],
|
|
2355
|
+
....: [[1, 0], [0, 1]]); M3
|
|
2356
|
+
[ 1 0| 0 0| 0 0]
|
|
2357
|
+
[-1 1| 0 0| 0 0]
|
|
2358
|
+
[-----+-----+-----]
|
|
2359
|
+
[ 0 0| 1 1| 0 0]
|
|
2360
|
+
[ 0 0|-1 0| 0 0]
|
|
2361
|
+
[-----+-----+-----]
|
|
2362
|
+
[ 0 0| 0 0| 1 0]
|
|
2363
|
+
[ 0 0| 0 0| 0 1]
|
|
2364
|
+
sage: result, certificate = M3.is_totally_unimodular(certificate=True); certificate
|
|
2365
|
+
OneSumNode (6×6) with 4 children
|
|
2366
|
+
sage: certificate.summand_matrices()
|
|
2367
|
+
(
|
|
2368
|
+
[ 1 0] [ 1 1]
|
|
2369
|
+
[-1 1], [-1 0], [1], [1]
|
|
2370
|
+
)
|
|
2371
|
+
sage: certificate.block_matrix_form()
|
|
2372
|
+
[ 1 0| 0 0| 0| 0]
|
|
2373
|
+
[-1 1| 0 0| 0| 0]
|
|
2374
|
+
[-----+-----+--+--]
|
|
2375
|
+
[ 0 0| 1 1| 0| 0]
|
|
2376
|
+
[ 0 0|-1 0| 0| 0]
|
|
2377
|
+
[-----+-----+--+--]
|
|
2378
|
+
[ 0 0| 0 0| 1| 0]
|
|
2379
|
+
[-----+-----+--+--]
|
|
2380
|
+
[ 0 0| 0 0| 0| 1]
|
|
2381
|
+
"""
|
|
2382
|
+
return Matrix_cmr_chr_sparse.one_sum(*self.summand_matrices())
|
|
2383
|
+
|
|
2384
|
+
@staticmethod
|
|
2385
|
+
def check(result_matrix, summand_matrices, summand_parent_rows_and_columns):
|
|
2386
|
+
r"""
|
|
2387
|
+
Check that ``result_matrix`` is a 1-sum of ``summand_matrices``.
|
|
2388
|
+
|
|
2389
|
+
EXAMPLES::
|
|
2390
|
+
|
|
2391
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2392
|
+
sage: from sage.matrix.seymour_decomposition import OneSumNode
|
|
2393
|
+
|
|
2394
|
+
sage: M2 = Matrix_cmr_chr_sparse.one_sum([[1, 0], [-1, 1]],
|
|
2395
|
+
....: [[1, 1], [-1, 0]])
|
|
2396
|
+
sage: result, certificate = M2.is_totally_unimodular(certificate=True); certificate
|
|
2397
|
+
OneSumNode (4×4) with 2 children
|
|
2398
|
+
sage: OneSumNode.check(M2,
|
|
2399
|
+
....: certificate.summand_matrices(),
|
|
2400
|
+
....: certificate.child_keys())
|
|
2401
|
+
|
|
2402
|
+
Symbolic identities::
|
|
2403
|
+
|
|
2404
|
+
sage: from sage.matrix.seymour_decomposition import OneSumNode
|
|
2405
|
+
sage: R.<x,y> = QQ[]
|
|
2406
|
+
sage: A = matrix([[x, 0], [-x, 1]])
|
|
2407
|
+
sage: B = matrix([[x, y], [-x, 0]])
|
|
2408
|
+
sage: A1B = block_diagonal_matrix([A, B])
|
|
2409
|
+
sage: OneSumNode.check(A1B, [A, B], [([0, 1], [0, 1]),
|
|
2410
|
+
....: ([2, 3], [2, 3])])
|
|
2411
|
+
|
|
2412
|
+
Using program analysis::
|
|
2413
|
+
|
|
2414
|
+
sage: # optional - cutgeneratingfunctionology
|
|
2415
|
+
sage: R.<x,y,z> = ParametricRealField({x: 1}, {y: -1}, {z: 0}) # true example
|
|
2416
|
+
sage: A = matrix([[x, 0], [-x, 1]])
|
|
2417
|
+
sage: B = matrix([[x, y], [-x, 0]])
|
|
2418
|
+
sage: A1B = matrix([[z, 0, 0, 0], [-x, z, 0, 0], [], []])
|
|
2419
|
+
sage: OneSumNode.check(A1B, [A, B], [([0, 1], [0, 1]),
|
|
2420
|
+
....: ([2, 3], [2, 3])])
|
|
2421
|
+
sage: # side-effect: R stores polynomial identities
|
|
2422
|
+
"""
|
|
2423
|
+
# TODO: Check that summand_parent_rows_and_columns form partitions of rows and columns
|
|
2424
|
+
for matrix, rows_and_columns in zip(summand_matrices, summand_parent_rows_and_columns):
|
|
2425
|
+
assert result_matrix.matrix_from_rows_and_columns(*rows_and_columns) == matrix
|
|
2426
|
+
# TODO: Check zero blocks
|
|
2427
|
+
|
|
2428
|
+
|
|
2429
|
+
cdef class TwoSumNode(SumNode):
|
|
2430
|
+
|
|
2431
|
+
def block_matrix_form(self):
|
|
2432
|
+
r"""
|
|
2433
|
+
Return the block matrix representing the two sum node.
|
|
2434
|
+
|
|
2435
|
+
.. SEEALSO::
|
|
2436
|
+
|
|
2437
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.two_sum`
|
|
2438
|
+
|
|
2439
|
+
EXAMPLES::
|
|
2440
|
+
|
|
2441
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2442
|
+
sage: M2 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 5, sparse=True),
|
|
2443
|
+
....: [[1, 1, 1, 1, 1], [1, 1, 1, 0, 0],
|
|
2444
|
+
....: [1, 0, 1, 1, 0], [1, 0, 0, 1, 1],
|
|
2445
|
+
....: [1, 1, 0, 0, 1]]); M2
|
|
2446
|
+
[1 1 1 1 1]
|
|
2447
|
+
[1 1 1 0 0]
|
|
2448
|
+
[1 0 1 1 0]
|
|
2449
|
+
[1 0 0 1 1]
|
|
2450
|
+
[1 1 0 0 1]
|
|
2451
|
+
sage: M3 = Matrix_cmr_chr_sparse.two_sum(M2, M2, 0, 1); M3
|
|
2452
|
+
[1 1 1 1|1 1 1 0 0]
|
|
2453
|
+
[1 1 0 0|1 1 1 0 0]
|
|
2454
|
+
[0 1 1 0|1 1 1 0 0]
|
|
2455
|
+
[0 0 1 1|1 1 1 0 0]
|
|
2456
|
+
[1 0 0 1|1 1 1 0 0]
|
|
2457
|
+
[-------+---------]
|
|
2458
|
+
[0 0 0 0|1 1 1 1 1]
|
|
2459
|
+
[0 0 0 0|1 0 1 1 0]
|
|
2460
|
+
[0 0 0 0|1 0 0 1 1]
|
|
2461
|
+
[0 0 0 0|1 1 0 0 1]
|
|
2462
|
+
sage: result, certificate = M3.is_totally_unimodular(certificate=True); certificate
|
|
2463
|
+
TwoSumNode (9×9)
|
|
2464
|
+
|
|
2465
|
+
sage: K33 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 4, sparse=True),
|
|
2466
|
+
....: [[1, 1, 0, 0], [1, 1, 1, 0],
|
|
2467
|
+
....: [1, 0, 0,-1], [0, 1, 1, 1],
|
|
2468
|
+
....: [0, 0, 1, 1]]); K33
|
|
2469
|
+
[ 1 1 0 0]
|
|
2470
|
+
[ 1 1 1 0]
|
|
2471
|
+
[ 1 0 0 -1]
|
|
2472
|
+
[ 0 1 1 1]
|
|
2473
|
+
[ 0 0 1 1]
|
|
2474
|
+
sage: K33_dual = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 4, 5, sparse=True),
|
|
2475
|
+
....: [[1, 1, 1, 0, 0], [1, 1, 0, 1, 0],
|
|
2476
|
+
....: [0, 1, 0, 1, 1], [0, 0,-1, 1, 1]]); K33_dual
|
|
2477
|
+
[ 1 1 1 0 0]
|
|
2478
|
+
[ 1 1 0 1 0]
|
|
2479
|
+
[ 0 1 0 1 1]
|
|
2480
|
+
[ 0 0 -1 1 1]
|
|
2481
|
+
sage: M = Matrix_cmr_chr_sparse.two_sum(K33, K33_dual, 0, 0,
|
|
2482
|
+
....: nonzero_block="bottom_left"); M
|
|
2483
|
+
[ 1 1 1 0| 0 0 0 0]
|
|
2484
|
+
[ 1 0 0 -1| 0 0 0 0]
|
|
2485
|
+
[ 0 1 1 1| 0 0 0 0]
|
|
2486
|
+
[ 0 0 1 1| 0 0 0 0]
|
|
2487
|
+
[-----------+-----------]
|
|
2488
|
+
[ 1 1 0 0| 1 1 0 0]
|
|
2489
|
+
[ 1 1 0 0| 1 0 1 0]
|
|
2490
|
+
[ 0 0 0 0| 1 0 1 1]
|
|
2491
|
+
[ 0 0 0 0| 0 -1 1 1]
|
|
2492
|
+
sage: result1, certificate1 = M.is_totally_unimodular(certificate=True); certificate1
|
|
2493
|
+
TwoSumNode (8×8)
|
|
2494
|
+
sage: certificate1.summand_matrices()
|
|
2495
|
+
(
|
|
2496
|
+
[ 1 1 1 0]
|
|
2497
|
+
[ 1 0 0 -1] [ 1 1 1 0 0]
|
|
2498
|
+
[ 0 1 1 1] [ 1 1 0 1 0]
|
|
2499
|
+
[ 0 0 1 1] [ 0 1 0 1 1]
|
|
2500
|
+
[ 1 1 0 0], [ 0 0 -1 1 1]
|
|
2501
|
+
)
|
|
2502
|
+
sage: certificate1.block_matrix_form()
|
|
2503
|
+
[ 1 1 1 0| 0 0 0 0]
|
|
2504
|
+
[ 1 0 0 -1| 0 0 0 0]
|
|
2505
|
+
[ 0 1 1 1| 0 0 0 0]
|
|
2506
|
+
[ 0 0 1 1| 0 0 0 0]
|
|
2507
|
+
[-----------+-----------]
|
|
2508
|
+
[ 1 1 0 0| 1 1 0 0]
|
|
2509
|
+
[ 1 1 0 0| 1 0 1 0]
|
|
2510
|
+
[ 0 0 0 0| 1 0 1 1]
|
|
2511
|
+
[ 0 0 0 0| 0 -1 1 1]
|
|
2512
|
+
sage: certificate1.child_keys()
|
|
2513
|
+
(((0, 1, 2, 3, 4), (0, 1, 2, 3)), ((4, 5, 6, 7), (0, 4, 5, 6, 7)))
|
|
2514
|
+
sage: M_perm = M.matrix_from_rows_and_columns([4, 6, 5, 7, 0, 1, 2, 3], range(M.ncols()))
|
|
2515
|
+
sage: M_perm
|
|
2516
|
+
[ 1 1 0 0 1 1 0 0]
|
|
2517
|
+
[ 0 0 0 0 1 0 1 1]
|
|
2518
|
+
[ 1 1 0 0 1 0 1 0]
|
|
2519
|
+
[ 0 0 0 0 0 -1 1 1]
|
|
2520
|
+
[ 1 1 1 0 0 0 0 0]
|
|
2521
|
+
[ 1 0 0 -1 0 0 0 0]
|
|
2522
|
+
[ 0 1 1 1 0 0 0 0]
|
|
2523
|
+
[ 0 0 1 1 0 0 0 0]
|
|
2524
|
+
sage: result2, certificate2 = M_perm.is_totally_unimodular(certificate=True)
|
|
2525
|
+
sage: certificate2.summand_matrices()
|
|
2526
|
+
(
|
|
2527
|
+
[ 1 1 1 0]
|
|
2528
|
+
[ 1 0 0 -1] [ 1 1 1 0 0]
|
|
2529
|
+
[ 0 1 1 1] [ 0 1 0 1 1]
|
|
2530
|
+
[ 0 0 1 1] [ 1 1 0 1 0]
|
|
2531
|
+
[ 1 1 0 0], [ 0 0 -1 1 1]
|
|
2532
|
+
)
|
|
2533
|
+
sage: certificate2.block_matrix_form()
|
|
2534
|
+
[ 1 1 1 0| 0 0 0 0]
|
|
2535
|
+
[ 1 0 0 -1| 0 0 0 0]
|
|
2536
|
+
[ 0 1 1 1| 0 0 0 0]
|
|
2537
|
+
[ 0 0 1 1| 0 0 0 0]
|
|
2538
|
+
[-----------+-----------]
|
|
2539
|
+
[ 1 1 0 0| 1 1 0 0]
|
|
2540
|
+
[ 0 0 0 0| 1 0 1 1]
|
|
2541
|
+
[ 1 1 0 0| 1 0 1 0]
|
|
2542
|
+
[ 0 0 0 0| 0 -1 1 1]
|
|
2543
|
+
sage: certificate2.child_keys()
|
|
2544
|
+
(((4, 5, 6, 7, 0), (0, 1, 2, 3)), ((0, 1, 2, 3), (0, 4, 5, 6, 7)))
|
|
2545
|
+
"""
|
|
2546
|
+
M1, M2 = self.summand_matrices()
|
|
2547
|
+
return Matrix_cmr_chr_sparse.two_sum(M1, M2, M1.nrows() - 1, 0, "bottom_left")
|
|
2548
|
+
|
|
2549
|
+
|
|
2550
|
+
cdef class DeltaSumNode(SumNode):
|
|
2551
|
+
|
|
2552
|
+
def _children(self):
|
|
2553
|
+
r"""
|
|
2554
|
+
Return a tuple of the tuples of the two children
|
|
2555
|
+
and their row and column keys.
|
|
2556
|
+
|
|
2557
|
+
.. SEEALSO::
|
|
2558
|
+
|
|
2559
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.delta_sum`
|
|
2560
|
+
|
|
2561
|
+
TESTS:
|
|
2562
|
+
|
|
2563
|
+
This is test ``DeltasumR12`` in CMR's ``test_tu.cpp``::
|
|
2564
|
+
|
|
2565
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2566
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
2567
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
2568
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
2569
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
2570
|
+
....: decompose_strategy="delta_pivot",
|
|
2571
|
+
....: row_keys=range(6),
|
|
2572
|
+
....: column_keys='abcdef')
|
|
2573
|
+
sage: certificate.child_keys()
|
|
2574
|
+
((0, 1, a, 3, 4, 5), (2, b, c, d, e, f))
|
|
2575
|
+
sage: C = certificate.child_nodes()[0]
|
|
2576
|
+
sage: C1, C2 = C.child_nodes()
|
|
2577
|
+
sage: C1.matrix()
|
|
2578
|
+
[ 0 0 1 1 1]
|
|
2579
|
+
[ 1 1 1 0 0]
|
|
2580
|
+
[ 0 1 0 -1 -1]
|
|
2581
|
+
[-1 0 -1 0 -1]
|
|
2582
|
+
sage: C2.matrix()
|
|
2583
|
+
[-1 0 1 -1 -1]
|
|
2584
|
+
[ 1 1 0 1 1]
|
|
2585
|
+
[ 0 0 1 0 -1]
|
|
2586
|
+
[ 1 1 0 0 1]
|
|
2587
|
+
sage: C.child_keys()
|
|
2588
|
+
(((0, 1, a, 3), (b, c, d, 2, +2-3)), ((0, 3, 4, 5), (-0+b, b, 2, e, f)))
|
|
2589
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
2590
|
+
sage: node = UnknownNode(R12,
|
|
2591
|
+
....: row_keys=range(6),
|
|
2592
|
+
....: column_keys='abcdef'); node
|
|
2593
|
+
UnknownNode (6×6)
|
|
2594
|
+
sage: C0 = node.complete_decomposition(
|
|
2595
|
+
....: decompose_strategy="delta_pivot",
|
|
2596
|
+
....: )
|
|
2597
|
+
sage: C0
|
|
2598
|
+
PivotsNode (6×6)
|
|
2599
|
+
sage: unicode_art(C0)
|
|
2600
|
+
PivotsNode (6×6)
|
|
2601
|
+
│
|
|
2602
|
+
╭DeltaSumNode (6×6)─╮
|
|
2603
|
+
│ │
|
|
2604
|
+
CographicNode (4×5) GraphicNode (4×5)
|
|
2605
|
+
sage: unicode_art(node)
|
|
2606
|
+
UnknownNode (6×6)
|
|
2607
|
+
|
|
2608
|
+
sage: R12_pivot = R12.ternary_pivot(4, 0); R12_pivot
|
|
2609
|
+
[ 1 0 0 1 -1 0]
|
|
2610
|
+
[ 0 1 1 1 0 0]
|
|
2611
|
+
[ 1 0 0 0 0 1]
|
|
2612
|
+
[ 0 -1 0 -1 1 1]
|
|
2613
|
+
[-1 0 1 0 1 0]
|
|
2614
|
+
[ 0 -1 0 -1 0 1]
|
|
2615
|
+
sage: result, certificate = R12_pivot.is_totally_unimodular(certificate=True,
|
|
2616
|
+
....: decompose_strategy="delta_pivot")
|
|
2617
|
+
sage: C1, C2 = certificate.child_nodes()
|
|
2618
|
+
sage: C1.matrix()
|
|
2619
|
+
[ 0 0 1 1 1]
|
|
2620
|
+
[ 1 1 1 0 0]
|
|
2621
|
+
[ 0 1 0 -1 -1]
|
|
2622
|
+
[-1 0 -1 0 -1]
|
|
2623
|
+
sage: C2.matrix()
|
|
2624
|
+
[-1 0 1 -1 0]
|
|
2625
|
+
[ 0 0 1 0 1]
|
|
2626
|
+
[ 1 1 0 1 1]
|
|
2627
|
+
[ 1 1 0 0 1]
|
|
2628
|
+
|
|
2629
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2630
|
+
sage: R12_large = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
2631
|
+
....: [[1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
2632
|
+
....: [0, 0, 0, 1, -1, 0, 0, 0, 1 , 1, 1, 1],
|
|
2633
|
+
....: [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
2634
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
2635
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
2636
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
2637
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
2638
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
2639
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
2640
|
+
sage: result, certificate = R12_large.is_totally_unimodular(certificate=True,
|
|
2641
|
+
....: decompose_strategy="delta_pivot",
|
|
2642
|
+
....: row_keys=range(9),
|
|
2643
|
+
....: column_keys='abcdefghijkl')
|
|
2644
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
2645
|
+
DeltaSumNode (9×12)
|
|
2646
|
+
sage: C1, C2 = C.child_nodes()
|
|
2647
|
+
sage: C1.matrix()
|
|
2648
|
+
[ 0 0 1 1 1 1 1]
|
|
2649
|
+
[ 1 1 0 0 0 -1 -1]
|
|
2650
|
+
[ 1 0 -1 0 -1 -1 -1]
|
|
2651
|
+
[ 0 1 1 0 1 0 0]
|
|
2652
|
+
[ 0 0 0 -1 -1 0 -1]
|
|
2653
|
+
sage: C2.matrix()
|
|
2654
|
+
[-1 0 1 -1 0 0 0 0 -1]
|
|
2655
|
+
[ 0 0 -1 1 0 1 -1 0 1]
|
|
2656
|
+
[ 1 1 0 1 1 0 0 0 1]
|
|
2657
|
+
[ 1 1 0 1 1 0 0 0 0]
|
|
2658
|
+
[ 1 1 -1 1 0 1 0 1 1]
|
|
2659
|
+
[ 1 1 0 0 0 0 1 1 0]
|
|
2660
|
+
sage: C.row_keys()
|
|
2661
|
+
(i, 1, 2, 3, 4, 5, 6, 7, 8)
|
|
2662
|
+
sage: C.column_keys()
|
|
2663
|
+
(a, b, c, d, e, f, g, h, 0, j, k, l)
|
|
2664
|
+
sage: C.child_keys()[0]
|
|
2665
|
+
((i, 2, 7, 8, 3), (g, h, j, k, l, a, -3+a))
|
|
2666
|
+
sage: C.child_keys()[1]
|
|
2667
|
+
((i, 1, 3, 4, 5, 6), (-i+k, k, a, b, c, d, e, f, 0))
|
|
2668
|
+
"""
|
|
2669
|
+
if self._child_nodes is not None:
|
|
2670
|
+
return self._child_nodes
|
|
2671
|
+
|
|
2672
|
+
if self.nchildren() != 2:
|
|
2673
|
+
raise ValueError(f"DeltaSumNode has exactly two children not {self.nchildren()}!")
|
|
2674
|
+
|
|
2675
|
+
self.set_default_keys()
|
|
2676
|
+
|
|
2677
|
+
cdef CMR_SEYMOUR_NODE *child1_dec = CMRseymourChild(self._dec, 0)
|
|
2678
|
+
cdef CMR_ELEMENT *parent_rows1 = CMRseymourChildRowsToParent(self._dec, 0)
|
|
2679
|
+
cdef CMR_ELEMENT *parent_columns1 = CMRseymourChildColumnsToParent(self._dec, 0)
|
|
2680
|
+
cdef CMR_CHRMAT *mat1 = CMRseymourGetMatrix(child1_dec)
|
|
2681
|
+
|
|
2682
|
+
cdef CMR_SEYMOUR_NODE *child2_dec = CMRseymourChild(self._dec, 1)
|
|
2683
|
+
cdef CMR_ELEMENT *parent_rows2 = CMRseymourChildRowsToParent(self._dec, 1)
|
|
2684
|
+
cdef CMR_ELEMENT *parent_columns2 = CMRseymourChildColumnsToParent(self._dec, 1)
|
|
2685
|
+
cdef CMR_CHRMAT *mat2 = CMRseymourGetMatrix(child2_dec)
|
|
2686
|
+
|
|
2687
|
+
cdef size_t index1
|
|
2688
|
+
|
|
2689
|
+
child1_nrows = CMRseymourNumRows(child1_dec)
|
|
2690
|
+
child1_ncols = CMRseymourNumColumns(child1_dec)
|
|
2691
|
+
|
|
2692
|
+
child1_row_keys = tuple(self._CMRelement_to_key(parent_rows1[i])
|
|
2693
|
+
for i in range(child1_nrows))
|
|
2694
|
+
child1_column_keys = tuple(self._CMRelement_to_key(parent_columns1[i])
|
|
2695
|
+
for i in range(child1_ncols - 1))
|
|
2696
|
+
|
|
2697
|
+
row1_index = child1_nrows - 1
|
|
2698
|
+
column1_index = child1_ncols - 1
|
|
2699
|
+
CMR_CALL(CMRchrmatFindEntry(mat1, row1_index, column1_index, &index1))
|
|
2700
|
+
if index1 == SIZE_MAX:
|
|
2701
|
+
eps1 = Integer(0)
|
|
2702
|
+
else:
|
|
2703
|
+
eps1 = Integer(mat1.entryValues[index1])
|
|
2704
|
+
if eps1 != 1 and eps1 != -1:
|
|
2705
|
+
raise ValueError(f"First child in the Delta Sum "
|
|
2706
|
+
f"has 1 or -1 in the entry "
|
|
2707
|
+
f"row {row1_index} and column {column1_index} "
|
|
2708
|
+
f"but got {eps1}")
|
|
2709
|
+
|
|
2710
|
+
extra_key = ElementKey((1, child1_column_keys[column1_index - 1],
|
|
2711
|
+
eps1, child1_row_keys[row1_index]),
|
|
2712
|
+
composition=True)
|
|
2713
|
+
child1_column_keys += (extra_key,)
|
|
2714
|
+
|
|
2715
|
+
child1 = create_DecompositionNode(child1_dec, matrix=None,
|
|
2716
|
+
row_keys=child1_row_keys,
|
|
2717
|
+
column_keys=child1_column_keys,
|
|
2718
|
+
base_ring=self.base_ring())
|
|
2719
|
+
|
|
2720
|
+
child2_nrows = CMRseymourNumRows(child2_dec)
|
|
2721
|
+
child2_ncols = CMRseymourNumColumns(child2_dec)
|
|
2722
|
+
|
|
2723
|
+
child2_row_keys = tuple(self._CMRelement_to_key(parent_rows2[i])
|
|
2724
|
+
for i in range(child2_nrows))
|
|
2725
|
+
|
|
2726
|
+
row2_index = 0
|
|
2727
|
+
column2_index = 0
|
|
2728
|
+
CMR_CALL(CMRchrmatFindEntry(mat2, row2_index, column2_index, &index1))
|
|
2729
|
+
if index1 == SIZE_MAX:
|
|
2730
|
+
eps1 = Integer(0)
|
|
2731
|
+
else:
|
|
2732
|
+
eps1 = Integer(mat2.entryValues[index1])
|
|
2733
|
+
|
|
2734
|
+
if eps1 != 1 and eps1 != -1:
|
|
2735
|
+
raise ValueError(f"Second child in the Delta Sum "
|
|
2736
|
+
f"has 1 or -1 in the entry "
|
|
2737
|
+
f"row {row2_index} and column {column2_index} "
|
|
2738
|
+
f"but got {eps1}")
|
|
2739
|
+
|
|
2740
|
+
child2_column_keys = tuple(self._CMRelement_to_key(parent_columns2[i])
|
|
2741
|
+
for i in range(1, child2_ncols))
|
|
2742
|
+
extra_key = ElementKey((1, child2_column_keys[column2_index],
|
|
2743
|
+
eps1, child2_row_keys[row2_index]),
|
|
2744
|
+
composition=True)
|
|
2745
|
+
child2_column_keys = (extra_key,) + child2_column_keys
|
|
2746
|
+
|
|
2747
|
+
child2 = create_DecompositionNode(child2_dec, matrix=None,
|
|
2748
|
+
row_keys=child2_row_keys,
|
|
2749
|
+
column_keys=child2_column_keys,
|
|
2750
|
+
base_ring=self.base_ring())
|
|
2751
|
+
|
|
2752
|
+
self._child_nodes = ((child1, child1_row_keys, child1_column_keys),
|
|
2753
|
+
(child2, child2_row_keys, child2_column_keys))
|
|
2754
|
+
return self._child_nodes
|
|
2755
|
+
|
|
2756
|
+
def is_distributed_ranks(self):
|
|
2757
|
+
r"""
|
|
2758
|
+
Return ``True`` for the `\Delta`-sum node ``self``.
|
|
2759
|
+
|
|
2760
|
+
``distributed_ranks`` is named after the two rank 1 off-diagonal blocks.
|
|
2761
|
+
|
|
2762
|
+
.. SEEALSO::
|
|
2763
|
+
|
|
2764
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.delta_sum`
|
|
2765
|
+
|
|
2766
|
+
EXAMPLES::
|
|
2767
|
+
|
|
2768
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2769
|
+
sage: R12_large = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
2770
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
2771
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
2772
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
2773
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
2774
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
2775
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
2776
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
2777
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
2778
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
2779
|
+
sage: result, certificate = R12_large.is_totally_unimodular(certificate=True,
|
|
2780
|
+
....: decompose_strategy="delta_pivot")
|
|
2781
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
2782
|
+
DeltaSumNode (9×12)
|
|
2783
|
+
sage: C.is_distributed_ranks()
|
|
2784
|
+
True
|
|
2785
|
+
sage: C.is_concentrated_rank()
|
|
2786
|
+
False
|
|
2787
|
+
"""
|
|
2788
|
+
return True
|
|
2789
|
+
|
|
2790
|
+
def is_concentrated_rank(self):
|
|
2791
|
+
r"""
|
|
2792
|
+
Return ``False`` for the `\Delta`-sum node ``self``.
|
|
2793
|
+
|
|
2794
|
+
``concentrated_rank`` is named after the rank 2 and rank 0 off-diagonal blocks.
|
|
2795
|
+
|
|
2796
|
+
.. SEEALSO::
|
|
2797
|
+
|
|
2798
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.delta_sum`
|
|
2799
|
+
"""
|
|
2800
|
+
return False
|
|
2801
|
+
|
|
2802
|
+
def block_matrix_form(self):
|
|
2803
|
+
r"""
|
|
2804
|
+
Return the block matrix constructed from the `\Delta`-sum of children.
|
|
2805
|
+
|
|
2806
|
+
EXAMPLES::
|
|
2807
|
+
|
|
2808
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2809
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
2810
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
2811
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
2812
|
+
sage: R12
|
|
2813
|
+
[ 1 0 1 1 0 0]
|
|
2814
|
+
[ 0 1 1 1 0 0]
|
|
2815
|
+
[ 1 0 1 0 1 1]
|
|
2816
|
+
[ 0 -1 0 -1 1 1]
|
|
2817
|
+
[ 1 0 1 0 1 0]
|
|
2818
|
+
[ 0 -1 0 -1 0 1]
|
|
2819
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
2820
|
+
....: decompose_strategy="delta_pivot")
|
|
2821
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
2822
|
+
DeltaSumNode (6×6)
|
|
2823
|
+
sage: C.matrix()
|
|
2824
|
+
[ 1 0 0 1 -1 -1]
|
|
2825
|
+
[ 0 1 1 1 0 0]
|
|
2826
|
+
[-1 0 1 0 1 1]
|
|
2827
|
+
[ 0 -1 0 -1 1 1]
|
|
2828
|
+
[ 1 0 0 0 0 -1]
|
|
2829
|
+
[ 0 -1 0 -1 0 1]
|
|
2830
|
+
sage: C.summand_matrices()
|
|
2831
|
+
(
|
|
2832
|
+
[ 0 0 1 1 1] [-1 0 1 -1 -1]
|
|
2833
|
+
[ 1 1 1 0 0] [ 1 1 0 1 1]
|
|
2834
|
+
[ 0 1 0 -1 -1] [ 0 0 1 0 -1]
|
|
2835
|
+
[-1 0 -1 0 -1], [ 1 1 0 0 1]
|
|
2836
|
+
)
|
|
2837
|
+
sage: C.row_keys()
|
|
2838
|
+
(r0, r1, c0, r3, r4, r5)
|
|
2839
|
+
sage: C.column_keys()
|
|
2840
|
+
(r2, c1, c2, c3, c4, c5)
|
|
2841
|
+
sage: C.child_keys()
|
|
2842
|
+
(((r0, r1, c0, r3), (c1, c2, c3, r2, +r2-r3)),
|
|
2843
|
+
((r0, r3, r4, r5), (+c1-r0, c1, r2, c4, c5)))
|
|
2844
|
+
sage: C.block_matrix_form()
|
|
2845
|
+
[ 0 0 1 1 -1 -1]
|
|
2846
|
+
[ 1 1 1 0 0 0]
|
|
2847
|
+
[ 0 1 0 -1 1 1]
|
|
2848
|
+
[-1 0 -1 0 1 1]
|
|
2849
|
+
[ 0 0 0 1 0 -1]
|
|
2850
|
+
[-1 0 -1 0 0 1]
|
|
2851
|
+
"""
|
|
2852
|
+
M1, M2 = self.summand_matrices()
|
|
2853
|
+
return Matrix_cmr_chr_sparse.delta_sum(M1, M2)
|
|
2854
|
+
|
|
2855
|
+
def permuted_block_matrix(self):
|
|
2856
|
+
r"""
|
|
2857
|
+
Return the permutation matrices between the matrix
|
|
2858
|
+
and the block matrix form.
|
|
2859
|
+
|
|
2860
|
+
OUTPUT: a tuple ``(Prow, BlockMatrix, Pcolumn)``, where
|
|
2861
|
+
``self.matrix() == Prow * BlockMatrix * Pcolumn``, and
|
|
2862
|
+
``BlockMatrix == self.block_matrix_form()``.
|
|
2863
|
+
|
|
2864
|
+
EXAMPLES::
|
|
2865
|
+
|
|
2866
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2867
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
2868
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
2869
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
2870
|
+
sage: R12
|
|
2871
|
+
[ 1 0 1 1 0 0]
|
|
2872
|
+
[ 0 1 1 1 0 0]
|
|
2873
|
+
[ 1 0 1 0 1 1]
|
|
2874
|
+
[ 0 -1 0 -1 1 1]
|
|
2875
|
+
[ 1 0 1 0 1 0]
|
|
2876
|
+
[ 0 -1 0 -1 0 1]
|
|
2877
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
2878
|
+
....: decompose_strategy="delta_pivot")
|
|
2879
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
2880
|
+
DeltaSumNode (6×6)
|
|
2881
|
+
sage: C.matrix()
|
|
2882
|
+
[ 1 0 0 1 -1 -1]
|
|
2883
|
+
[ 0 1 1 1 0 0]
|
|
2884
|
+
[-1 0 1 0 1 1]
|
|
2885
|
+
[ 0 -1 0 -1 1 1]
|
|
2886
|
+
[ 1 0 0 0 0 -1]
|
|
2887
|
+
[ 0 -1 0 -1 0 1]
|
|
2888
|
+
sage: C.block_matrix_form()
|
|
2889
|
+
[ 0 0 1 1 -1 -1]
|
|
2890
|
+
[ 1 1 1 0 0 0]
|
|
2891
|
+
[ 0 1 0 -1 1 1]
|
|
2892
|
+
[-1 0 -1 0 1 1]
|
|
2893
|
+
[ 0 0 0 1 0 -1]
|
|
2894
|
+
[-1 0 -1 0 0 1]
|
|
2895
|
+
sage: P_row, block_matrix, P_column = C.permuted_block_matrix()
|
|
2896
|
+
sage: P_row^(-1) * C.matrix() * P_column^(-1) == block_matrix
|
|
2897
|
+
True
|
|
2898
|
+
"""
|
|
2899
|
+
from sage.combinat.permutation import Permutation
|
|
2900
|
+
cdef CMR_SEYMOUR_NODE *child_dec
|
|
2901
|
+
cdef CMR_ELEMENT *parent_rows
|
|
2902
|
+
cdef CMR_ELEMENT *parent_columns
|
|
2903
|
+
children_row = tuple()
|
|
2904
|
+
children_column = tuple()
|
|
2905
|
+
for index in range(self.nchildren()):
|
|
2906
|
+
child_dec = CMRseymourChild(self._dec, index)
|
|
2907
|
+
parent_rows = CMRseymourChildRowsToParent(self._dec, index)
|
|
2908
|
+
parent_columns = CMRseymourChildColumnsToParent(self._dec, index)
|
|
2909
|
+
child_nrows = CMRseymourNumRows(child_dec)
|
|
2910
|
+
child_ncols = CMRseymourNumColumns(child_dec)
|
|
2911
|
+
|
|
2912
|
+
if parent_rows == NULL or all(parent_rows[i] == 0 for i in range(child_nrows)):
|
|
2913
|
+
raise ValueError(f"Child {index} does not have parents rows")
|
|
2914
|
+
parent_rows_tuple = tuple(parent_rows[i] for i in range(child_nrows))
|
|
2915
|
+
|
|
2916
|
+
if parent_columns == NULL or all(parent_columns[i] == 0 for i in range(child_ncols)):
|
|
2917
|
+
raise ValueError(f"Child {index} does not have parents columns")
|
|
2918
|
+
parent_columns_tuple = tuple(parent_columns[i] for i in range(child_ncols))
|
|
2919
|
+
if index == 0:
|
|
2920
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
2921
|
+
for element in parent_rows_tuple[:-1])
|
|
2922
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
2923
|
+
for element in parent_columns_tuple[:-2])
|
|
2924
|
+
else:
|
|
2925
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
2926
|
+
for element in parent_rows_tuple[1:])
|
|
2927
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
2928
|
+
for element in parent_columns_tuple[2:])
|
|
2929
|
+
children_row += child_row
|
|
2930
|
+
children_column += child_column
|
|
2931
|
+
P_row = Permutation(list(children_row)).to_matrix()
|
|
2932
|
+
P_column = Permutation(list(children_column)).to_matrix().transpose()
|
|
2933
|
+
return (P_row, self.block_matrix_form(), P_column)
|
|
2934
|
+
|
|
2935
|
+
|
|
2936
|
+
cdef class ThreeSumNode(SumNode):
|
|
2937
|
+
|
|
2938
|
+
def _children(self):
|
|
2939
|
+
r"""
|
|
2940
|
+
Return a tuple of the tuples of the two children
|
|
2941
|
+
and their row and column keys.
|
|
2942
|
+
|
|
2943
|
+
.. SEEALSO::
|
|
2944
|
+
|
|
2945
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.three_sum`
|
|
2946
|
+
|
|
2947
|
+
TESTS:
|
|
2948
|
+
|
|
2949
|
+
This is test ``ThreesumR12`` in CMR's ``test_tu.cpp``::
|
|
2950
|
+
|
|
2951
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
2952
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
2953
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
2954
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
2955
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
2956
|
+
....: decompose_strategy="three_pivot")
|
|
2957
|
+
sage: C1, C2 = certificate.child_nodes()
|
|
2958
|
+
sage: C1.matrix()
|
|
2959
|
+
[ 1 0 1 1 0]
|
|
2960
|
+
[ 0 1 1 1 0]
|
|
2961
|
+
[ 1 0 1 0 1]
|
|
2962
|
+
[ 0 -1 0 -1 1]
|
|
2963
|
+
sage: C2.matrix()
|
|
2964
|
+
[ 1 1 0 0]
|
|
2965
|
+
[ 1 0 1 1]
|
|
2966
|
+
[ 0 -1 1 1]
|
|
2967
|
+
[ 1 0 1 0]
|
|
2968
|
+
[ 0 -1 0 1]
|
|
2969
|
+
sage: certificate.child_keys()[0]
|
|
2970
|
+
((r0, r1, r2, r3), (c0, c1, c2, c3, +r2+r3))
|
|
2971
|
+
sage: certificate.child_keys()[1]
|
|
2972
|
+
((+c0+c3, r2, r3, r4, r5), (c0, c3, c4, c5))
|
|
2973
|
+
|
|
2974
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
2975
|
+
....: decompose_strategy="three_pivot",
|
|
2976
|
+
....: row_keys=range(6),
|
|
2977
|
+
....: column_keys='abcdef')
|
|
2978
|
+
sage: C1, C2 = certificate.child_nodes()
|
|
2979
|
+
sage: C1.matrix()
|
|
2980
|
+
[ 1 0 1 1 0]
|
|
2981
|
+
[ 0 1 1 1 0]
|
|
2982
|
+
[ 1 0 1 0 1]
|
|
2983
|
+
[ 0 -1 0 -1 1]
|
|
2984
|
+
sage: C2.matrix()
|
|
2985
|
+
[ 1 1 0 0]
|
|
2986
|
+
[ 1 0 1 1]
|
|
2987
|
+
[ 0 -1 1 1]
|
|
2988
|
+
[ 1 0 1 0]
|
|
2989
|
+
[ 0 -1 0 1]
|
|
2990
|
+
sage: certificate.child_keys()[0]
|
|
2991
|
+
((0, 1, 2, 3), (a, b, c, d, +2+3))
|
|
2992
|
+
sage: certificate.child_keys()[1]
|
|
2993
|
+
((+a+d, 2, 3, 4, 5), (a, d, e, f))
|
|
2994
|
+
"""
|
|
2995
|
+
if self._child_nodes is not None:
|
|
2996
|
+
return self._child_nodes
|
|
2997
|
+
|
|
2998
|
+
if self.nchildren() != 2:
|
|
2999
|
+
raise ValueError(f"ThreeSumNode has exactly two children not {self.nchildren()}!")
|
|
3000
|
+
|
|
3001
|
+
self.set_default_keys()
|
|
3002
|
+
|
|
3003
|
+
cdef CMR_SEYMOUR_NODE *child1_dec = CMRseymourChild(self._dec, 0)
|
|
3004
|
+
cdef CMR_ELEMENT *parent_rows1 = CMRseymourChildRowsToParent(self._dec, 0)
|
|
3005
|
+
cdef CMR_ELEMENT *parent_columns1 = CMRseymourChildColumnsToParent(self._dec, 0)
|
|
3006
|
+
cdef CMR_CHRMAT *mat1 = CMRseymourGetMatrix(child1_dec)
|
|
3007
|
+
cdef size_t *first_special_rows = CMRseymourChildSpecialRows(self._dec, 0)
|
|
3008
|
+
cdef size_t *first_special_columns = CMRseymourChildSpecialColumns(self._dec, 0)
|
|
3009
|
+
|
|
3010
|
+
cdef CMR_SEYMOUR_NODE *child2_dec = CMRseymourChild(self._dec, 1)
|
|
3011
|
+
cdef CMR_ELEMENT *parent_rows2 = CMRseymourChildRowsToParent(self._dec, 1)
|
|
3012
|
+
cdef CMR_ELEMENT *parent_columns2 = CMRseymourChildColumnsToParent(self._dec, 1)
|
|
3013
|
+
cdef CMR_CHRMAT *mat2 = CMRseymourGetMatrix(child2_dec)
|
|
3014
|
+
cdef size_t *second_special_rows = CMRseymourChildSpecialRows(self._dec, 1)
|
|
3015
|
+
cdef size_t *second_special_columns = CMRseymourChildSpecialColumns(self._dec, 1)
|
|
3016
|
+
|
|
3017
|
+
cdef size_t index1, index2
|
|
3018
|
+
|
|
3019
|
+
child1_nrows = CMRseymourNumRows(child1_dec)
|
|
3020
|
+
child1_ncols = CMRseymourNumColumns(child1_dec)
|
|
3021
|
+
|
|
3022
|
+
child1_row_keys = tuple(self._CMRelement_to_key(parent_rows1[i])
|
|
3023
|
+
for i in range(child1_nrows))
|
|
3024
|
+
child1_column_keys = tuple(self._CMRelement_to_key(parent_columns1[i])
|
|
3025
|
+
for i in range(child1_ncols - 1))
|
|
3026
|
+
|
|
3027
|
+
row1_index = first_special_rows[0]
|
|
3028
|
+
extra_column = first_special_columns[2]
|
|
3029
|
+
CMR_CALL(CMRchrmatFindEntry(mat1, row1_index, extra_column, &index1))
|
|
3030
|
+
if index1 == SIZE_MAX:
|
|
3031
|
+
eps1 = Integer(0)
|
|
3032
|
+
else:
|
|
3033
|
+
eps1 = Integer(mat1.entryValues[index1])
|
|
3034
|
+
if eps1 != 1:
|
|
3035
|
+
raise ValueError(f"First child in the Three Sum "
|
|
3036
|
+
f"has 1 in the entry "
|
|
3037
|
+
f"row {row1_index} and column {extra_column} "
|
|
3038
|
+
f"but got {eps1}")
|
|
3039
|
+
|
|
3040
|
+
row2_index = first_special_rows[1]
|
|
3041
|
+
CMR_CALL(CMRchrmatFindEntry(mat1, row2_index, extra_column, &index2))
|
|
3042
|
+
if index2 == SIZE_MAX:
|
|
3043
|
+
eps2 = Integer(0)
|
|
3044
|
+
else:
|
|
3045
|
+
eps2 = Integer(mat1.entryValues[index2])
|
|
3046
|
+
if eps2 != 1 and eps2 != -1:
|
|
3047
|
+
raise ValueError(f"First child in the Three Sum "
|
|
3048
|
+
f"has 1 or -1 in the entry "
|
|
3049
|
+
f"row {row2_index} and column {extra_column} "
|
|
3050
|
+
f"but got {eps2}")
|
|
3051
|
+
|
|
3052
|
+
extra_key = ElementKey((eps1, child1_row_keys[row1_index],
|
|
3053
|
+
eps2, child1_row_keys[row2_index]),
|
|
3054
|
+
composition=True)
|
|
3055
|
+
child1_column_keys += (extra_key,)
|
|
3056
|
+
|
|
3057
|
+
child1 = create_DecompositionNode(child1_dec, matrix=None,
|
|
3058
|
+
row_keys=child1_row_keys,
|
|
3059
|
+
column_keys=child1_column_keys,
|
|
3060
|
+
base_ring=self.base_ring())
|
|
3061
|
+
|
|
3062
|
+
child2_nrows = CMRseymourNumRows(child2_dec)
|
|
3063
|
+
child2_ncols = CMRseymourNumColumns(child2_dec)
|
|
3064
|
+
|
|
3065
|
+
child2_row_keys = tuple(self._CMRelement_to_key(parent_rows2[i])
|
|
3066
|
+
for i in range(1, child2_nrows))
|
|
3067
|
+
child2_column_keys = tuple(self._CMRelement_to_key(parent_columns2[i])
|
|
3068
|
+
for i in range(child2_ncols))
|
|
3069
|
+
|
|
3070
|
+
column1_index = second_special_columns[0]
|
|
3071
|
+
extra_row = second_special_rows[0]
|
|
3072
|
+
CMR_CALL(CMRchrmatFindEntry(mat2, extra_row, column1_index, &index1))
|
|
3073
|
+
if index1 == SIZE_MAX:
|
|
3074
|
+
eps1 = Integer(0)
|
|
3075
|
+
else:
|
|
3076
|
+
eps1 = Integer(mat1.entryValues[index1])
|
|
3077
|
+
if eps1 != 1 and eps1 != -1:
|
|
3078
|
+
raise ValueError(f"Second child in the Three Sum "
|
|
3079
|
+
f"has 1 or -1 in the entry "
|
|
3080
|
+
f"row {extra_row} and column {column1_index} "
|
|
3081
|
+
f"but got {eps1}")
|
|
3082
|
+
column2_index = second_special_columns[1]
|
|
3083
|
+
CMR_CALL(CMRchrmatFindEntry(mat2, extra_row, column2_index, &index2))
|
|
3084
|
+
if index2 == SIZE_MAX:
|
|
3085
|
+
eps2 = Integer(0)
|
|
3086
|
+
else:
|
|
3087
|
+
eps2 = Integer(mat2.entryValues[index2])
|
|
3088
|
+
if eps2 != 1:
|
|
3089
|
+
raise ValueError(f"Second child in the Three Sum "
|
|
3090
|
+
f"has 1 in the entry "
|
|
3091
|
+
f"row {extra_row} and column {column2_index} "
|
|
3092
|
+
f"but got {eps2}")
|
|
3093
|
+
|
|
3094
|
+
extra_key = ElementKey((eps1, child2_column_keys[column1_index],
|
|
3095
|
+
eps2, child2_column_keys[column2_index]),
|
|
3096
|
+
composition=True)
|
|
3097
|
+
child2_row_keys = (extra_key,) + child2_row_keys
|
|
3098
|
+
|
|
3099
|
+
child2 = create_DecompositionNode(child2_dec, matrix=None,
|
|
3100
|
+
row_keys=child2_row_keys,
|
|
3101
|
+
column_keys=child2_column_keys,
|
|
3102
|
+
base_ring=self.base_ring())
|
|
3103
|
+
|
|
3104
|
+
self._child_nodes = ((child1, child1_row_keys, child1_column_keys),
|
|
3105
|
+
(child2, child2_row_keys, child2_column_keys))
|
|
3106
|
+
return self._child_nodes
|
|
3107
|
+
|
|
3108
|
+
def is_distributed_ranks(self):
|
|
3109
|
+
r"""
|
|
3110
|
+
Return ``False`` for the 3-sum node ``self``.
|
|
3111
|
+
|
|
3112
|
+
``distributed_ranks`` is named after the two rank 1 off-diagonal blocks.
|
|
3113
|
+
|
|
3114
|
+
.. SEEALSO::
|
|
3115
|
+
|
|
3116
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.three_sum`
|
|
3117
|
+
|
|
3118
|
+
EXAMPLES::
|
|
3119
|
+
|
|
3120
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3121
|
+
sage: R12_large = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
3122
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
3123
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
3124
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
3125
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
3126
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
3127
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
3128
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
3129
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
3130
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
3131
|
+
sage: result, certificate = R12_large.is_totally_unimodular(certificate=True,
|
|
3132
|
+
....: decompose_strategy="three_pivot")
|
|
3133
|
+
sage: C = certificate; C
|
|
3134
|
+
ThreeSumNode (9×12)
|
|
3135
|
+
sage: C.is_distributed_ranks()
|
|
3136
|
+
False
|
|
3137
|
+
sage: C.is_concentrated_rank()
|
|
3138
|
+
True
|
|
3139
|
+
"""
|
|
3140
|
+
return False
|
|
3141
|
+
|
|
3142
|
+
def is_concentrated_rank(self):
|
|
3143
|
+
r"""
|
|
3144
|
+
Return ``True`` for the 3-sum node ``self``.
|
|
3145
|
+
|
|
3146
|
+
``concentrated_rank`` is named after the rank 2 and rank 0 off-diagonal blocks.
|
|
3147
|
+
|
|
3148
|
+
.. SEEALSO::
|
|
3149
|
+
|
|
3150
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.three_sum`
|
|
3151
|
+
"""
|
|
3152
|
+
return True
|
|
3153
|
+
|
|
3154
|
+
def block_matrix_form(self):
|
|
3155
|
+
r"""
|
|
3156
|
+
Return the block matrix constructed from the 3-sum of children.
|
|
3157
|
+
|
|
3158
|
+
EXAMPLES::
|
|
3159
|
+
|
|
3160
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3161
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
3162
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
3163
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
3164
|
+
sage: R12
|
|
3165
|
+
[ 1 0 1 1 0 0]
|
|
3166
|
+
[ 0 1 1 1 0 0]
|
|
3167
|
+
[ 1 0 1 0 1 1]
|
|
3168
|
+
[ 0 -1 0 -1 1 1]
|
|
3169
|
+
[ 1 0 1 0 1 0]
|
|
3170
|
+
[ 0 -1 0 -1 0 1]
|
|
3171
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
3172
|
+
....: decompose_strategy="three_pivot")
|
|
3173
|
+
sage: C = certificate; C
|
|
3174
|
+
ThreeSumNode (6×6)
|
|
3175
|
+
sage: C.matrix()
|
|
3176
|
+
[ 1 0 1 1 0 0]
|
|
3177
|
+
[ 0 1 1 1 0 0]
|
|
3178
|
+
[ 1 0 1 0 1 1]
|
|
3179
|
+
[ 0 -1 0 -1 1 1]
|
|
3180
|
+
[ 1 0 1 0 1 0]
|
|
3181
|
+
[ 0 -1 0 -1 0 1]
|
|
3182
|
+
sage: C.summand_matrices()
|
|
3183
|
+
(
|
|
3184
|
+
[ 1 1 0 0]
|
|
3185
|
+
[ 1 0 1 1 0] [ 1 0 1 1]
|
|
3186
|
+
[ 0 1 1 1 0] [ 0 -1 1 1]
|
|
3187
|
+
[ 1 0 1 0 1] [ 1 0 1 0]
|
|
3188
|
+
[ 0 -1 0 -1 1], [ 0 -1 0 1]
|
|
3189
|
+
)
|
|
3190
|
+
sage: C.child_keys()
|
|
3191
|
+
(((r0, r1, r2, r3), (c0, c1, c2, c3, +r2+r3)),
|
|
3192
|
+
((+c0+c3, r2, r3, r4, r5), (c0, c3, c4, c5)))
|
|
3193
|
+
sage: C.block_matrix_form()
|
|
3194
|
+
[ 1 0 1 1 0 0]
|
|
3195
|
+
[ 0 1 1 1 0 0]
|
|
3196
|
+
[ 1 0 1 0 1 1]
|
|
3197
|
+
[ 0 -1 0 -1 1 1]
|
|
3198
|
+
[ 1 0 1 0 1 0]
|
|
3199
|
+
[ 0 -1 0 -1 0 1]
|
|
3200
|
+
"""
|
|
3201
|
+
M1, M2 = self.summand_matrices()
|
|
3202
|
+
return Matrix_cmr_chr_sparse.three_sum(M1, M2)
|
|
3203
|
+
|
|
3204
|
+
def permuted_block_matrix(self):
|
|
3205
|
+
r"""
|
|
3206
|
+
Return the permutation matrices between the matrix
|
|
3207
|
+
and the block matrix form.
|
|
3208
|
+
|
|
3209
|
+
OUTPUT: a tuple ``(Prow, BlockMatrix, Pcolumn)``, where
|
|
3210
|
+
``self.matrix() == Prow * BlockMatrix * Pcolumn``, and
|
|
3211
|
+
``BlockMatrix == self.block_matrix_form()``.
|
|
3212
|
+
|
|
3213
|
+
EXAMPLES::
|
|
3214
|
+
|
|
3215
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3216
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
3217
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
3218
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
3219
|
+
sage: R12
|
|
3220
|
+
[ 1 0 1 1 0 0]
|
|
3221
|
+
[ 0 1 1 1 0 0]
|
|
3222
|
+
[ 1 0 1 0 1 1]
|
|
3223
|
+
[ 0 -1 0 -1 1 1]
|
|
3224
|
+
[ 1 0 1 0 1 0]
|
|
3225
|
+
[ 0 -1 0 -1 0 1]
|
|
3226
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
3227
|
+
....: decompose_strategy="three_pivot")
|
|
3228
|
+
sage: C = certificate; C
|
|
3229
|
+
ThreeSumNode (6×6)
|
|
3230
|
+
sage: C.matrix()
|
|
3231
|
+
[ 1 0 1 1 0 0]
|
|
3232
|
+
[ 0 1 1 1 0 0]
|
|
3233
|
+
[ 1 0 1 0 1 1]
|
|
3234
|
+
[ 0 -1 0 -1 1 1]
|
|
3235
|
+
[ 1 0 1 0 1 0]
|
|
3236
|
+
[ 0 -1 0 -1 0 1]
|
|
3237
|
+
sage: C.block_matrix_form()
|
|
3238
|
+
[ 1 0 1 1 0 0]
|
|
3239
|
+
[ 0 1 1 1 0 0]
|
|
3240
|
+
[ 1 0 1 0 1 1]
|
|
3241
|
+
[ 0 -1 0 -1 1 1]
|
|
3242
|
+
[ 1 0 1 0 1 0]
|
|
3243
|
+
[ 0 -1 0 -1 0 1]
|
|
3244
|
+
sage: P_row, block_matrix, P_column = C.permuted_block_matrix()
|
|
3245
|
+
sage: P_row^(-1) * C.matrix() * P_column^(-1) == block_matrix
|
|
3246
|
+
True
|
|
3247
|
+
"""
|
|
3248
|
+
from sage.combinat.permutation import Permutation
|
|
3249
|
+
cdef CMR_SEYMOUR_NODE *child_dec
|
|
3250
|
+
cdef CMR_ELEMENT *parent_rows
|
|
3251
|
+
cdef CMR_ELEMENT *parent_columns
|
|
3252
|
+
children_row = tuple()
|
|
3253
|
+
children_column = tuple()
|
|
3254
|
+
for index in range(self.nchildren()):
|
|
3255
|
+
child_dec = CMRseymourChild(self._dec, index)
|
|
3256
|
+
parent_rows = CMRseymourChildRowsToParent(self._dec, index)
|
|
3257
|
+
parent_columns = CMRseymourChildColumnsToParent(self._dec, index)
|
|
3258
|
+
child_nrows = CMRseymourNumRows(child_dec)
|
|
3259
|
+
child_ncols = CMRseymourNumColumns(child_dec)
|
|
3260
|
+
|
|
3261
|
+
if parent_rows == NULL or all(parent_rows[i] == 0 for i in range(child_nrows)):
|
|
3262
|
+
raise ValueError(f"Child {index} does not have parents rows")
|
|
3263
|
+
parent_rows_tuple = tuple(parent_rows[i] for i in range(child_nrows))
|
|
3264
|
+
|
|
3265
|
+
if parent_columns == NULL or all(parent_columns[i] == 0 for i in range(child_ncols)):
|
|
3266
|
+
raise ValueError(f"Child {index} does not have parents columns")
|
|
3267
|
+
parent_columns_tuple = tuple(parent_columns[i] for i in range(child_ncols))
|
|
3268
|
+
if index == 0:
|
|
3269
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
3270
|
+
for element in parent_rows_tuple[:-2])
|
|
3271
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
3272
|
+
for element in parent_columns_tuple[:-1])
|
|
3273
|
+
else:
|
|
3274
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
3275
|
+
for element in parent_rows_tuple[1:])
|
|
3276
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
3277
|
+
for element in parent_columns_tuple[2:])
|
|
3278
|
+
children_row += child_row
|
|
3279
|
+
children_column += child_column
|
|
3280
|
+
P_row = Permutation(list(children_row)).to_matrix()
|
|
3281
|
+
P_column = Permutation(list(children_column)).to_matrix().transpose()
|
|
3282
|
+
return (P_row, self.block_matrix_form(), P_column)
|
|
3283
|
+
|
|
3284
|
+
|
|
3285
|
+
cdef class YSumNode(SumNode):
|
|
3286
|
+
|
|
3287
|
+
def _children(self):
|
|
3288
|
+
r"""
|
|
3289
|
+
Return a tuple of the tuples of the two children
|
|
3290
|
+
and their row and column keys.
|
|
3291
|
+
|
|
3292
|
+
.. SEEALSO::
|
|
3293
|
+
|
|
3294
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.y_sum`
|
|
3295
|
+
|
|
3296
|
+
TESTS:
|
|
3297
|
+
|
|
3298
|
+
This is test ``YsumR12`` in CMR's ``test_tu.cpp``::
|
|
3299
|
+
|
|
3300
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3301
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
3302
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
3303
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
3304
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
3305
|
+
....: decompose_strategy="y_pivot",
|
|
3306
|
+
....: row_keys=range(6),
|
|
3307
|
+
....: column_keys='abcdef')
|
|
3308
|
+
sage: certificate.child_keys()
|
|
3309
|
+
((0, 1, a, 3, 4, 5), (2, b, c, d, e, f))
|
|
3310
|
+
sage: C = certificate.child_nodes()[0]
|
|
3311
|
+
sage: C1, C2 = C.child_nodes()
|
|
3312
|
+
sage: C1.matrix()
|
|
3313
|
+
[ 0 0 1 1]
|
|
3314
|
+
[ 1 1 1 0]
|
|
3315
|
+
[ 0 1 0 -1]
|
|
3316
|
+
[-1 0 -1 0]
|
|
3317
|
+
[-1 0 -1 -1]
|
|
3318
|
+
sage: C2.matrix()
|
|
3319
|
+
[-1 1 -1 -1]
|
|
3320
|
+
[ 0 1 -1 -1]
|
|
3321
|
+
[ 1 0 1 1]
|
|
3322
|
+
[ 0 1 0 -1]
|
|
3323
|
+
[ 1 0 0 1]
|
|
3324
|
+
sage: C.child_keys()
|
|
3325
|
+
(((0, 1, a, 3, -2+3), (b, c, d, 2)), ((+0-b, 0, 3, 4, 5), (b, 2, e, f)))
|
|
3326
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
3327
|
+
sage: node = UnknownNode(R12,
|
|
3328
|
+
....: row_keys=range(6),
|
|
3329
|
+
....: column_keys='abcdef'); node
|
|
3330
|
+
UnknownNode (6×6)
|
|
3331
|
+
sage: C0 = node.complete_decomposition(
|
|
3332
|
+
....: decompose_strategy="y_pivot",
|
|
3333
|
+
....: )
|
|
3334
|
+
sage: C0
|
|
3335
|
+
PivotsNode (6×6)
|
|
3336
|
+
sage: unicode_art(C0)
|
|
3337
|
+
PivotsNode (6×6)
|
|
3338
|
+
│
|
|
3339
|
+
╭─YSumNode (6×6)──╮
|
|
3340
|
+
│ │
|
|
3341
|
+
GraphicNode (5×4) GraphicNode (5×4)
|
|
3342
|
+
sage: unicode_art(node)
|
|
3343
|
+
UnknownNode (6×6)
|
|
3344
|
+
"""
|
|
3345
|
+
if self._child_nodes is not None:
|
|
3346
|
+
return self._child_nodes
|
|
3347
|
+
|
|
3348
|
+
if self.nchildren() != 2:
|
|
3349
|
+
raise ValueError(f"YSumNode has exactly two children not {self.nchildren()}!")
|
|
3350
|
+
|
|
3351
|
+
self.set_default_keys()
|
|
3352
|
+
|
|
3353
|
+
cdef CMR_SEYMOUR_NODE *child1_dec = CMRseymourChild(self._dec, 0)
|
|
3354
|
+
cdef CMR_ELEMENT *parent_rows1 = CMRseymourChildRowsToParent(self._dec, 0)
|
|
3355
|
+
cdef CMR_ELEMENT *parent_columns1 = CMRseymourChildColumnsToParent(self._dec, 0)
|
|
3356
|
+
cdef CMR_CHRMAT *mat1 = CMRseymourGetMatrix(child1_dec)
|
|
3357
|
+
|
|
3358
|
+
cdef CMR_SEYMOUR_NODE *child2_dec = CMRseymourChild(self._dec, 1)
|
|
3359
|
+
cdef CMR_ELEMENT *parent_rows2 = CMRseymourChildRowsToParent(self._dec, 1)
|
|
3360
|
+
cdef CMR_ELEMENT *parent_columns2 = CMRseymourChildColumnsToParent(self._dec, 1)
|
|
3361
|
+
cdef CMR_CHRMAT *mat2 = CMRseymourGetMatrix(child2_dec)
|
|
3362
|
+
|
|
3363
|
+
cdef size_t index1
|
|
3364
|
+
|
|
3365
|
+
child1_nrows = CMRseymourNumRows(child1_dec)
|
|
3366
|
+
child1_ncols = CMRseymourNumColumns(child1_dec)
|
|
3367
|
+
|
|
3368
|
+
child1_row_keys = tuple(self._CMRelement_to_key(parent_rows1[i])
|
|
3369
|
+
for i in range(child1_nrows - 1))
|
|
3370
|
+
child1_column_keys = tuple(self._CMRelement_to_key(parent_columns1[i])
|
|
3371
|
+
for i in range(child1_ncols))
|
|
3372
|
+
|
|
3373
|
+
row1_index = child1_nrows - 1
|
|
3374
|
+
column1_index = child1_ncols - 1
|
|
3375
|
+
CMR_CALL(CMRchrmatFindEntry(mat1, row1_index, column1_index, &index1))
|
|
3376
|
+
if index1 == SIZE_MAX:
|
|
3377
|
+
eps1 = Integer(0)
|
|
3378
|
+
else:
|
|
3379
|
+
eps1 = Integer(mat1.entryValues[index1])
|
|
3380
|
+
if eps1 != 1 and eps1 != -1:
|
|
3381
|
+
raise ValueError(f"First child in the Y Sum "
|
|
3382
|
+
f"has 1 or -1 in the entry "
|
|
3383
|
+
f"row {row1_index} and column {column1_index} "
|
|
3384
|
+
f"but got {eps1}")
|
|
3385
|
+
|
|
3386
|
+
extra_key = ElementKey((1, child1_row_keys[row1_index - 1],
|
|
3387
|
+
eps1, child1_column_keys[column1_index]),
|
|
3388
|
+
composition=True)
|
|
3389
|
+
child1_row_keys += (extra_key,)
|
|
3390
|
+
|
|
3391
|
+
child1 = create_DecompositionNode(child1_dec, matrix=None,
|
|
3392
|
+
row_keys=child1_row_keys,
|
|
3393
|
+
column_keys=child1_column_keys,
|
|
3394
|
+
base_ring=self.base_ring())
|
|
3395
|
+
|
|
3396
|
+
child2_nrows = CMRseymourNumRows(child2_dec)
|
|
3397
|
+
child2_ncols = CMRseymourNumColumns(child2_dec)
|
|
3398
|
+
|
|
3399
|
+
child2_row_keys = tuple(self._CMRelement_to_key(parent_rows2[i])
|
|
3400
|
+
for i in range(1, child2_nrows))
|
|
3401
|
+
child2_column_keys = tuple(self._CMRelement_to_key(parent_columns2[i])
|
|
3402
|
+
for i in range(child2_ncols))
|
|
3403
|
+
|
|
3404
|
+
row2_index = 0
|
|
3405
|
+
column2_index = 0
|
|
3406
|
+
CMR_CALL(CMRchrmatFindEntry(mat2, row2_index, column2_index, &index1))
|
|
3407
|
+
if index1 == SIZE_MAX:
|
|
3408
|
+
eps1 = Integer(0)
|
|
3409
|
+
else:
|
|
3410
|
+
eps1 = Integer(mat2.entryValues[index1])
|
|
3411
|
+
|
|
3412
|
+
if eps1 != 1 and eps1 != -1:
|
|
3413
|
+
raise ValueError(f"Second child in the Y Sum "
|
|
3414
|
+
f"has 1 or -1 in the entry "
|
|
3415
|
+
f"row {row2_index} and column {column2_index} "
|
|
3416
|
+
f"but got {eps1}")
|
|
3417
|
+
|
|
3418
|
+
extra_key = ElementKey((1, child2_row_keys[row2_index],
|
|
3419
|
+
eps1, child2_column_keys[column2_index]),
|
|
3420
|
+
composition=True)
|
|
3421
|
+
child2_row_keys = (extra_key,) + child2_row_keys
|
|
3422
|
+
|
|
3423
|
+
child2 = create_DecompositionNode(child2_dec, matrix=None,
|
|
3424
|
+
row_keys=child2_row_keys,
|
|
3425
|
+
column_keys=child2_column_keys,
|
|
3426
|
+
base_ring=self.base_ring())
|
|
3427
|
+
|
|
3428
|
+
self._child_nodes = ((child1, child1_row_keys, child1_column_keys),
|
|
3429
|
+
(child2, child2_row_keys, child2_column_keys))
|
|
3430
|
+
return self._child_nodes
|
|
3431
|
+
|
|
3432
|
+
def is_distributed_ranks(self):
|
|
3433
|
+
r"""
|
|
3434
|
+
Return ``True`` for the `\Delta`-sum node ``self``.
|
|
3435
|
+
|
|
3436
|
+
``distributed_ranks`` is named after the two rank 1 off-diagonal blocks.
|
|
3437
|
+
|
|
3438
|
+
.. SEEALSO::
|
|
3439
|
+
|
|
3440
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.y_sum`
|
|
3441
|
+
|
|
3442
|
+
EXAMPLES::
|
|
3443
|
+
|
|
3444
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3445
|
+
sage: R12_large = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
3446
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
3447
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
3448
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
3449
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
3450
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
3451
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
3452
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
3453
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
3454
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
3455
|
+
sage: result, certificate = R12_large.is_totally_unimodular(certificate=True,
|
|
3456
|
+
....: decompose_strategy="y_pivot")
|
|
3457
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
3458
|
+
YSumNode (9×12)
|
|
3459
|
+
sage: C.is_distributed_ranks()
|
|
3460
|
+
True
|
|
3461
|
+
sage: C.is_concentrated_rank()
|
|
3462
|
+
False
|
|
3463
|
+
"""
|
|
3464
|
+
return True
|
|
3465
|
+
|
|
3466
|
+
def is_concentrated_rank(self):
|
|
3467
|
+
r"""
|
|
3468
|
+
Return ``False`` for the `\Delta`-sum node ``self``.
|
|
3469
|
+
|
|
3470
|
+
``concentrated_rank`` is named after the rank 2 and rank 0 off-diagonal blocks.
|
|
3471
|
+
|
|
3472
|
+
.. SEEALSO::
|
|
3473
|
+
|
|
3474
|
+
:meth:`sage.matrix.matrix_cmr_sparse.Matrix_cmr_chr_sparse.y_sum`
|
|
3475
|
+
"""
|
|
3476
|
+
return False
|
|
3477
|
+
|
|
3478
|
+
def block_matrix_form(self):
|
|
3479
|
+
r"""
|
|
3480
|
+
Return the block matrix constructed from the Y-sum of children.
|
|
3481
|
+
|
|
3482
|
+
EXAMPLES::
|
|
3483
|
+
|
|
3484
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3485
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
3486
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
3487
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
3488
|
+
sage: R12
|
|
3489
|
+
[ 1 0 1 1 0 0]
|
|
3490
|
+
[ 0 1 1 1 0 0]
|
|
3491
|
+
[ 1 0 1 0 1 1]
|
|
3492
|
+
[ 0 -1 0 -1 1 1]
|
|
3493
|
+
[ 1 0 1 0 1 0]
|
|
3494
|
+
[ 0 -1 0 -1 0 1]
|
|
3495
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
3496
|
+
....: decompose_strategy="y_pivot")
|
|
3497
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
3498
|
+
YSumNode (6×6)
|
|
3499
|
+
sage: C.matrix()
|
|
3500
|
+
[ 1 0 0 1 -1 -1]
|
|
3501
|
+
[ 0 1 1 1 0 0]
|
|
3502
|
+
[-1 0 1 0 1 1]
|
|
3503
|
+
[ 0 -1 0 -1 1 1]
|
|
3504
|
+
[ 1 0 0 0 0 -1]
|
|
3505
|
+
[ 0 -1 0 -1 0 1]
|
|
3506
|
+
sage: C.summand_matrices()
|
|
3507
|
+
(
|
|
3508
|
+
[ 0 0 1 1] [-1 1 -1 -1]
|
|
3509
|
+
[ 1 1 1 0] [ 0 1 -1 -1]
|
|
3510
|
+
[ 0 1 0 -1] [ 1 0 1 1]
|
|
3511
|
+
[-1 0 -1 0] [ 0 1 0 -1]
|
|
3512
|
+
[-1 0 -1 -1], [ 1 0 0 1]
|
|
3513
|
+
)
|
|
3514
|
+
sage: C.row_keys()
|
|
3515
|
+
(r0, r1, c0, r3, r4, r5)
|
|
3516
|
+
sage: C.column_keys()
|
|
3517
|
+
(r2, c1, c2, c3, c4, c5)
|
|
3518
|
+
sage: C.child_keys()
|
|
3519
|
+
(((r0, r1, c0, r3, -r2+r3), (c1, c2, c3, r2)),
|
|
3520
|
+
((-c1+r0, r0, r3, r4, r5), (c1, r2, c4, c5)))
|
|
3521
|
+
sage: C.block_matrix_form()
|
|
3522
|
+
[ 0 0 1 1 -1 -1]
|
|
3523
|
+
[ 1 1 1 0 0 0]
|
|
3524
|
+
[ 0 1 0 -1 1 1]
|
|
3525
|
+
[-1 0 -1 0 1 1]
|
|
3526
|
+
[ 0 0 0 1 0 -1]
|
|
3527
|
+
[-1 0 -1 0 0 1]
|
|
3528
|
+
"""
|
|
3529
|
+
M1, M2 = self.summand_matrices()
|
|
3530
|
+
return Matrix_cmr_chr_sparse.y_sum(M1, M2)
|
|
3531
|
+
|
|
3532
|
+
def permuted_block_matrix(self):
|
|
3533
|
+
r"""
|
|
3534
|
+
Return the permutation matrices between the matrix
|
|
3535
|
+
and the block matrix form.
|
|
3536
|
+
|
|
3537
|
+
OUTPUT: a tuple ``(Prow, BlockMatrix, Pcolumn)``, where
|
|
3538
|
+
``self.matrix() == Prow * BlockMatrix * Pcolumn``, and
|
|
3539
|
+
``BlockMatrix == self.block_matrix_form()``.
|
|
3540
|
+
|
|
3541
|
+
EXAMPLES::
|
|
3542
|
+
|
|
3543
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3544
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 6, sparse=True),
|
|
3545
|
+
....: [[1,0,1,1,0,0],[0,1,1,1,0,0],[1,0,1,0,1,1],
|
|
3546
|
+
....: [0,-1,0,-1,1,1],[1,0,1,0,1,0],[0,-1,0,-1,0,1]])
|
|
3547
|
+
sage: R12
|
|
3548
|
+
[ 1 0 1 1 0 0]
|
|
3549
|
+
[ 0 1 1 1 0 0]
|
|
3550
|
+
[ 1 0 1 0 1 1]
|
|
3551
|
+
[ 0 -1 0 -1 1 1]
|
|
3552
|
+
[ 1 0 1 0 1 0]
|
|
3553
|
+
[ 0 -1 0 -1 0 1]
|
|
3554
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
3555
|
+
....: decompose_strategy="y_pivot")
|
|
3556
|
+
sage: C = certificate.child_nodes()[0]; C
|
|
3557
|
+
YSumNode (6×6)
|
|
3558
|
+
sage: C.matrix()
|
|
3559
|
+
[ 1 0 0 1 -1 -1]
|
|
3560
|
+
[ 0 1 1 1 0 0]
|
|
3561
|
+
[-1 0 1 0 1 1]
|
|
3562
|
+
[ 0 -1 0 -1 1 1]
|
|
3563
|
+
[ 1 0 0 0 0 -1]
|
|
3564
|
+
[ 0 -1 0 -1 0 1]
|
|
3565
|
+
sage: C.block_matrix_form()
|
|
3566
|
+
[ 0 0 1 1 -1 -1]
|
|
3567
|
+
[ 1 1 1 0 0 0]
|
|
3568
|
+
[ 0 1 0 -1 1 1]
|
|
3569
|
+
[-1 0 -1 0 1 1]
|
|
3570
|
+
[ 0 0 0 1 0 -1]
|
|
3571
|
+
[-1 0 -1 0 0 1]
|
|
3572
|
+
sage: P_row, block_matrix, P_column = C.permuted_block_matrix()
|
|
3573
|
+
sage: P_row^(-1) * C.matrix() * P_column^(-1) == block_matrix
|
|
3574
|
+
True
|
|
3575
|
+
"""
|
|
3576
|
+
from sage.combinat.permutation import Permutation
|
|
3577
|
+
cdef CMR_SEYMOUR_NODE *child_dec
|
|
3578
|
+
cdef CMR_ELEMENT *parent_rows
|
|
3579
|
+
cdef CMR_ELEMENT *parent_columns
|
|
3580
|
+
children_row = tuple()
|
|
3581
|
+
children_column = tuple()
|
|
3582
|
+
for index in range(self.nchildren()):
|
|
3583
|
+
child_dec = CMRseymourChild(self._dec, index)
|
|
3584
|
+
parent_rows = CMRseymourChildRowsToParent(self._dec, index)
|
|
3585
|
+
parent_columns = CMRseymourChildColumnsToParent(self._dec, index)
|
|
3586
|
+
child_nrows = CMRseymourNumRows(child_dec)
|
|
3587
|
+
child_ncols = CMRseymourNumColumns(child_dec)
|
|
3588
|
+
|
|
3589
|
+
if parent_rows == NULL or all(parent_rows[i] == 0 for i in range(child_nrows)):
|
|
3590
|
+
raise ValueError(f"Child {index} does not have parents rows")
|
|
3591
|
+
parent_rows_tuple = tuple(parent_rows[i] for i in range(child_nrows))
|
|
3592
|
+
|
|
3593
|
+
if parent_columns == NULL or all(parent_columns[i] == 0 for i in range(child_ncols)):
|
|
3594
|
+
raise ValueError(f"Child {index} does not have parents columns")
|
|
3595
|
+
parent_columns_tuple = tuple(parent_columns[i] for i in range(child_ncols))
|
|
3596
|
+
if index == 0:
|
|
3597
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
3598
|
+
for element in parent_rows_tuple[:-2])
|
|
3599
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
3600
|
+
for element in parent_columns_tuple[:-1])
|
|
3601
|
+
else:
|
|
3602
|
+
child_row = tuple(CMRelementToRowIndex(element) + 1
|
|
3603
|
+
for element in parent_rows_tuple[2:])
|
|
3604
|
+
child_column = tuple(CMRelementToColumnIndex(element) + 1
|
|
3605
|
+
for element in parent_columns_tuple[1:])
|
|
3606
|
+
children_row += child_row
|
|
3607
|
+
children_column += child_column
|
|
3608
|
+
P_row = Permutation(list(children_row)).to_matrix()
|
|
3609
|
+
P_column = Permutation(list(children_column)).to_matrix().transpose()
|
|
3610
|
+
return (P_row, self.block_matrix_form(), P_column)
|
|
3611
|
+
|
|
3612
|
+
|
|
3613
|
+
cdef class BaseGraphicNode(DecompositionNode):
|
|
3614
|
+
|
|
3615
|
+
def __init__(self, matrix=None,
|
|
3616
|
+
graph=None, forest_edges=None, coforest_edges=None,
|
|
3617
|
+
row_keys=None, column_keys=None, base_ring=None):
|
|
3618
|
+
r"""
|
|
3619
|
+
Base class for :class:`GraphicNode`, :class:`CographicNode`, and :class:`PlanarNode`
|
|
3620
|
+
|
|
3621
|
+
If ``base_ring`` is `\GF{2}`, then it represents a graphic/cographic/planar matroid.
|
|
3622
|
+
|
|
3623
|
+
Suppose that ``self.matrix()`` is a graphic matrix of a graph `G`
|
|
3624
|
+
with respect to `T`, a spanning forest of the graph `G`.
|
|
3625
|
+
|
|
3626
|
+
- ``self._graph`` is the graph `G = (V,E)`.
|
|
3627
|
+
|
|
3628
|
+
- ``self._forest_edges`` is the edges of `T`.
|
|
3629
|
+
|
|
3630
|
+
- ``self._coforest_edges`` is the edges of `E \setminus T`.
|
|
3631
|
+
|
|
3632
|
+
If ``base_ring`` is `\GF{3}` or `\ZZ`, then it represents a network/conetwork
|
|
3633
|
+
or a both network and conetwork matrix.
|
|
3634
|
+
|
|
3635
|
+
Suppose that ``self.matrix()`` is a network matrix of a digraph `D`
|
|
3636
|
+
with respect to `T`, a directed spanning forest of the underlying undirected graph.
|
|
3637
|
+
|
|
3638
|
+
- ``self._graph`` is the digraph `D = (V,A)`.
|
|
3639
|
+
|
|
3640
|
+
- ``self._forest_edges`` is the arcs of `T`.
|
|
3641
|
+
|
|
3642
|
+
- ``self._coforest_edges`` is the arcs of `A \setminus T`.
|
|
3643
|
+
"""
|
|
3644
|
+
super().__init__(matrix=matrix, row_keys=row_keys, column_keys=column_keys,
|
|
3645
|
+
base_ring=base_ring)
|
|
3646
|
+
self._graph = graph
|
|
3647
|
+
self._forest_edges = forest_edges
|
|
3648
|
+
self._coforest_edges = coforest_edges
|
|
3649
|
+
|
|
3650
|
+
def graph(self):
|
|
3651
|
+
r"""
|
|
3652
|
+
Return the graph representing ``self``.
|
|
3653
|
+
|
|
3654
|
+
If ``self.base_ring()`` is `\GF{2}`, then return an undirected graph.
|
|
3655
|
+
If ``self.base_ring()`` is `\GF{3}` or `\ZZ`, then return directed graph.
|
|
3656
|
+
|
|
3657
|
+
EXAMPLES:
|
|
3658
|
+
|
|
3659
|
+
Undirected graph::
|
|
3660
|
+
|
|
3661
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3662
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
3663
|
+
....: [[1, 0], [1, 1], [0,1]]); M
|
|
3664
|
+
[1 0]
|
|
3665
|
+
[1 1]
|
|
3666
|
+
[0 1]
|
|
3667
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True)
|
|
3668
|
+
sage: result, certificate
|
|
3669
|
+
(True, GraphicNode (3×2))
|
|
3670
|
+
sage: G = certificate.graph(); G
|
|
3671
|
+
Graph on 4 vertices
|
|
3672
|
+
sage: G.vertices(sort=True)
|
|
3673
|
+
[1, 2, 7, 12]
|
|
3674
|
+
sage: G.edges(sort=True)
|
|
3675
|
+
[(1, 2, None), (1, 7, None), (1, 12, None), (2, 7, None), (7, 12, None)]
|
|
3676
|
+
|
|
3677
|
+
Directed graph::
|
|
3678
|
+
|
|
3679
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3680
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
3681
|
+
....: [[1, 0], [-1, 1], [0,-1]]); M
|
|
3682
|
+
[ 1 0]
|
|
3683
|
+
[-1 1]
|
|
3684
|
+
[ 0 -1]
|
|
3685
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
3686
|
+
sage: result, certificate
|
|
3687
|
+
(True, GraphicNode (3×2))
|
|
3688
|
+
sage: G = certificate.graph(); G
|
|
3689
|
+
Digraph on 4 vertices
|
|
3690
|
+
sage: G.vertices(sort=True)
|
|
3691
|
+
[1, 2, 7, 12]
|
|
3692
|
+
sage: G.edges(sort=True)
|
|
3693
|
+
[(2, 1, None), (2, 7, None), (7, 1, None), (7, 12, None), (12, 1, None)]
|
|
3694
|
+
"""
|
|
3695
|
+
if self._graph is not None:
|
|
3696
|
+
return self._graph
|
|
3697
|
+
base_ring = self.base_ring()
|
|
3698
|
+
if base_ring.characteristic() == 2:
|
|
3699
|
+
self._graph = _sage_graph(CMRseymourGraph(self._dec))
|
|
3700
|
+
else:
|
|
3701
|
+
self._graph = _sage_digraph(CMRseymourGraph(self._dec),
|
|
3702
|
+
CMRseymourGraphArcsReversed(self._dec))
|
|
3703
|
+
return self._graph
|
|
3704
|
+
|
|
3705
|
+
def forest_edges(self):
|
|
3706
|
+
r"""
|
|
3707
|
+
Return the forest edges of ``self``.
|
|
3708
|
+
|
|
3709
|
+
If ``self.base_ring()`` is `\GF{2}`, then return edges.
|
|
3710
|
+
If ``self.base_ring()`` is `\GF{3}` or `\ZZ`, then arcs.
|
|
3711
|
+
|
|
3712
|
+
EXAMPLES:
|
|
3713
|
+
|
|
3714
|
+
Undirected graph::
|
|
3715
|
+
|
|
3716
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3717
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
3718
|
+
....: [[1, 0], [1, 1], [0,1]]); M
|
|
3719
|
+
[1 0]
|
|
3720
|
+
[1 1]
|
|
3721
|
+
[0 1]
|
|
3722
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True)
|
|
3723
|
+
sage: result, certificate
|
|
3724
|
+
(True, GraphicNode (3×2))
|
|
3725
|
+
sage: certificate.forest_edges()
|
|
3726
|
+
((1, 2), (7, 1), (12, 7))
|
|
3727
|
+
sage: certificate.coforest_edges()
|
|
3728
|
+
((2, 7), (1, 12))
|
|
3729
|
+
|
|
3730
|
+
Directed graph::
|
|
3731
|
+
|
|
3732
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3733
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
3734
|
+
....: [[1, 0], [-1, 1], [0,-1]]); M
|
|
3735
|
+
[ 1 0]
|
|
3736
|
+
[-1 1]
|
|
3737
|
+
[ 0 -1]
|
|
3738
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
3739
|
+
sage: result, certificate
|
|
3740
|
+
(True, GraphicNode (3×2))
|
|
3741
|
+
sage: certificate.forest_edges()
|
|
3742
|
+
((2, 1), (7, 1), (7, 12))
|
|
3743
|
+
sage: certificate.coforest_edges()
|
|
3744
|
+
((2, 7), (12, 1))
|
|
3745
|
+
|
|
3746
|
+
Starting with a morphism::
|
|
3747
|
+
|
|
3748
|
+
sage: from sage.matrix.seymour_decomposition import UnknownNode
|
|
3749
|
+
sage: phi = matrix(ZZ, [[1, 0], [1, 1], [0, 1]],
|
|
3750
|
+
....: row_keys=['a', 'b', 'c'], column_keys=['v', 'w'])
|
|
3751
|
+
sage: phi; phi._unicode_art_matrix()
|
|
3752
|
+
Generic morphism:
|
|
3753
|
+
From: Free module generated by {'v', 'w'} over Integer Ring
|
|
3754
|
+
To: Free module generated by {'a', 'b', 'c'} over Integer Ring
|
|
3755
|
+
v w
|
|
3756
|
+
a⎛1 0⎞
|
|
3757
|
+
b⎜1 1⎟
|
|
3758
|
+
c⎝0 1⎠
|
|
3759
|
+
sage: phi_node = UnknownNode(phi)
|
|
3760
|
+
sage: is_graphic, rephined_node = phi_node._is_binary_linear_matroid_graphic(decomposition=True)
|
|
3761
|
+
sage: is_graphic, rephined_node
|
|
3762
|
+
(True, GraphicNode (3×2))
|
|
3763
|
+
sage: rephined_node.forest_edges()
|
|
3764
|
+
{'a': (1, 2), 'b': (7, 1), 'c': (12, 7)}
|
|
3765
|
+
sage: phi_node # still in the dark about graphicness
|
|
3766
|
+
UnknownNode (3×2)
|
|
3767
|
+
"""
|
|
3768
|
+
if self._forest_edges is not None:
|
|
3769
|
+
return self._forest_edges
|
|
3770
|
+
cdef CMR_GRAPH *graph = CMRseymourGraph(self._dec)
|
|
3771
|
+
cdef size_t num_edges = CMRseymourGraphSizeForest(self._dec)
|
|
3772
|
+
cdef CMR_GRAPH_EDGE *edges = CMRseymourGraphForest(self._dec)
|
|
3773
|
+
cdef bool *arcs_reversed
|
|
3774
|
+
base_ring = self.base_ring()
|
|
3775
|
+
if base_ring.characteristic() == 2:
|
|
3776
|
+
self._forest_edges = _sage_edges(graph, edges, num_edges, self.row_keys())
|
|
3777
|
+
else:
|
|
3778
|
+
arcs_reversed = CMRseymourGraphArcsReversed(self._dec)
|
|
3779
|
+
self._forest_edges = _sage_arcs(graph, edges, arcs_reversed, num_edges, self.row_keys())
|
|
3780
|
+
return self._forest_edges
|
|
3781
|
+
|
|
3782
|
+
def coforest_edges(self):
|
|
3783
|
+
r"""
|
|
3784
|
+
Return the forest edges of ``self``.
|
|
3785
|
+
|
|
3786
|
+
If ``self.base_ring()`` is `\GF{2}`, then return edges.
|
|
3787
|
+
If ``self.base_ring()`` is `\GF{3}` or `\ZZ`, then arcs.
|
|
3788
|
+
|
|
3789
|
+
EXAMPLES::
|
|
3790
|
+
|
|
3791
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3792
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 6, 7, sparse=True),
|
|
3793
|
+
....: [[-1, 0, 0, 0, 1, -1, 0],
|
|
3794
|
+
....: [ 1, 0, 0, 1, -1, 1, 0],
|
|
3795
|
+
....: [ 0, -1, 0, -1, 1, -1, 0],
|
|
3796
|
+
....: [ 0, 1, 0, 0, 0, 0, 1],
|
|
3797
|
+
....: [ 0, 0, 1, -1, 1, 0, 1],
|
|
3798
|
+
....: [ 0, 0, -1, 1, -1, 0, 0]])
|
|
3799
|
+
sage: M.is_network_matrix()
|
|
3800
|
+
True
|
|
3801
|
+
sage: result, certificate = M.is_network_matrix(certificate=True)
|
|
3802
|
+
sage: result, certificate
|
|
3803
|
+
(True,
|
|
3804
|
+
(Digraph on 7 vertices,
|
|
3805
|
+
((9, 8), (3, 8), (3, 4), (5, 4), (4, 6), (0, 6)),
|
|
3806
|
+
((3, 9), (5, 3), (4, 0), (0, 8), (9, 0), (4, 9), (5, 6))))
|
|
3807
|
+
sage: digraph, forest_arcs, coforest_arcs = certificate
|
|
3808
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
3809
|
+
sage: certificate.graph() == digraph
|
|
3810
|
+
True
|
|
3811
|
+
sage: certificate.forest_edges() == forest_arcs
|
|
3812
|
+
True
|
|
3813
|
+
sage: certificate.coforest_edges() == coforest_arcs
|
|
3814
|
+
True
|
|
3815
|
+
"""
|
|
3816
|
+
if self._coforest_edges is not None:
|
|
3817
|
+
return self._coforest_edges
|
|
3818
|
+
cdef CMR_GRAPH *graph = CMRseymourGraph(self._dec)
|
|
3819
|
+
cdef size_t num_edges = CMRseymourGraphSizeCoforest(self._dec)
|
|
3820
|
+
cdef CMR_GRAPH_EDGE *edges = CMRseymourGraphCoforest(self._dec)
|
|
3821
|
+
cdef bool *arcs_reversed
|
|
3822
|
+
base_ring = self.base_ring()
|
|
3823
|
+
if base_ring.characteristic() == 2:
|
|
3824
|
+
self._coforest_edges = _sage_edges(graph, edges, num_edges, self.column_keys())
|
|
3825
|
+
else:
|
|
3826
|
+
arcs_reversed = CMRseymourGraphArcsReversed(self._dec)
|
|
3827
|
+
self._coforest_edges = _sage_arcs(graph, edges, arcs_reversed, num_edges, self.column_keys())
|
|
3828
|
+
return self._coforest_edges
|
|
3829
|
+
|
|
3830
|
+
|
|
3831
|
+
cdef class GraphicNode(BaseGraphicNode):
|
|
3832
|
+
|
|
3833
|
+
pass
|
|
3834
|
+
|
|
3835
|
+
|
|
3836
|
+
cdef class CographicNode(BaseGraphicNode):
|
|
3837
|
+
@cached_method
|
|
3838
|
+
def graph(self):
|
|
3839
|
+
r"""
|
|
3840
|
+
Actually the cograph of matrix, in the case where it is not graphic.
|
|
3841
|
+
|
|
3842
|
+
EXAMPLES:
|
|
3843
|
+
|
|
3844
|
+
Undirected graph::
|
|
3845
|
+
|
|
3846
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3847
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 4, 5, sparse=True),
|
|
3848
|
+
....: [[1, 1, 1, 0, 0],
|
|
3849
|
+
....: [0, 1, 1, 1, 0],
|
|
3850
|
+
....: [0, 0, 1, 1, 1],
|
|
3851
|
+
....: [1, 0, 0, 1, 1]]); M
|
|
3852
|
+
[1 1 1 0 0]
|
|
3853
|
+
[0 1 1 1 0]
|
|
3854
|
+
[0 0 1 1 1]
|
|
3855
|
+
[1 0 0 1 1]
|
|
3856
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True)
|
|
3857
|
+
sage: certificate
|
|
3858
|
+
CographicNode (4×5)
|
|
3859
|
+
sage: certificate.base_ring()
|
|
3860
|
+
Finite Field of size 2
|
|
3861
|
+
sage: G = certificate.graph(); G
|
|
3862
|
+
Graph on 6 vertices
|
|
3863
|
+
sage: G.vertices(sort=True)
|
|
3864
|
+
[0, 1, 2, 5, 7, 8]
|
|
3865
|
+
sage: G.edges(sort=True)
|
|
3866
|
+
[(0, 2, None), (0, 5, None), (0, 7, None), (1, 2, None), (1, 5, None),
|
|
3867
|
+
(1, 7, None), (2, 8, None), (5, 8, None), (7, 8, None)]
|
|
3868
|
+
|
|
3869
|
+
Directed graph::
|
|
3870
|
+
|
|
3871
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3872
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 4, 5, sparse=True),
|
|
3873
|
+
....: [[1, -1, 1, 0, 0],
|
|
3874
|
+
....: [0, 1, -1, 1, 0],
|
|
3875
|
+
....: [0, 0, 1, -1, 1],
|
|
3876
|
+
....: [1, 0, 0, 1, -1]]); M
|
|
3877
|
+
[ 1 -1 1 0 0]
|
|
3878
|
+
[ 0 1 -1 1 0]
|
|
3879
|
+
[ 0 0 1 -1 1]
|
|
3880
|
+
[ 1 0 0 1 -1]
|
|
3881
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
3882
|
+
sage: certificate
|
|
3883
|
+
CographicNode (4×5)
|
|
3884
|
+
sage: G = certificate.graph(); G
|
|
3885
|
+
Digraph on 6 vertices
|
|
3886
|
+
sage: G.vertices(sort=True)
|
|
3887
|
+
[0, 1, 2, 5, 7, 8]
|
|
3888
|
+
sage: G.edges(sort=True)
|
|
3889
|
+
[(0, 2, None), (0, 5, None), (0, 7, None), (1, 2, None), (1, 5, None),
|
|
3890
|
+
(1, 7, None), (2, 8, None), (5, 8, None), (7, 8, None)]
|
|
3891
|
+
"""
|
|
3892
|
+
if self._graph is not None:
|
|
3893
|
+
return self._graph
|
|
3894
|
+
base_ring = self.base_ring()
|
|
3895
|
+
if base_ring.characteristic() == 2:
|
|
3896
|
+
self._graph = _sage_graph(CMRseymourCograph(self._dec))
|
|
3897
|
+
else:
|
|
3898
|
+
self._graph = _sage_digraph(CMRseymourCograph(self._dec),
|
|
3899
|
+
CMRseymourCographArcsReversed(self._dec))
|
|
3900
|
+
return self._graph
|
|
3901
|
+
|
|
3902
|
+
@cached_method
|
|
3903
|
+
def forest_edges(self):
|
|
3904
|
+
r"""
|
|
3905
|
+
Return the forest edges of the cograph of ``self``.
|
|
3906
|
+
|
|
3907
|
+
EXAMPLES:
|
|
3908
|
+
|
|
3909
|
+
Undirected graph::
|
|
3910
|
+
|
|
3911
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3912
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 4, 5, sparse=True),
|
|
3913
|
+
....: [[1, 1, 1, 0, 0],
|
|
3914
|
+
....: [0, 1, 1, 1, 0],
|
|
3915
|
+
....: [0, 0, 1, 1, 1],
|
|
3916
|
+
....: [1, 0, 0, 1, 1]]); M
|
|
3917
|
+
[1 1 1 0 0]
|
|
3918
|
+
[0 1 1 1 0]
|
|
3919
|
+
[0 0 1 1 1]
|
|
3920
|
+
[1 0 0 1 1]
|
|
3921
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True)
|
|
3922
|
+
sage: result, certificate
|
|
3923
|
+
(True, CographicNode (4×5))
|
|
3924
|
+
sage: certificate.forest_edges()
|
|
3925
|
+
((7, 8), (5, 0), (0, 7), (1, 7), (2, 1))
|
|
3926
|
+
sage: certificate.coforest_edges()
|
|
3927
|
+
((5, 8), (5, 1), (0, 2), (2, 8))
|
|
3928
|
+
|
|
3929
|
+
Directed graph::
|
|
3930
|
+
|
|
3931
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3932
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 4, 5, sparse=True),
|
|
3933
|
+
....: [[1, -1, 1, 0, 0],
|
|
3934
|
+
....: [0, 1, -1, 1, 0],
|
|
3935
|
+
....: [0, 0, 1, -1, 1],
|
|
3936
|
+
....: [1, 0, 0, 1, -1]]); M
|
|
3937
|
+
[ 1 -1 1 0 0]
|
|
3938
|
+
[ 0 1 -1 1 0]
|
|
3939
|
+
[ 0 0 1 -1 1]
|
|
3940
|
+
[ 1 0 0 1 -1]
|
|
3941
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
3942
|
+
sage: result, certificate
|
|
3943
|
+
(True, CographicNode (4×5))
|
|
3944
|
+
sage: certificate.forest_edges()
|
|
3945
|
+
((7, 8), (0, 5), (0, 7), (1, 7), (1, 2))
|
|
3946
|
+
sage: certificate.coforest_edges()
|
|
3947
|
+
((5, 8), (1, 5), (0, 2), (2, 8))
|
|
3948
|
+
"""
|
|
3949
|
+
if self._forest_edges is not None:
|
|
3950
|
+
return self._forest_edges
|
|
3951
|
+
cdef CMR_GRAPH *graph = CMRseymourCograph(self._dec)
|
|
3952
|
+
cdef size_t num_edges = CMRseymourCographSizeForest(self._dec)
|
|
3953
|
+
cdef CMR_GRAPH_EDGE *edges = CMRseymourCographForest(self._dec)
|
|
3954
|
+
cdef bool *arcs_reversed
|
|
3955
|
+
base_ring = self.base_ring()
|
|
3956
|
+
if base_ring.characteristic() == 2:
|
|
3957
|
+
self._forest_edges = _sage_edges(graph, edges, num_edges, self.row_keys())
|
|
3958
|
+
else:
|
|
3959
|
+
arcs_reversed = CMRseymourCographArcsReversed(self._dec)
|
|
3960
|
+
self._forest_edges = _sage_arcs(graph, edges, arcs_reversed, num_edges, self.row_keys())
|
|
3961
|
+
return self._forest_edges
|
|
3962
|
+
|
|
3963
|
+
@cached_method
|
|
3964
|
+
def coforest_edges(self):
|
|
3965
|
+
r"""
|
|
3966
|
+
Return the forest edges of the cograph of ``self``.
|
|
3967
|
+
"""
|
|
3968
|
+
if self._coforest_edges is not None:
|
|
3969
|
+
return self._coforest_edges
|
|
3970
|
+
cdef CMR_GRAPH *graph = CMRseymourCograph(self._dec)
|
|
3971
|
+
cdef size_t num_edges = CMRseymourCographSizeCoforest(self._dec)
|
|
3972
|
+
cdef CMR_GRAPH_EDGE *edges = CMRseymourCographCoforest(self._dec)
|
|
3973
|
+
cdef bool *arcs_reversed
|
|
3974
|
+
base_ring = self.base_ring()
|
|
3975
|
+
if base_ring.characteristic() == 2:
|
|
3976
|
+
self._coforest_edges = _sage_edges(graph, edges, num_edges, self.column_keys())
|
|
3977
|
+
else:
|
|
3978
|
+
arcs_reversed = CMRseymourCographArcsReversed(self._dec)
|
|
3979
|
+
self._coforest_edges = _sage_arcs(graph, edges, arcs_reversed, num_edges, self.column_keys())
|
|
3980
|
+
return self._coforest_edges
|
|
3981
|
+
|
|
3982
|
+
|
|
3983
|
+
cdef class PlanarNode(BaseGraphicNode):
|
|
3984
|
+
@cached_method
|
|
3985
|
+
def cograph(self):
|
|
3986
|
+
r"""
|
|
3987
|
+
Return the cograph of matrix.
|
|
3988
|
+
|
|
3989
|
+
EXAMPLES:
|
|
3990
|
+
|
|
3991
|
+
Undirected graph::
|
|
3992
|
+
|
|
3993
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
3994
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
3995
|
+
....: [[1, 0], [1, 1], [0,1]]); M
|
|
3996
|
+
[1 0]
|
|
3997
|
+
[1 1]
|
|
3998
|
+
[0 1]
|
|
3999
|
+
sage: result, certificate = M._is_binary_linear_matroid_regular(certificate=True,
|
|
4000
|
+
....: check_graphic_minors_planar=True)
|
|
4001
|
+
sage: result, certificate
|
|
4002
|
+
(True, PlanarNode (3×2))
|
|
4003
|
+
sage: G = certificate.cograph(); G
|
|
4004
|
+
Graph on 3 vertices
|
|
4005
|
+
sage: G.vertices(sort=True)
|
|
4006
|
+
[1, 2, 7]
|
|
4007
|
+
sage: G.edges(sort=True)
|
|
4008
|
+
[(1, 2, None), (1, 7, None), (2, 7, None)]
|
|
4009
|
+
sage: certificate.cograph_forest_edges()
|
|
4010
|
+
((1, 2), (7, 1))
|
|
4011
|
+
sage: certificate.cograph_coforest_edges()
|
|
4012
|
+
((1, 2), (2, 7), (7, 1))
|
|
4013
|
+
|
|
4014
|
+
Directed graph::
|
|
4015
|
+
|
|
4016
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4017
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 3, 2, sparse=True),
|
|
4018
|
+
....: [[1, 0], [-1, 1], [0,-1]]); M
|
|
4019
|
+
[ 1 0]
|
|
4020
|
+
[-1 1]
|
|
4021
|
+
[ 0 -1]
|
|
4022
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True,
|
|
4023
|
+
....: check_graphic_minors_planar=True)
|
|
4024
|
+
sage: result, certificate
|
|
4025
|
+
(True, PlanarNode (3×2))
|
|
4026
|
+
sage: G = certificate.cograph(); G
|
|
4027
|
+
Digraph on 3 vertices
|
|
4028
|
+
sage: G.vertices(sort=True)
|
|
4029
|
+
[1, 2, 7]
|
|
4030
|
+
sage: G.edges(sort=True)
|
|
4031
|
+
[(1, 2, None), (1, 7, None), (2, 7, None), (7, 1, None)]
|
|
4032
|
+
sage: certificate.cograph_forest_edges()
|
|
4033
|
+
((1, 2), (1, 7))
|
|
4034
|
+
sage: certificate.cograph_coforest_edges()
|
|
4035
|
+
((1, 2), (2, 7), (7, 1))
|
|
4036
|
+
"""
|
|
4037
|
+
if self._cograph is not None:
|
|
4038
|
+
return self._cograph
|
|
4039
|
+
base_ring = self.base_ring()
|
|
4040
|
+
if base_ring.characteristic() == 2:
|
|
4041
|
+
self._cograph = _sage_graph(CMRseymourCograph(self._dec))
|
|
4042
|
+
else:
|
|
4043
|
+
self._cograph = _sage_digraph(CMRseymourCograph(self._dec),
|
|
4044
|
+
CMRseymourCographArcsReversed(self._dec))
|
|
4045
|
+
return self._cograph
|
|
4046
|
+
|
|
4047
|
+
@cached_method
|
|
4048
|
+
def cograph_forest_edges(self):
|
|
4049
|
+
r"""
|
|
4050
|
+
Return the forest edges of the cograph of ``self``.
|
|
4051
|
+
"""
|
|
4052
|
+
if self._cograph_forest_edges is not None:
|
|
4053
|
+
return self._cograph_forest_edges
|
|
4054
|
+
cdef CMR_GRAPH *cograph = CMRseymourCograph(self._dec)
|
|
4055
|
+
cdef size_t num_edges = CMRseymourCographSizeForest(self._dec)
|
|
4056
|
+
cdef CMR_GRAPH_EDGE *edges = CMRseymourCographForest(self._dec)
|
|
4057
|
+
cdef bool *arcs_reversed
|
|
4058
|
+
base_ring = self.base_ring()
|
|
4059
|
+
if base_ring.characteristic() == 2:
|
|
4060
|
+
self._cograph_forest_edges = _sage_edges(cograph, edges, num_edges, self.row_keys())
|
|
4061
|
+
else:
|
|
4062
|
+
arcs_reversed = CMRseymourCographArcsReversed(self._dec)
|
|
4063
|
+
self._cograph_forest_edges = _sage_arcs(cograph, edges, arcs_reversed, num_edges, self.row_keys())
|
|
4064
|
+
return self._cograph_forest_edges
|
|
4065
|
+
|
|
4066
|
+
@cached_method
|
|
4067
|
+
def cograph_coforest_edges(self):
|
|
4068
|
+
r"""
|
|
4069
|
+
Return the forest edges of the cograph of ``self``.
|
|
4070
|
+
"""
|
|
4071
|
+
if self._cograph_coforest_edges is not None:
|
|
4072
|
+
return self._cograph_coforest_edges
|
|
4073
|
+
cdef CMR_GRAPH *cograph = CMRseymourCograph(self._dec)
|
|
4074
|
+
cdef size_t num_edges = CMRseymourCographSizeCoforest(self._dec)
|
|
4075
|
+
cdef CMR_GRAPH_EDGE *edges = CMRseymourCographCoforest(self._dec)
|
|
4076
|
+
cdef bool *arcs_reversed
|
|
4077
|
+
base_ring = self.base_ring()
|
|
4078
|
+
if base_ring.characteristic() == 2:
|
|
4079
|
+
self._cograph_coforest_edges = _sage_edges(cograph, edges, num_edges, self.column_keys())
|
|
4080
|
+
else:
|
|
4081
|
+
arcs_reversed = CMRseymourCographArcsReversed(self._dec)
|
|
4082
|
+
self._cograph_coforest_edges = _sage_arcs(cograph, edges, arcs_reversed, num_edges, self.column_keys())
|
|
4083
|
+
return self._cograph_coforest_edges
|
|
4084
|
+
|
|
4085
|
+
|
|
4086
|
+
cdef class SeriesParallelReductionNode(DecompositionNode):
|
|
4087
|
+
|
|
4088
|
+
def core(self):
|
|
4089
|
+
r"""
|
|
4090
|
+
Return the core of ``self``.
|
|
4091
|
+
|
|
4092
|
+
A :class:`SeriesParallelReductionNode` indicates that `M`
|
|
4093
|
+
arises from a smaller matrix `M'` (called the *core*)
|
|
4094
|
+
by successively adding zero rows/columns,
|
|
4095
|
+
unit rows/columns or duplicates of existing rows/columns
|
|
4096
|
+
(potentially scaled with `-1`).
|
|
4097
|
+
|
|
4098
|
+
Note that such series-parallel reductions preserve total unimodularity
|
|
4099
|
+
and binary regularity.
|
|
4100
|
+
|
|
4101
|
+
EXAMPLES::
|
|
4102
|
+
|
|
4103
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4104
|
+
sage: M = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 6, sparse=True),
|
|
4105
|
+
....: [[1, 1, 1, 1, 1, 0], [1, 1, 1, 0, 0, 0],
|
|
4106
|
+
....: [1, 0, 1, 1, 0, 1], [1, 0, 0, 1, 1, 0],
|
|
4107
|
+
....: [1, 1, 0, 0, 1, 0]]); M
|
|
4108
|
+
[1 1 1 1 1 0]
|
|
4109
|
+
[1 1 1 0 0 0]
|
|
4110
|
+
[1 0 1 1 0 1]
|
|
4111
|
+
[1 0 0 1 1 0]
|
|
4112
|
+
[1 1 0 0 1 0]
|
|
4113
|
+
sage: result, certificate = M.is_totally_unimodular(certificate=True)
|
|
4114
|
+
sage: result, certificate
|
|
4115
|
+
(True, SeriesParallelReductionNode (5×6))
|
|
4116
|
+
sage: certificate.core()
|
|
4117
|
+
[1 1 1 1 1]
|
|
4118
|
+
[1 1 1 0 0]
|
|
4119
|
+
[1 0 1 1 0]
|
|
4120
|
+
[1 0 0 1 1]
|
|
4121
|
+
[1 1 0 0 1]
|
|
4122
|
+
"""
|
|
4123
|
+
return self.child_nodes()[0].matrix()
|
|
4124
|
+
|
|
4125
|
+
|
|
4126
|
+
cdef class R10Node(DecompositionNode):
|
|
4127
|
+
r"""
|
|
4128
|
+
Special R10 Node.
|
|
4129
|
+
Only two possible 5 by 5 matrices up to row and column permutations and negations.
|
|
4130
|
+
|
|
4131
|
+
EXAMPLES::
|
|
4132
|
+
|
|
4133
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4134
|
+
sage: R10 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 5, sparse=True),
|
|
4135
|
+
....: [[1, 0, 0, 1, 1],
|
|
4136
|
+
....: [1, 1, 0, 0, 1],
|
|
4137
|
+
....: [0, 1, 1, 0, 1],
|
|
4138
|
+
....: [0, 0, 1, 1, 1],
|
|
4139
|
+
....: [1, 1, 1, 1, 1]]); R10
|
|
4140
|
+
[1 0 0 1 1]
|
|
4141
|
+
[1 1 0 0 1]
|
|
4142
|
+
[0 1 1 0 1]
|
|
4143
|
+
[0 0 1 1 1]
|
|
4144
|
+
[1 1 1 1 1]
|
|
4145
|
+
sage: result, certificate = R10.is_totally_unimodular(certificate=True)
|
|
4146
|
+
sage: result
|
|
4147
|
+
True
|
|
4148
|
+
sage: R10.is_network_matrix()
|
|
4149
|
+
False
|
|
4150
|
+
sage: R10.is_conetwork_matrix()
|
|
4151
|
+
False
|
|
4152
|
+
sage: result, certificate = R10._is_binary_linear_matroid_regular(certificate=True)
|
|
4153
|
+
sage: result
|
|
4154
|
+
True
|
|
4155
|
+
sage: certificate._is_binary_linear_matroid_graphic()
|
|
4156
|
+
False
|
|
4157
|
+
sage: certificate._is_binary_linear_matroid_cographic()
|
|
4158
|
+
False
|
|
4159
|
+
|
|
4160
|
+
sage: R10 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 5, sparse=True),
|
|
4161
|
+
....: [[ 1,-1, 0, 0,-1],
|
|
4162
|
+
....: [-1, 1,-1, 0, 0],
|
|
4163
|
+
....: [ 0,-1, 1,-1, 0],
|
|
4164
|
+
....: [ 0, 0,-1, 1,-1],
|
|
4165
|
+
....: [-1, 0, 0,-1, 1]]); R10
|
|
4166
|
+
[ 1 -1 0 0 -1]
|
|
4167
|
+
[-1 1 -1 0 0]
|
|
4168
|
+
[ 0 -1 1 -1 0]
|
|
4169
|
+
[ 0 0 -1 1 -1]
|
|
4170
|
+
[-1 0 0 -1 1]
|
|
4171
|
+
sage: result, certificate = R10.is_totally_unimodular(certificate=True)
|
|
4172
|
+
sage: result
|
|
4173
|
+
True
|
|
4174
|
+
sage: R10.is_network_matrix()
|
|
4175
|
+
False
|
|
4176
|
+
sage: R10.is_conetwork_matrix()
|
|
4177
|
+
False
|
|
4178
|
+
|
|
4179
|
+
sage: R10 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 5, sparse=True),
|
|
4180
|
+
....: [[1, 1, 0, 0, 1],
|
|
4181
|
+
....: [1, 1,-1, 0, 0],
|
|
4182
|
+
....: [0, 1,-1,-1, 0],
|
|
4183
|
+
....: [0, 0, 1, 1, 1],
|
|
4184
|
+
....: [1, 0, 0, 1, 1]]); R10
|
|
4185
|
+
[ 1 1 0 0 1]
|
|
4186
|
+
[ 1 1 -1 0 0]
|
|
4187
|
+
[ 0 1 -1 -1 0]
|
|
4188
|
+
[ 0 0 1 1 1]
|
|
4189
|
+
[ 1 0 0 1 1]
|
|
4190
|
+
sage: result, certificate = R10.is_totally_unimodular(certificate=True)
|
|
4191
|
+
sage: result
|
|
4192
|
+
True
|
|
4193
|
+
sage: R10.is_network_matrix()
|
|
4194
|
+
False
|
|
4195
|
+
sage: R10.is_conetwork_matrix()
|
|
4196
|
+
False
|
|
4197
|
+
|
|
4198
|
+
sage: R10 = Matrix_cmr_chr_sparse(MatrixSpace(GF(2), 5, 5, sparse=True),
|
|
4199
|
+
....: [[1, 1, 0, 0, 1],
|
|
4200
|
+
....: [1, 1,-1, 0, 0],
|
|
4201
|
+
....: [0, 1,-1,-1, 0],
|
|
4202
|
+
....: [0, 0, 1, 1, 1],
|
|
4203
|
+
....: [1, 0, 0, 1, 1]]); R10
|
|
4204
|
+
[1 1 0 0 1]
|
|
4205
|
+
[1 1 1 0 0]
|
|
4206
|
+
[0 1 1 1 0]
|
|
4207
|
+
[0 0 1 1 1]
|
|
4208
|
+
[1 0 0 1 1]
|
|
4209
|
+
sage: result, certificate = R10._is_binary_linear_matroid_regular(certificate=True)
|
|
4210
|
+
sage: result
|
|
4211
|
+
True
|
|
4212
|
+
sage: certificate
|
|
4213
|
+
R10Node (5×5) a reduced matrix representation of R10 matroid
|
|
4214
|
+
sage: certificate._is_binary_linear_matroid_graphic()
|
|
4215
|
+
False
|
|
4216
|
+
sage: certificate._is_binary_linear_matroid_cographic()
|
|
4217
|
+
False
|
|
4218
|
+
"""
|
|
4219
|
+
|
|
4220
|
+
@cached_method
|
|
4221
|
+
def _matroid(self):
|
|
4222
|
+
r"""
|
|
4223
|
+
Return the R10 matroid represented by ``self``.
|
|
4224
|
+
``self.matrix()`` is the reduced matrix representation of the matroid.
|
|
4225
|
+
|
|
4226
|
+
EXAMPLES::
|
|
4227
|
+
|
|
4228
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4229
|
+
sage: R10 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 5, sparse=True),
|
|
4230
|
+
....: [[1, 1, 1, 1, 1],
|
|
4231
|
+
....: [1, 1, 1, 0, 0],
|
|
4232
|
+
....: [1, 0, 1, 1, 0],
|
|
4233
|
+
....: [1, 0, 0, 1, 1],
|
|
4234
|
+
....: [1, 1, 0, 0, 1]]); R10
|
|
4235
|
+
[1 1 1 1 1]
|
|
4236
|
+
[1 1 1 0 0]
|
|
4237
|
+
[1 0 1 1 0]
|
|
4238
|
+
[1 0 0 1 1]
|
|
4239
|
+
[1 1 0 0 1]
|
|
4240
|
+
sage: result, certificate = R10.is_totally_unimodular(certificate=True,
|
|
4241
|
+
....: row_keys=range(5),
|
|
4242
|
+
....: column_keys='abcde')
|
|
4243
|
+
sage: certificate._matroid()
|
|
4244
|
+
R10: Regular matroid of rank 5 on 10 elements with 162 bases
|
|
4245
|
+
sage: certificate._isomorphism()
|
|
4246
|
+
{'a': 0,
|
|
4247
|
+
'b': 1,
|
|
4248
|
+
'c': 2,
|
|
4249
|
+
'd': 'a',
|
|
4250
|
+
'e': 'b',
|
|
4251
|
+
'f': 4,
|
|
4252
|
+
'g': 'c',
|
|
4253
|
+
'h': 'e',
|
|
4254
|
+
'i': 3,
|
|
4255
|
+
'j': 'd'}
|
|
4256
|
+
sage: result, certificate = R10.is_totally_unimodular(certificate=True)
|
|
4257
|
+
sage: certificate._matroid()
|
|
4258
|
+
R10: Regular matroid of rank 5 on 10 elements with 162 bases
|
|
4259
|
+
sage: certificate._isomorphism()
|
|
4260
|
+
{'a': 0,
|
|
4261
|
+
'b': 1,
|
|
4262
|
+
'c': 2,
|
|
4263
|
+
'd': 5,
|
|
4264
|
+
'e': 6,
|
|
4265
|
+
'f': 4,
|
|
4266
|
+
'g': 7,
|
|
4267
|
+
'h': 9,
|
|
4268
|
+
'i': 3,
|
|
4269
|
+
'j': 8}
|
|
4270
|
+
|
|
4271
|
+
sage: R10 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 5, 5, sparse=True),
|
|
4272
|
+
....: [[ 1,-1, 0, 0,-1],
|
|
4273
|
+
....: [-1, 1,-1, 0, 0],
|
|
4274
|
+
....: [ 0,-1, 1,-1, 0],
|
|
4275
|
+
....: [ 0, 0,-1, 1,-1],
|
|
4276
|
+
....: [-1, 0, 0,-1, 1]]); R10
|
|
4277
|
+
[ 1 -1 0 0 -1]
|
|
4278
|
+
[-1 1 -1 0 0]
|
|
4279
|
+
[ 0 -1 1 -1 0]
|
|
4280
|
+
[ 0 0 -1 1 -1]
|
|
4281
|
+
[-1 0 0 -1 1]
|
|
4282
|
+
sage: result, certificate = R10.is_totally_unimodular(certificate=True,
|
|
4283
|
+
....: row_keys=range(5),
|
|
4284
|
+
....: column_keys='abcde')
|
|
4285
|
+
sage: certificate._matroid()
|
|
4286
|
+
R10: Regular matroid of rank 5 on 10 elements with 162 bases
|
|
4287
|
+
"""
|
|
4288
|
+
from sage.matroids.constructor import Matroid
|
|
4289
|
+
if self.row_keys() is None or self.column_keys() is None:
|
|
4290
|
+
M = Matroid(reduced_matrix=self.matrix(), regular=True)
|
|
4291
|
+
else:
|
|
4292
|
+
M = Matroid(reduced_morphism=self.morphism(), regular=True)
|
|
4293
|
+
M.rename("R10: " + repr(M))
|
|
4294
|
+
return M
|
|
4295
|
+
|
|
4296
|
+
def _isomorphism(self):
|
|
4297
|
+
r"""
|
|
4298
|
+
Return one isomorphism between the R10 matroid
|
|
4299
|
+
and ``self._matroid()``.
|
|
4300
|
+
"""
|
|
4301
|
+
import sage.matroids.matroids_catalog as matroids
|
|
4302
|
+
M0 = matroids.catalog.R10()
|
|
4303
|
+
result, isomorphism = M0.is_isomorphic(self._matroid(), certificate=True)
|
|
4304
|
+
if result is False:
|
|
4305
|
+
raise ValueError("This is not a R10 node")
|
|
4306
|
+
else:
|
|
4307
|
+
return isomorphism
|
|
4308
|
+
|
|
4309
|
+
def _repr_(self):
|
|
4310
|
+
r"""
|
|
4311
|
+
Return a string representation of ``self``.
|
|
4312
|
+
"""
|
|
4313
|
+
result = super()._repr_()
|
|
4314
|
+
result += f' a reduced matrix representation of R10 matroid'
|
|
4315
|
+
return result
|
|
4316
|
+
|
|
4317
|
+
|
|
4318
|
+
cdef class PivotsNode(DecompositionNode):
|
|
4319
|
+
|
|
4320
|
+
def npivots(self):
|
|
4321
|
+
r"""
|
|
4322
|
+
Return the number of pivots in ``self``.
|
|
4323
|
+
|
|
4324
|
+
EXAMPLES::
|
|
4325
|
+
|
|
4326
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4327
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
4328
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
4329
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
4330
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
4331
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
4332
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
4333
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
4334
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
4335
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
4336
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
4337
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
4338
|
+
....: decompose_strategy="delta_pivot")
|
|
4339
|
+
sage: certificate
|
|
4340
|
+
PivotsNode (9×12)
|
|
4341
|
+
sage: certificate.npivots()
|
|
4342
|
+
1
|
|
4343
|
+
"""
|
|
4344
|
+
return CMRseymourNumPivots(self._dec)
|
|
4345
|
+
|
|
4346
|
+
@cached_method
|
|
4347
|
+
def pivot_rows_and_columns(self):
|
|
4348
|
+
r"""
|
|
4349
|
+
Return a tuple of the row and column indices of all pivot entries.
|
|
4350
|
+
|
|
4351
|
+
EXAMPLES::
|
|
4352
|
+
|
|
4353
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4354
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
4355
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
4356
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
4357
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
4358
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
4359
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
4360
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
4361
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
4362
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
4363
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
4364
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
4365
|
+
....: decompose_strategy="delta_pivot")
|
|
4366
|
+
sage: certificate
|
|
4367
|
+
PivotsNode (9×12)
|
|
4368
|
+
sage: certificate.pivot_rows_and_columns()
|
|
4369
|
+
((0, 8),)
|
|
4370
|
+
"""
|
|
4371
|
+
cdef size_t *pivot_rows = CMRseymourPivotRows(self._dec)
|
|
4372
|
+
cdef size_t *pivot_columns = CMRseymourPivotColumns(self._dec)
|
|
4373
|
+
|
|
4374
|
+
return tuple((pivot_rows[i], pivot_columns[i]) for i in range(self.npivots()))
|
|
4375
|
+
|
|
4376
|
+
def _children(self):
|
|
4377
|
+
r"""
|
|
4378
|
+
Return a tuple of the tuples of children and their row and column keys.
|
|
4379
|
+
The underlying implementation of :meth:`child_nodes`
|
|
4380
|
+
and :meth:`child_keys`.
|
|
4381
|
+
|
|
4382
|
+
If row and column keys are not given, set the default keys.
|
|
4383
|
+
See alse :meth:set_default_keys
|
|
4384
|
+
|
|
4385
|
+
EXAMPLES::
|
|
4386
|
+
|
|
4387
|
+
sage: from sage.matrix.matrix_cmr_sparse import Matrix_cmr_chr_sparse
|
|
4388
|
+
sage: R12 = Matrix_cmr_chr_sparse(MatrixSpace(ZZ, 9, 12, sparse=True),
|
|
4389
|
+
....: [[ 1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
4390
|
+
....: [ 0, 0, 0, 1, -1, 0, 0, 0, 1, 1, 1, 1],
|
|
4391
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
|
4392
|
+
....: [ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
|
|
4393
|
+
....: [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
|
4394
|
+
....: [ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
4395
|
+
....: [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1],
|
|
4396
|
+
....: [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
|
4397
|
+
....: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]])
|
|
4398
|
+
sage: result, certificate = R12.is_totally_unimodular(certificate=True,
|
|
4399
|
+
....: decompose_strategy="delta_pivot")
|
|
4400
|
+
sage: certificate
|
|
4401
|
+
PivotsNode (9×12)
|
|
4402
|
+
sage: certificate.row_keys()
|
|
4403
|
+
sage: certificate.child_nodes()
|
|
4404
|
+
(DeltaSumNode (9×12),)
|
|
4405
|
+
sage: certificate.row_keys()
|
|
4406
|
+
(r0, r1, r2, r3, r4, r5, r6, r7, r8)
|
|
4407
|
+
"""
|
|
4408
|
+
if self._child_nodes is not None:
|
|
4409
|
+
return self._child_nodes
|
|
4410
|
+
self.set_default_keys()
|
|
4411
|
+
children_tuple = tuple(self._create_child_node(index)
|
|
4412
|
+
for index in range(self.nchildren()))
|
|
4413
|
+
self._child_nodes = children_tuple
|
|
4414
|
+
return self._child_nodes
|
|
4415
|
+
|
|
4416
|
+
|
|
4417
|
+
cdef class ElementKey:
|
|
4418
|
+
|
|
4419
|
+
cdef frozenset _key
|
|
4420
|
+
cdef bint _composition
|
|
4421
|
+
|
|
4422
|
+
def __init__(self, keys, composition=False):
|
|
4423
|
+
r"""
|
|
4424
|
+
Create the element key for a row or column index
|
|
4425
|
+
of :class:`DecompositionNode`.
|
|
4426
|
+
|
|
4427
|
+
INPUT:
|
|
4428
|
+
|
|
4429
|
+
- ``keys`` -- a row/column key or a tuple
|
|
4430
|
+
(`\pm 1`, row/column key, `\pm 1`, row/column key).
|
|
4431
|
+
|
|
4432
|
+
- ``composition`` -- ``True`` or ``False`` (default).
|
|
4433
|
+
whether the key is a composition key or not.
|
|
4434
|
+
If ``False``, ``self._key`` is a frozenset with a row/column key.
|
|
4435
|
+
If ``True``, ``self._key`` is a frozenset with two tuples,
|
|
4436
|
+
where each tuple has a sign value and a row/column key.
|
|
4437
|
+
For example, ``frozenset((1,'a'), (-1,'7'))``.
|
|
4438
|
+
"""
|
|
4439
|
+
if composition:
|
|
4440
|
+
sign1, key1, sign2, key2 = keys
|
|
4441
|
+
self._key = frozenset([(sign1, key1), (sign2, key2)])
|
|
4442
|
+
self._composition = True
|
|
4443
|
+
else:
|
|
4444
|
+
self._key = frozenset((keys,))
|
|
4445
|
+
self._composition = False
|
|
4446
|
+
|
|
4447
|
+
@property
|
|
4448
|
+
def key(self):
|
|
4449
|
+
return self._key
|
|
4450
|
+
|
|
4451
|
+
def __hash__(self):
|
|
4452
|
+
"""
|
|
4453
|
+
Return a hash of this element key
|
|
4454
|
+
"""
|
|
4455
|
+
return hash(self.key)
|
|
4456
|
+
|
|
4457
|
+
def __eq__(self, other):
|
|
4458
|
+
if isinstance(other, ElementKey):
|
|
4459
|
+
return self.key == other.key
|
|
4460
|
+
return False
|
|
4461
|
+
|
|
4462
|
+
def __repr__(self):
|
|
4463
|
+
"""
|
|
4464
|
+
Return a string representation of ``self``.
|
|
4465
|
+
The composition key is sorted by the string of keys.
|
|
4466
|
+
"""
|
|
4467
|
+
if self._composition:
|
|
4468
|
+
sorted_key = sorted(self.key, key=lambda x: (str(x[1]), x[0]))
|
|
4469
|
+
return "".join(['+'+str(a[1]) if a[0] == 1 else '-'+str(a[1]) for a in sorted_key])
|
|
4470
|
+
else:
|
|
4471
|
+
return "".join([str(a) for a in self.key])
|
|
4472
|
+
|
|
4473
|
+
|
|
4474
|
+
cdef _class(CMR_SEYMOUR_NODE *dec):
|
|
4475
|
+
r"""
|
|
4476
|
+
Return the class of the decomposition `CMR_SEYMOUR_NODE`.
|
|
4477
|
+
|
|
4478
|
+
INPUT:
|
|
4479
|
+
|
|
4480
|
+
- ``dec`` -- a ``CMR_SEYMOUR_NODE``
|
|
4481
|
+
"""
|
|
4482
|
+
cdef CMR_SEYMOUR_NODE_TYPE typ = CMRseymourType(dec)
|
|
4483
|
+
|
|
4484
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_ONESUM:
|
|
4485
|
+
return OneSumNode
|
|
4486
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_TWOSUM:
|
|
4487
|
+
return TwoSumNode
|
|
4488
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_DELTASUM:
|
|
4489
|
+
return DeltaSumNode
|
|
4490
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_THREESUM:
|
|
4491
|
+
return ThreeSumNode
|
|
4492
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_YSUM:
|
|
4493
|
+
return YSumNode
|
|
4494
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_GRAPH:
|
|
4495
|
+
return GraphicNode
|
|
4496
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_COGRAPH:
|
|
4497
|
+
return CographicNode
|
|
4498
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_PLANAR:
|
|
4499
|
+
return PlanarNode
|
|
4500
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_SERIES_PARALLEL:
|
|
4501
|
+
return SeriesParallelReductionNode
|
|
4502
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_PIVOTS:
|
|
4503
|
+
return PivotsNode
|
|
4504
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_IRREGULAR:
|
|
4505
|
+
return ThreeConnectedIrregularNode
|
|
4506
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_UNKNOWN:
|
|
4507
|
+
return UnknownNode
|
|
4508
|
+
if typ == CMR_SEYMOUR_NODE_TYPE_R10:
|
|
4509
|
+
return R10Node
|
|
4510
|
+
raise NotImplementedError
|
|
4511
|
+
|
|
4512
|
+
|
|
4513
|
+
cdef create_DecompositionNode(CMR_SEYMOUR_NODE *dec, matrix=None, row_keys=None, column_keys=None, base_ring=None):
|
|
4514
|
+
r"""
|
|
4515
|
+
Create an instance of a subclass of :class:`DecompositionNode`.
|
|
4516
|
+
|
|
4517
|
+
INPUT:
|
|
4518
|
+
|
|
4519
|
+
- ``dec`` -- a ``CMR_SEYMOUR_NODE``
|
|
4520
|
+
"""
|
|
4521
|
+
if dec == NULL:
|
|
4522
|
+
return None
|
|
4523
|
+
cdef DecompositionNode result = <DecompositionNode> _class(dec)(
|
|
4524
|
+
matrix, row_keys=row_keys, column_keys=column_keys, base_ring=base_ring)
|
|
4525
|
+
result._set_dec(dec)
|
|
4526
|
+
return result
|