Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matroids/lean_matrix.pyx
8817 views
1
"""
2
Lean matrices
3
4
Internal data structures for the ``LinearMatroid`` class and some subclasses.
5
Note that many of the methods are ``cdef``, and therefore only available from
6
Cython code.
7
8
.. warning::
9
10
Intended for internal use by the ``LinearMatroid`` classes only. End users
11
should work with Sage matrices instead. Methods that are used outside
12
lean_matrix.pyx and have no equivalent in Sage's ``Matrix`` have been
13
flagged in the code, as well as where they are used, by ``# Not a Sage
14
matrix operation`` or ``# Deprecated Sage matrix operation``.
15
16
AUTHORS:
17
18
- Rudi Pendavingh, Stefan van Zwam (2013-04-01): initial version
19
20
"""
21
#*****************************************************************************
22
# Copyright (C) 2013 Rudi Pendavingh <[email protected]>
23
# Copyright (C) 2013 Stefan van Zwam <[email protected]>
24
#
25
#
26
# Distributed under the terms of the GNU General Public License (GPL)
27
# as published by the Free Software Foundation; either version 2 of
28
# the License, or (at your option) any later version.
29
# http://www.gnu.org/licenses/
30
#*****************************************************************************
31
32
include 'sage/ext/stdsage.pxi'
33
include 'sage/misc/bitset.pxi'
34
from sage.matrix.matrix2 cimport Matrix
35
from sage.rings.all import ZZ, FiniteField, GF
36
from sage.rings.integer cimport Integer
37
import sage.matrix.constructor
38
39
cdef class LeanMatrix:
40
"""
41
Lean matrices
42
43
Sage's matrix classes are powerful, versatile, and often very fast. However, their performance with regard to pivoting
44
(pretty much the only task performed on them within the context of matroids) leaves something to be desired. The LeanMatrix
45
classes provide the LinearMatroid classes with a custom, light-weight data structure to store and manipulate matrices.
46
47
This is the abstract base class. Most methods are not implemented; this is only to fix the interface.
48
49
.. NOTE::
50
51
This class is intended for internal use by the LinearMatroid class only. Hence it does not derive from ``SageObject``.
52
If ``A`` is a LeanMatrix instance, and you need access from other parts of Sage, use ``Matrix(A)`` instead.
53
54
EXAMPLES::
55
56
sage: M = Matroid(ring=GF(5), matrix=[[1, 0, 1, 1, 1], [0, 1, 1, 2, 3]]) # indirect doctest
57
sage: M.is_isomorphic(matroids.Uniform(2, 5))
58
True
59
"""
60
def __init__(self, long m, long n, M=None):
61
"""
62
Initialize a lean matrix; see class documentation for more info.
63
64
EXAMPLES::
65
66
sage: from sage.matroids.lean_matrix import LeanMatrix
67
sage: A = LeanMatrix(3, 4)
68
sage: A.ncols()
69
4
70
"""
71
self._nrows = m
72
self._ncols = n
73
74
def __repr__(self):
75
"""
76
Return a string representation.
77
78
EXAMPLES::
79
80
sage: from sage.matroids.lean_matrix import LeanMatrix
81
sage: A = LeanMatrix(3, 4)
82
sage: repr(A)
83
'LeanMatrix instance with 3 rows and 4 columns'
84
"""
85
return "LeanMatrix instance with " + str(self._nrows) + " rows and " + str(self._ncols) + " columns"
86
87
def _matrix_(self):
88
"""
89
Return a matrix version.
90
91
EXAMPLES::
92
93
sage: from sage.matroids.lean_matrix import GenericMatrix
94
sage: A = Matrix(GF(5), [[1, 0, 1, 1, 1], [0, 1, 1, 2, 3]])
95
sage: A == GenericMatrix(2, 5, A)._matrix_()
96
True
97
"""
98
cdef long r, c
99
M = sage.matrix.constructor.Matrix(self.base_ring(), self._nrows, self._ncols)
100
for r from 0 <= r < self._nrows:
101
for c from 0 <= c < self._ncols:
102
M[r, c] = self.get_unsafe(r, c)
103
return M
104
105
cdef LeanMatrix copy(self): # Deprecated Sage matrix operation
106
"""
107
Make a copy of ``self``.
108
"""
109
cdef LeanMatrix A = type(self)(self.nrows(), self.ncols(), self)
110
return A
111
112
cdef void resize(self, long k): # Not a Sage matrix operation
113
"""
114
Change number of rows of ``self`` to ``k``. Preserves data.
115
"""
116
raise NotImplementedError
117
118
cdef LeanMatrix stack(self, LeanMatrix M):
119
"""
120
Stack ``self`` on top of ``M``. Assumes ``self`` and ``M`` are of same
121
type, and compatible dimensions.
122
"""
123
cdef LeanMatrix A
124
cdef long i, j
125
cdef long sr = self.nrows()
126
A = type(self)(sr + M.nrows(), self.ncols())
127
for i from 0 <= i < sr:
128
for j from 0 <= j < self.ncols():
129
A.set_unsafe(i, j, self.get_unsafe(i, j))
130
for i from 0 <= i < M.nrows():
131
for j from 0 <= j < M.ncols():
132
A.set_unsafe(i + sr, j, M.get_unsafe(i, j))
133
return A
134
135
cdef LeanMatrix augment(self, LeanMatrix M):
136
"""
137
Concatenates ``self`` with ``M``, placing ``M`` to the right of
138
``self``. Assumes ``self`` and ``M`` are of same type, and compatible
139
dimensions.
140
"""
141
cdef LeanMatrix A
142
cdef long i, j
143
cdef long sc = self.ncols()
144
A = type(self)(self.nrows(), sc + M.ncols())
145
for i from 0 <= i < self.nrows():
146
for j from 0 <= j < sc:
147
A.set_unsafe(i, j, self.get_unsafe(i, j))
148
for j from 0 <= j < M.ncols():
149
A.set_unsafe(i, j + sc, M.get_unsafe(i, j))
150
return A
151
152
cdef LeanMatrix prepend_identity(self): # Not a Sage matrix operation
153
"""
154
Return the matrix obtained by prepending an identity matrix. Special
155
case of ``augment``.
156
"""
157
cdef long i, j
158
cdef LeanMatrix A = type(self)(self.nrows(), self.ncols() + self.nrows())
159
for i from 0 <= i < self.nrows():
160
A.set_unsafe(i, i, self.base_ring()(1))
161
for j from 0 <= j < self.ncols():
162
A.set_unsafe(i, self.nrows() + j, self.get_unsafe(i, j))
163
return A
164
165
cpdef long ncols(self):
166
"""
167
Return the number of columns.
168
169
EXAMPLES::
170
171
sage: from sage.matroids.lean_matrix import LeanMatrix
172
sage: A = LeanMatrix(3, 4)
173
sage: A.ncols()
174
4
175
"""
176
return self._ncols
177
178
cpdef long nrows(self):
179
"""
180
Return the number of rows.
181
182
EXAMPLES::
183
184
sage: from sage.matroids.lean_matrix import LeanMatrix
185
sage: A = LeanMatrix(3, 4)
186
sage: A.nrows()
187
3
188
"""
189
return self._nrows
190
191
cpdef base_ring(self):
192
"""
193
Return the base ring.
194
195
EXAMPLES::
196
197
sage: from sage.matroids.lean_matrix import LeanMatrix
198
sage: A = LeanMatrix(3, 4)
199
sage: A.base_ring()
200
Traceback (most recent call last):
201
...
202
NotImplementedError: subclasses need to implement this.
203
"""
204
raise NotImplementedError("subclasses need to implement this.")
205
206
cpdef characteristic(self):
207
"""
208
Return the characteristic of ``self.base_ring()``.
209
210
EXAMPLES::
211
212
sage: from sage.matroids.lean_matrix import GenericMatrix
213
sage: A = GenericMatrix(3, 4, ring=GF(5))
214
sage: A.characteristic()
215
5
216
"""
217
return self.base_ring().characteristic()
218
219
cdef get_unsafe(self, long r, long c):
220
"""
221
Return the value in row ``r``, column ``c``.
222
"""
223
raise NotImplementedError
224
225
cdef void set_unsafe(self, long r, long c, x):
226
"""
227
Set the value in row ``r``, column ``c`` to ``x``.
228
"""
229
raise NotImplementedError
230
231
cdef bint is_nonzero(self, long r, long c): # Not a Sage matrix operation
232
"""
233
Check if value in row ``r``, column ``c`` equals ``0``.
234
"""
235
return self.get_unsafe(r, c) != 0
236
237
cdef void add_multiple_of_row_c(self, long x, long y, s, bint col_start):
238
"""
239
Add ``s`` times row ``y`` to row ``x``. Argument ``col_start`` is
240
ignored.
241
"""
242
cdef long i
243
if s is None:
244
for i from 0 <= i < self._ncols:
245
self.set_unsafe(x, i, self.get_unsafe(x, i) + self.get_unsafe(y, i))
246
else:
247
for i from 0 <= i < self._ncols:
248
self.set_unsafe(x, i, self.get_unsafe(x, i) + s * self.get_unsafe(y, i))
249
250
cdef void swap_rows_c(self, long x, long y):
251
"""
252
Swap rows ``x`` and ``y``.
253
"""
254
cdef long i
255
for i from 0 <= i < self._ncols:
256
tmp = self.get_unsafe(x, i)
257
self.set_unsafe(x, i, self.get_unsafe(y, i))
258
self.set_unsafe(y, i, tmp)
259
260
cdef void rescale_row_c(self, long x, s, bint col_start):
261
"""
262
Scale row ``x`` by ``s``. Argument ``col_start`` is for Sage
263
compatibility, and is ignored.
264
"""
265
cdef long i
266
for i from 0 <= i < self._ncols:
267
self.set_unsafe(x, i, s * self.get_unsafe(x, i))
268
269
cdef void rescale_column_c(self, long y, s, bint start_row):
270
"""
271
Scale column ``y`` by ``s``. Argument ``start_row`` is for Sage
272
compatibility, and ignored.
273
"""
274
cdef long j
275
for j from 0 <= j < self._nrows:
276
self.set_unsafe(j, y, self.get_unsafe(j, y) * s)
277
278
cdef void pivot(self, long x, long y): # Not a Sage matrix operation
279
"""
280
Row-reduce to make column ``y`` have a ``1`` in row ``x`` and zeroes
281
elsewhere.
282
283
Assumption (not checked): the entry in row ``x``, column ``y`` is
284
nonzero to start with.
285
286
.. NOTE::
287
288
This is different from what matroid theorists tend to call a
289
pivot, as it does not involve a column exchange!
290
"""
291
cdef long i, j
292
self.rescale_row_c(x, self.get_unsafe(x, y) ** (-1), 0)
293
for i from 0 <= i < self._nrows:
294
s = self.get_unsafe(i, y)
295
if s and i != x:
296
self.add_multiple_of_row_c(i, x, -s, 0)
297
298
cdef list gauss_jordan_reduce(self, columns): # Not a Sage matrix operation
299
"""
300
Row-reduce so the lexicographically first basis indexes an identity
301
submatrix.
302
"""
303
cdef long r = 0
304
cdef list P = []
305
cdef long c, p, row
306
cdef bint is_pivot
307
for c in columns:
308
is_pivot = False
309
for row from r <= row < self._nrows:
310
if self.is_nonzero(row, c):
311
is_pivot = True
312
p = row
313
break
314
if is_pivot:
315
self.swap_rows_c(p, r)
316
self.rescale_row_c(r, self.get_unsafe(r, c) ** (-1), 0)
317
for row from 0 <= row < self._nrows:
318
if row != r and self.is_nonzero(row, c):
319
self.add_multiple_of_row_c(row, r, -self.get_unsafe(row, c), 0)
320
P.append(c)
321
r += 1
322
if r == self._nrows:
323
break
324
return P
325
326
cdef list nonzero_positions_in_row(self, long r):
327
"""
328
Get coordinates of nonzero entries of row ``r``.
329
"""
330
return [i for i in xrange(self._ncols) if self.is_nonzero(r, i)]
331
332
cdef LeanMatrix transpose(self):
333
"""
334
Return the transpose of the matrix.
335
"""
336
cdef LeanMatrix A = type(self)(self.ncols(), self.nrows())
337
cdef long i, j
338
for i from 0 <= i < self.nrows():
339
for j from 0 <= j < self.ncols():
340
A.set_unsafe(j, i, self.get_unsafe(i, j))
341
return A
342
343
cdef LeanMatrix _matrix_times_matrix_(self, LeanMatrix other):
344
"""
345
Multiply two matrices. Assumes ``self`` and ``M`` are of same type,
346
and compatible dimensions.
347
"""
348
cdef LeanMatrix A = type(self)(self.nrows(), other.ncols())
349
cdef i, j, k
350
for i from 0 <= i < self.nrows():
351
for j from 0 <= j < other.ncols():
352
for k from 0 <= k < self.ncols():
353
A.set_unsafe(i, j, self.get_unsafe(i, k) * other.get_unsafe(k, j))
354
return A
355
356
cdef LeanMatrix matrix_from_rows_and_columns(self, rows, columns):
357
"""
358
Return submatrix indexed by indicated rows and columns.
359
"""
360
cdef long r, c
361
cdef LeanMatrix A = type(self)(len(rows), len(columns), ring=self.base_ring())
362
for r from 0 <= r < len(rows):
363
for c from 0 <= c < len(columns):
364
A.set_unsafe(r, c, self.get_unsafe(rows[r], columns[c]))
365
return A
366
367
def __mul__(left, right):
368
"""
369
Multiply two matrices, or multiply a matrix with a constant from the
370
base ring.
371
372
Only works if both matrices are of the same type, or if the constant
373
is the first operand and can be coerced into the base ring.
374
375
EXAMPLES::
376
377
sage: from sage.matroids.lean_matrix import *
378
sage: A = GenericMatrix(2, 2, Matrix(GF(2), [[1, 0], [1, 1]]))
379
sage: B = GenericMatrix(3, 2, Matrix(GF(2), [[1, 0], [1, 0], [1, 0]]))
380
sage: B * A
381
LeanMatrix instance with 3 rows and 2 columns over Finite Field of size 2
382
"""
383
cdef long i
384
cdef LeanMatrix A
385
if isinstance(left, LeanMatrix):
386
if type(left) == type(right):
387
return (<LeanMatrix>left)._matrix_times_matrix_(right)
388
else:
389
return NotImplemented
390
if not left in (<LeanMatrix>right).base_ring():
391
try:
392
left = (<LeanMatrix>right).base_ring()(left)
393
except (TypeError, NotImplemented, ValueError):
394
return NotImplemented
395
A = (<LeanMatrix>right).copy()
396
for i from 0 <= i < A.nrows():
397
A.rescale_row_c(i, left, 0)
398
return A
399
400
def __neg__(self):
401
"""
402
Return a matrix ``B`` such that ``self + B`` is the all-zero matrix.
403
404
Note that the `` + `` operator is currently not supported.
405
406
EXAMPLES::
407
408
sage: from sage.matroids.lean_matrix import *
409
sage: A = GenericMatrix(2, 2, Matrix(GF(3), [[1, 0], [0, 1]]))
410
sage: C = -A
411
sage: -C == A
412
True
413
"""
414
cdef long i
415
cdef LeanMatrix A = self.copy()
416
x = self.base_ring()(-1)
417
for i from 0 <= i < A.nrows():
418
A.rescale_row_c(i, x, 0)
419
return A
420
421
def __richcmp__(left, right, op):
422
"""
423
Compare two matrices.
424
425
EXAMPLES::
426
427
sage: from sage.matroids.lean_matrix import *
428
sage: A = GenericMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
429
sage: B = GenericMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
430
sage: C = GenericMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
431
sage: D = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
432
sage: E = GenericMatrix(2, 3, Matrix(GF(2), [[1, 0, 0], [0, 1, 0]]))
433
sage: A == B # indirect doctest
434
True
435
sage: A != C # indirect doctest
436
True
437
sage: A == D # indirect doctest
438
False
439
sage: E == A
440
False
441
"""
442
cdef long i, j
443
if op in [0, 1, 4, 5]: # <, <=, >, >=
444
return NotImplemented
445
if not isinstance(left, LeanMatrix) or not isinstance(right, LeanMatrix):
446
return NotImplemented
447
if type(left) != type(right):
448
return NotImplemented
449
if op == 2: # ==
450
res = True
451
if op == 3: # !=
452
res = False
453
# res gets inverted if matroids are deemed different.
454
if left.nrows() != right.nrows():
455
return not res
456
if left.ncols() != right.ncols():
457
return not res
458
for i from 0 <= i < left.nrows():
459
for j from 0 <= j < left.ncols():
460
if (<LeanMatrix>left).get_unsafe(i, j) != (<LeanMatrix>right).get_unsafe(i, j):
461
return not res
462
return res
463
464
# In Cythonized classes, use ``__richcmp__()`` instead of ``__eq__()``, ``__ne__()``.
465
466
# Copying, loading, saving:
467
468
def __copy__(self):
469
"""
470
Return a copy of ``self``.
471
472
EXAMPLES::
473
474
sage: from sage.matroids.lean_matrix import *
475
sage: A = GenericMatrix(2, 5, Matrix(GF(5), [[1, 0, 1, 1, 1], [0, 1, 1, 2, 3]]))
476
sage: A == copy(A) # indirect doctest
477
True
478
"""
479
return self.copy()
480
481
def __deepcopy__(self, memo={}):
482
"""
483
Return a deep copy of ``self``.
484
485
EXAMPLES::
486
487
sage: from sage.matroids.lean_matrix import *
488
sage: A = GenericMatrix(2, 5, Matrix(GF(5), [[1, 0, 1, 1, 1], [0, 1, 1, 2, 3]]))
489
sage: A == deepcopy(A) # indirect doctest
490
True
491
"""
492
return self.copy()
493
494
def __reduce__(self):
495
"""
496
Save the object.
497
498
EXAMPLES::
499
500
sage: from sage.matroids.lean_matrix import *
501
sage: A = LeanMatrix(2, 5)
502
sage: A == loads(dumps(A)) # indirect doctest
503
Traceback (most recent call last):
504
...
505
NotImplementedError: subclasses need to implement this.
506
"""
507
raise NotImplementedError("subclasses need to implement this.")
508
509
cdef class GenericMatrix(LeanMatrix):
510
"""
511
Matrix over arbitrary Sage ring.
512
513
INPUT:
514
515
- ``nrows`` -- number of rows
516
- ``ncols`` -- number of columns
517
- ``M`` -- (default: ``None``) a ``Matrix`` or ``GenericMatrix`` of
518
dimensions at most ``m*n``.
519
- ``ring`` -- (default: ``None``) a Sage ring.
520
521
.. NOTE::
522
523
This class is intended for internal use by the LinearMatroid class
524
only. Hence it does not derive from ``SageObject``. If ``A`` is a
525
LeanMatrix instance, and you need access from other parts of Sage,
526
use ``Matrix(A)`` instead.
527
528
If the constructor is fed a GenericMatrix instance, the ``ring``
529
argument is ignored. Otherwise, the matrix entries
530
will be converted to the appropriate ring.
531
532
EXAMPLES::
533
534
sage: M = Matroid(ring=GF(5), matrix=[[1, 0, 1, 1, 1], [0, 1, 1, 2, 3]]) # indirect doctest
535
sage: M.is_isomorphic(matroids.Uniform(2, 5))
536
True
537
"""
538
539
def __init__(self, long nrows, long ncols, M=None, ring=None):
540
"""
541
See class docstring for full information.
542
543
EXAMPLES::
544
545
sage: from sage.matroids.lean_matrix import *
546
sage: A = GenericMatrix(2, 2, Matrix(GF(3), [[0, 0], [0, 0]])) # indirect doctest
547
sage: B = GenericMatrix(2, 2, ring=GF(3))
548
sage: A == B
549
True
550
"""
551
cdef long i, j
552
cdef bint ring_override = False
553
self._nrows = nrows
554
self._ncols = ncols
555
if M is not None:
556
self._base_ring = M.base_ring()
557
if ring is not None:
558
# Overrides M's ring
559
self._base_ring = ring
560
ring_override = True
561
# Default:
562
if self._base_ring is None:
563
self._base_ring = ZZ
564
self._zero = self._base_ring(0)
565
self._one = self._base_ring(1)
566
self._entries = [self._zero] * nrows * ncols
567
if M is not None:
568
if isinstance(M, GenericMatrix):
569
if nrows == (<GenericMatrix>M)._nrows and ncols == (<GenericMatrix>M)._ncols:
570
self._entries = (<GenericMatrix>M)._entries[:] # Slicing notation makes copy
571
else:
572
for i from 0 <= i < (<GenericMatrix>M)._nrows:
573
self._entries[i * self._ncols:i * self._ncols + (<GenericMatrix>M)._ncols] = (<GenericMatrix>M)._entries[i * (<GenericMatrix>M)._ncols:(i + 1) * (<GenericMatrix>M)._ncols]
574
elif isinstance(M, LeanMatrix):
575
if ring_override:
576
for i from 0 <= i < M.nrows():
577
for j from 0 <= j < M.ncols():
578
self._entries[i * self._ncols + j] = self._base_ring((<LeanMatrix>M).get_unsafe(i, j))
579
else:
580
for i from 0 <= i < M.nrows():
581
for j from 0 <= j < M.ncols():
582
self._entries[i * self._ncols + j] = (<LeanMatrix>M).get_unsafe(i, j)
583
else: # Sage Matrix or otherwise
584
if ring_override:
585
for i from 0 <= i < M.nrows():
586
for j from 0 <= j < M.ncols():
587
self._entries[i * self._ncols + j] = self._base_ring(M[i, j])
588
else:
589
for i from 0 <= i < M.nrows():
590
for j from 0 <= j < M.ncols():
591
self._entries[i * self._ncols + j] = M[i, j]
592
593
def __repr__(self):
594
"""
595
Print representation.
596
597
EXAMPLES::
598
599
sage: from sage.matroids.lean_matrix import *
600
sage: A = GenericMatrix(2, 2, Matrix(GF(3), [[0, 0], [0, 0]]))
601
sage: repr(A) # indirect doctest
602
'LeanMatrix instance with 2 rows and 2 columns over Finite Field of size 3'
603
"""
604
return "LeanMatrix instance with " + str(self._nrows) + " rows and " + str(self._ncols) + " columns over " + repr(self._base_ring)
605
606
cdef LeanMatrix copy(self): # Deprecated Sage matrix operation
607
cdef GenericMatrix M = GenericMatrix(self._nrows, self._ncols, M=self)
608
return M
609
610
cdef void resize(self, long k): # Not a Sage matrix operation
611
"""
612
Change number of rows to ``k``. Preserves data.
613
"""
614
cdef long l = len(self._entries) - k * self._ncols
615
if l > 0:
616
self._entries.extend([self._zero] * l)
617
elif l < 0:
618
del self._entries[k * self._ncols:]
619
self._nrows = k
620
621
cdef LeanMatrix stack(self, LeanMatrix M):
622
"""
623
Warning: assumes ``M`` is a GenericMatrix instance!
624
"""
625
cdef GenericMatrix A
626
cdef long i, j
627
A = GenericMatrix(0, 0, ring=self._base_ring)
628
A._entries = self._entries + ((<GenericMatrix>M)._entries)
629
A._nrows = self._nrows + M.nrows()
630
A._ncols = self._ncols
631
return A
632
633
cdef LeanMatrix augment(self, LeanMatrix M):
634
"""
635
Warning: assumes ``M`` is a GenericMatrix instance!
636
"""
637
cdef GenericMatrix A
638
cdef long i
639
cdef long Mn = M.ncols()
640
A = GenericMatrix(self._nrows, self._ncols + Mn, ring=self._base_ring)
641
for i from 0 <= i < self._nrows:
642
A._entries[i * A._ncols:i * A._ncols + self._ncols] = self._entries[i * self._ncols:(i + 1) * self._ncols]
643
A._entries[i * A._ncols + self._ncols:(i + 1) * A._ncols]=(<GenericMatrix>M)._entries[i * Mn:(i + 1) * Mn]
644
return A
645
646
cdef LeanMatrix prepend_identity(self): # Not a Sage matrix operation
647
cdef GenericMatrix A = GenericMatrix(self._nrows, self._ncols + self._nrows, ring=self._base_ring)
648
for i from 0 <= i < self._nrows:
649
A._entries[i * A._ncols + i] = self._one
650
A._entries[i * A._ncols + self._nrows:(i + 1) * A._ncols]=self._entries[i * self._ncols:(i + 1) * self._ncols]
651
return A
652
653
cpdef base_ring(self):
654
"""
655
Return the base ring of ``self``.
656
657
EXAMPLES::
658
659
sage: from sage.matroids.lean_matrix import GenericMatrix
660
sage: A = GenericMatrix(3, 4, ring=GF(5))
661
sage: A.base_ring()
662
Finite Field of size 5
663
"""
664
return self._base_ring
665
666
cpdef characteristic(self):
667
"""
668
Return the characteristic of ``self.base_ring()``.
669
670
EXAMPLES::
671
672
sage: from sage.matroids.lean_matrix import GenericMatrix
673
sage: A = GenericMatrix(3, 4, ring=GF(5))
674
sage: A.characteristic()
675
5
676
"""
677
if self._characteristic is None:
678
self._characteristic = self._base_ring.characteristic()
679
return self._characteristic
680
681
cdef get_unsafe(self, long r, long c):
682
return self._entries[r * self._ncols + c]
683
684
cdef void set_unsafe(self, long r, long c, x):
685
self._entries[r * self._ncols + c] = x
686
687
cdef void swap_rows_c(self, long x, long y):
688
"""
689
Swap rows ``x`` and ``y``.
690
"""
691
cdef list tmp = self._entries[x * self._ncols:(x + 1) * self._ncols]
692
self._entries[x * self._ncols:(x + 1) * self._ncols] = self._entries[y * self._ncols:(y + 1) * self._ncols]
693
self._entries[y * self._ncols:(y + 1) * self._ncols] = tmp
694
695
cdef LeanMatrix transpose(self):
696
"""
697
Return the transpose of the matrix.
698
"""
699
cdef GenericMatrix A
700
cdef long i, j
701
A = GenericMatrix(self._ncols, self._nrows, ring=self._base_ring)
702
for i from 0 <= i < self._nrows:
703
for j from 0 <= j < self._ncols:
704
A.set_unsafe(j, i, self.get_unsafe(i, j))
705
return A
706
707
cdef inline row_inner_product(self, long i, long j): # Not a Sage matrix operation
708
"""
709
Return the inner product between rows ``i`` and ``j``.
710
"""
711
cdef long k
712
res = 0
713
for k from 0 <= k < self._ncols:
714
x = self.get_unsafe(i, k)
715
y = self.get_unsafe(j, k)
716
if y and y != self._one:
717
y += self._one
718
res += x * y
719
return res
720
721
cdef LeanMatrix _matrix_times_matrix_(self, LeanMatrix other):
722
"""
723
Return the product ``self * other``.
724
"""
725
cdef GenericMatrix A, ot
726
cdef long i, j, t
727
ot = <GenericMatrix > other
728
A = GenericMatrix(self._nrows, ot._ncols, ring=self._base_ring)
729
for i from 0 <= i < A._nrows:
730
for j from 0 <= j < A._ncols:
731
s = self._zero
732
for t from 0 <= t < self._ncols:
733
s += self.get_unsafe(i, t) * ot.get_unsafe(t, j)
734
A.set_unsafe(i, j, s)
735
return A
736
737
def __richcmp__(left, right, op):
738
"""
739
Compare two matrices.
740
741
EXAMPLES::
742
743
sage: from sage.matroids.lean_matrix import *
744
sage: A = GenericMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
745
sage: B = GenericMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
746
sage: C = GenericMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
747
sage: D = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
748
sage: E = GenericMatrix(2, 3, Matrix(GF(2), [[1, 0, 0], [0, 1, 0]]))
749
sage: A == B # indirect doctest
750
True
751
sage: A != C # indirect doctest
752
True
753
sage: A == D # indirect doctest
754
False
755
sage: E == A
756
False
757
"""
758
cdef long i, j
759
if op in [0, 1, 4, 5]: # <, <=, >, >=
760
return NotImplemented
761
if not isinstance(left, GenericMatrix) or not isinstance(right, GenericMatrix):
762
return NotImplemented
763
if op == 2: # ==
764
res = True
765
if op == 3: # !=
766
res = False
767
# res gets inverted if matroids are deemed different.
768
if left.nrows() != right.nrows():
769
return not res
770
if left.ncols() != right.ncols():
771
return not res
772
if left.base_ring() != right.base_ring():
773
return not res
774
if (<GenericMatrix>left)._entries != (<GenericMatrix>right)._entries:
775
return not res
776
return res
777
778
def __reduce__(self):
779
"""
780
Save the object.
781
782
EXAMPLES::
783
784
sage: from sage.matroids.lean_matrix import *
785
sage: A = GenericMatrix(2, 5, ring=QQ)
786
sage: A == loads(dumps(A)) # indirect doctest
787
True
788
sage: C = GenericMatrix(2, 2, Matrix(GF(3), [[1, 1], [0, 1]]))
789
sage: C == loads(dumps(C))
790
True
791
"""
792
import sage.matroids.unpickling
793
version = 0
794
data = (self.nrows(), self.ncols(), self.base_ring(), self._entries)
795
return sage.matroids.unpickling.unpickle_generic_matrix, (version, data)
796
797
# Binary matrices
798
799
cdef bint GF2_not_defined = True
800
cdef GF2, GF2_one, GF2_zero
801
802
cdef class BinaryMatrix(LeanMatrix):
803
"""
804
Binary matrix class. Entries are stored bit-packed into integers.
805
806
INPUT:
807
808
- ``m`` -- Number of rows.
809
- ``n`` -- Number of columns.
810
- ``M`` -- (default: ``None``) Matrix or BinaryMatrix instance.
811
Assumption: dimensions of ``M`` are at most ``m`` by ``n``.
812
- ``ring`` -- (default: ``None``) ignored.
813
814
EXAMPLES::
815
816
sage: from sage.matroids.lean_matrix import *
817
sage: A = BinaryMatrix(2, 2, Matrix(GF(7), [[0, 0], [0, 0]]))
818
sage: B = BinaryMatrix(2, 2, ring=GF(5))
819
sage: C = BinaryMatrix(2, 2)
820
sage: A == B and A == C
821
True
822
"""
823
def __cinit__(self, long m, long n, object M=None, object ring=None):
824
"""
825
Init internal data structures.
826
827
EXAMPLES::
828
829
sage: from sage.matroids.lean_matrix import *
830
sage: A = BinaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
831
sage: A.nrows()
832
2
833
"""
834
cdef long i, j
835
self._nrows = m
836
self._ncols = n
837
self._M = <bitset_t* > sage_malloc(self._nrows * sizeof(bitset_t))
838
if isinstance(M, BinaryMatrix):
839
j = max(1, (<BinaryMatrix>M)._ncols)
840
else:
841
j = max(1, self._ncols)
842
for i from 0 <= i < self._nrows:
843
bitset_init(self._M[i], j)
844
bitset_clear(self._M[i])
845
bitset_init(self._temp, j)
846
847
def __init__(self, long m, long n, object M=None, object ring=None):
848
"""
849
See class docstring for full specification.
850
851
EXAMPLES::
852
853
sage: from sage.matroids.lean_matrix import *
854
sage: A = BinaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
855
sage: A.nrows()
856
2
857
"""
858
cdef long i, j
859
global GF2, GF2_zero, GF2_one, GF2_not_defined
860
if GF2_not_defined:
861
GF2 = GF(2)
862
GF2_zero = GF2(0)
863
GF2_one = GF2(1)
864
GF2_not_defined = False
865
if M is not None:
866
if isinstance(M, BinaryMatrix):
867
for i from 0 <= i < M.nrows():
868
bitset_copy(self._M[i], (<BinaryMatrix>M)._M[i])
869
j = max(1, self._ncols)
870
for i from 0 <= i < self._nrows:
871
bitset_realloc(self._M[i], j)
872
elif isinstance(M, LeanMatrix):
873
for i from 0 <= i < M.nrows():
874
for j from 0 <= j < M.ncols():
875
if int((<LeanMatrix>M).get_unsafe(i, j)) % 2:
876
self.set(i, j)
877
elif isinstance(M, Matrix):
878
for i from 0 <= i < M.nrows():
879
for j from 0 <= j < M.ncols():
880
if int((<Matrix>M).get_unsafe(i, j)) % 2:
881
self.set(i, j)
882
else:
883
raise TypeError("unrecognized input type")
884
885
def __dealloc__(self):
886
cdef long i
887
for i from 0 <= i < self._nrows:
888
bitset_free(self._M[i])
889
sage_free(self._M)
890
bitset_free(self._temp)
891
892
def __repr__(self):
893
"""
894
Print representation string
895
896
EXAMPLES::
897
898
sage: from sage.matroids.lean_matrix import *
899
sage: A = BinaryMatrix(2, 3, Matrix(GF(4, 'x'), [[0, 0], [0, 0]]))
900
sage: repr(A) # indirect doctest
901
'2 x 3 BinaryMatrix\n[000]\n[000]'
902
"""
903
out = str(self._nrows) + ' x ' + str(self._ncols) + ' BinaryMatrix'
904
cdef long i
905
if self._ncols > 0:
906
for i from 0 <= i < self._nrows:
907
out += '\n[' + bitset_string(self._M[i]) + ']'
908
else:
909
for i from 0 <= i < self._nrows:
910
out += '[]'
911
return out
912
913
def _matrix_(self):
914
"""
915
Return a matrix version.
916
917
EXAMPLES::
918
919
sage: from sage.matroids.lean_matrix import *
920
sage: A = Matrix(GF(2), [[1, 0], [0, 1]])
921
sage: A == BinaryMatrix(2, 2, A)._matrix_()
922
True
923
"""
924
cdef long i, j
925
M = sage.matrix.constructor.Matrix(GF(2), self._nrows, self._ncols)
926
for i from 0 <= i < self._nrows:
927
for j from 0 <= j < self._ncols:
928
if bitset_in(self._M[i], j):
929
M[i, j] = 1
930
return M
931
932
cdef LeanMatrix copy(self): # Deprecated Sage matrix operation
933
cdef BinaryMatrix B
934
cdef long i
935
B = BinaryMatrix(self.nrows(), self.ncols())
936
for i from 0 <= i < self._nrows:
937
bitset_copy(B._M[i], self._M[i])
938
return B
939
940
cdef void resize(self, long k): # Not a Sage matrix operation
941
"""
942
Change number of rows to ``k``. Preserves data.
943
"""
944
cdef long i, c
945
if k < self._nrows:
946
for i from k <= i < self._nrows:
947
bitset_free(self._M[i])
948
self._nrows = k
949
self._M = <bitset_t* > sage_realloc(self._M, k * sizeof(bitset_t))
950
if k > self._nrows:
951
self._M = <bitset_t* > sage_realloc(self._M, k * sizeof(bitset_t))
952
c = max(1, self._ncols)
953
for i from self._nrows <= i < k:
954
bitset_init(self._M[i], c)
955
bitset_clear(self._M[i])
956
self._nrows = k
957
958
cdef LeanMatrix stack(self, LeanMatrix MM):
959
"""
960
Given ``A`` and ``B``, return
961
[A]
962
[B]
963
"""
964
cdef BinaryMatrix R
965
cdef BinaryMatrix M = <BinaryMatrix > MM
966
cdef long i
967
R = BinaryMatrix(self.nrows() + M.nrows(), self.ncols(), self)
968
for i from 0 <= i < M.nrows():
969
bitset_copy(R._M[i + self.nrows()], M._M[i])
970
return R
971
972
cdef LeanMatrix augment(self, LeanMatrix MM):
973
"""
974
Given ``A`` and ``B``, return
975
[A B]
976
"""
977
cdef BinaryMatrix R
978
cdef BinaryMatrix M = <BinaryMatrix > MM
979
cdef long i, j
980
R = BinaryMatrix(self.nrows(), self.ncols() + M.ncols(), self)
981
for i from 0 <= i < R.nrows():
982
for j from 0 <= j < M.ncols():
983
bitset_set_to(R._M[i], self.ncols() + j, bitset_in(M._M[i], j))
984
return R
985
986
cdef LeanMatrix prepend_identity(self): # Not a Sage matrix operation
987
"""
988
Return the matrix obtained by prepending an identity matrix. Special case of ``augment``.
989
"""
990
cdef long i, j
991
cdef BinaryMatrix A = BinaryMatrix(self._nrows, self._ncols + self._nrows)
992
for i from 0 <= i < self._nrows:
993
A.set(i, i)
994
for j from 0 <= j < self._ncols:
995
if self.get(i, j):
996
A.set(i, self._nrows + j)
997
return A
998
999
cpdef base_ring(self):
1000
"""
1001
Return `GF(2)`.
1002
1003
EXAMPLES::
1004
1005
sage: from sage.matroids.lean_matrix import *
1006
sage: A = BinaryMatrix(4, 4)
1007
sage: A.base_ring()
1008
Finite Field of size 2
1009
"""
1010
global GF2
1011
return GF2
1012
1013
cpdef characteristic(self):
1014
"""
1015
Return the characteristic of ``self.base_ring()``.
1016
1017
EXAMPLES::
1018
1019
sage: from sage.matroids.lean_matrix import *
1020
sage: A = BinaryMatrix(3, 4)
1021
sage: A.characteristic()
1022
2
1023
"""
1024
return 2
1025
1026
cdef get_unsafe(self, long r, long c):
1027
global GF2_one, GF2_zero
1028
if bitset_in(self._M[r], c):
1029
return GF2_one
1030
return GF2_zero
1031
1032
cdef void set_unsafe(self, long r, long c, x):
1033
if x:
1034
bitset_add(self._M[r], c)
1035
else:
1036
bitset_discard(self._M[r], c)
1037
1038
cdef bint is_nonzero(self, long r, long c): # Not a Sage matrix operation
1039
return bitset_in(self._M[r], c)
1040
1041
cdef inline bint get(self, long r, long c): # Not a Sage matrix operation
1042
return bitset_in(self._M[r], c)
1043
1044
cdef inline void set(self, long x, long y): # Not a Sage matrix operation
1045
bitset_add(self._M[x], y)
1046
1047
cdef void pivot(self, long x, long y): # Not a Sage matrix operation
1048
"""
1049
Row-reduce to make column ``y`` have a ``1`` in row ``x`` and
1050
zeroes elsewhere.
1051
1052
Assumption (not checked): the entry in row ``x``, column ``y``
1053
is nonzero to start with.
1054
1055
.. NOTE::
1056
1057
This is different from what matroid theorists tend to call a
1058
pivot, as it does not involve a column exchange!
1059
"""
1060
cdef long i, j
1061
for i from 0 <= i < self._nrows:
1062
if bitset_in(self._M[i], y) and i != x:
1063
bitset_symmetric_difference(self._M[i], self._M[i], self._M[x])
1064
1065
cdef inline long row_len(self, long i): # Not a Sage matrix operation
1066
"""
1067
Return number of nonzero entries in row ``i``.
1068
"""
1069
return bitset_len(self._M[i])
1070
1071
cdef inline bint row_inner_product(self, long i, long j): # Not a Sage matrix operation
1072
"""
1073
Return the inner product between rows ``i`` and ``j``.
1074
"""
1075
bitset_copy(self._temp, self._M[i])
1076
bitset_intersection(self._temp, self._temp, self._M[j])
1077
return bitset_len(self._temp) % 2
1078
1079
cdef void add_multiple_of_row_c(self, long i, long j, s, bint col_start):
1080
"""
1081
Add row ``j`` to row ``i``. Other arguments are ignored.
1082
"""
1083
bitset_symmetric_difference(self._M[i], self._M[i], self._M[j])
1084
1085
cdef void swap_rows_c(self, long i, long j):
1086
bitset_copy(self._temp, self._M[i])
1087
bitset_copy(self._M[i], self._M[j])
1088
bitset_copy(self._M[j], self._temp)
1089
1090
cdef inline list nonzero_positions_in_row(self, long i):
1091
"""
1092
Get coordinates of nonzero entries of row ``r``.
1093
"""
1094
return bitset_list(self._M[i])
1095
1096
cdef inline list row_sum(self, object L): # Not a Sage matrix operation
1097
"""
1098
Return the mod-2 sum of the rows indexed by ``L``.
1099
"""
1100
bitset_clear(self._temp)
1101
for l in L:
1102
bitset_symmetric_difference(self._temp, self._temp, self._M[l])
1103
return bitset_list(self._temp)
1104
1105
cdef inline list row_union(self, object L): # Not a Sage matrix operation
1106
"""
1107
Return the ``or`` of the rows indexed by ``L``.
1108
"""
1109
bitset_clear(self._temp)
1110
for l in L:
1111
bitset_union(self._temp, self._temp, self._M[l])
1112
return bitset_list(self._temp)
1113
1114
cdef LeanMatrix transpose(self):
1115
"""
1116
Return the transpose of the matrix.
1117
"""
1118
cdef BinaryMatrix T
1119
cdef long i, j
1120
T = BinaryMatrix(self._ncols, self._nrows)
1121
for i from 0 <= i < self._nrows:
1122
j = bitset_first(self._M[i])
1123
while j >= 0:
1124
T.set(j, i)
1125
j = bitset_next(self._M[i], j + 1)
1126
return T
1127
1128
cdef LeanMatrix _matrix_times_matrix_(self, LeanMatrix other):
1129
"""
1130
Return the product ``self * other``.
1131
"""
1132
cdef BinaryMatrix M
1133
cdef BinaryMatrix ot = <BinaryMatrix > other
1134
M = BinaryMatrix(self._nrows, ot._ncols)
1135
cdef long i, j
1136
for i from 0 <= i < self._nrows:
1137
j = bitset_first(self._M[i])
1138
while j >= 0:
1139
bitset_symmetric_difference(M._M[i], M._M[i], ot._M[j])
1140
j = bitset_next(self._M[i], j + 1)
1141
return M
1142
1143
cdef LeanMatrix matrix_from_rows_and_columns(self, rows, columns):
1144
"""
1145
Return submatrix indexed by indicated rows and columns.
1146
"""
1147
cdef long r, c
1148
cdef BinaryMatrix A = BinaryMatrix(len(rows), len(columns))
1149
for r from 0 <= r < len(rows):
1150
for c from 0 <= c < len(columns):
1151
if bitset_in(self._M[rows[r]], columns[c]):
1152
bitset_add(A._M[r], c)
1153
return A
1154
1155
cdef list _character(self, bitset_t x): # Not a Sage matrix operation
1156
"""
1157
Return the vector of intersection lengths of the rows with ``x``.
1158
"""
1159
cdef long i
1160
I = []
1161
for i from 0 <= i < self._nrows:
1162
bitset_intersection(self._temp, self._M[i], x)
1163
I.append(bitset_len(self._temp))
1164
return I
1165
1166
cdef BinaryMatrix _distinguish_by(self, BinaryMatrix P):
1167
"""
1168
Helper method for equitable partition.
1169
"""
1170
cdef BinaryMatrix Q
1171
d = {}
1172
for i from 0 <= i < self._nrows:
1173
c = hash(tuple(P._character(self._M[i])))
1174
if c in d:
1175
d[c].append(i)
1176
else:
1177
d[c] = [i]
1178
Q = BinaryMatrix(len(d), self._nrows)
1179
i = 0
1180
for c in sorted(d):
1181
for j in d[c]:
1182
bitset_add(Q._M[i], j)
1183
i += 1
1184
return Q
1185
1186
cdef BinaryMatrix _splice_by(self, BinaryMatrix P):
1187
"""
1188
Helper method for equitable partition.
1189
"""
1190
cdef BinaryMatrix Q
1191
cdef long i, j, r
1192
Q = BinaryMatrix(self._ncols, self._ncols)
1193
r = 0
1194
for i from 0 <= i < self._nrows:
1195
for j from 0 <= j < P._nrows:
1196
bitset_intersection(self._temp, self._M[i], P._M[j])
1197
if not bitset_isempty(self._temp):
1198
bitset_copy(Q._M[r], self._temp)
1199
r += 1
1200
Q.resize(r)
1201
return Q
1202
1203
cdef BinaryMatrix _isolate(self, long j):
1204
"""
1205
Helper method for isomorphism test.
1206
"""
1207
cdef BinaryMatrix Q
1208
cdef long i, r
1209
Q = BinaryMatrix(self._nrows + 1, self._ncols)
1210
for i from 0 <= i < self._nrows:
1211
bitset_copy(Q._M[i], self._M[i])
1212
bitset_discard(Q._M[i], j)
1213
bitset_add(Q._M[self._nrows], j)
1214
return Q
1215
1216
cdef BinaryMatrix equitable_partition(self, BinaryMatrix P=None):
1217
"""
1218
Compute an equitable partition of the columns.
1219
"""
1220
if P is None:
1221
P = BinaryMatrix(1, self._ncols)
1222
bitset_set_first_n(P._M[0], self._ncols)
1223
r = 0
1224
while P.nrows() > r:
1225
r = P.nrows()
1226
P = P._splice_by(self._distinguish_by(P))
1227
return P
1228
1229
cdef bint is_isomorphic(self, BinaryMatrix other, BinaryMatrix s_eq=None, BinaryMatrix o_eq=None): # Not a Sage matrix operation
1230
"""
1231
Test for isomorphism between the row spaces.
1232
"""
1233
cdef long e, f, i, j
1234
if s_eq is None:
1235
s_eq = self.equitable_partition()
1236
if o_eq is None:
1237
o_eq = other.equitable_partition()
1238
1239
if s_eq.nrows() != o_eq.nrows():
1240
return False
1241
if s_eq.nrows() == s_eq.ncols(): # s_eq and o_eq partition into singletons
1242
morph = [0 for i from 0 <= i < self._nrows]
1243
for i from 0 <= i < self._nrows:
1244
morph[bitset_first(s_eq._M[i])] = bitset_first(o_eq._M[i])
1245
for i from 0 <= i < self._nrows:
1246
for j from 0 <= j < self._ncols:
1247
if self.get(i, j) != other.get(morph[i], morph[j]):
1248
return False
1249
return True
1250
1251
for i from 0 <= i < s_eq.nrows():
1252
if s_eq.row_len(i) != o_eq.row_len(i):
1253
return False
1254
for i from 0 <= i < s_eq.nrows():
1255
if s_eq.row_len(i) > 1:
1256
break
1257
e = bitset_first(s_eq._M[i])
1258
s_eq2 = self.equitable_partition(s_eq._isolate(e))
1259
f = bitset_first(o_eq._M[i])
1260
while f >= 0:
1261
if self.is_isomorphic(other, s_eq2, other.equitable_partition(o_eq._isolate(f))):
1262
return True
1263
f = bitset_next(o_eq._M[i], f + 1)
1264
return False
1265
1266
def __neg__(self):
1267
"""
1268
Negate the matrix.
1269
1270
In characteristic 2, this does nothing.
1271
1272
EXAMPLES::
1273
1274
sage: from sage.matroids.lean_matrix import *
1275
sage: A = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
1276
sage: B = -A # indirect doctest
1277
sage: B == A
1278
True
1279
"""
1280
return self.copy()
1281
1282
def __richcmp__(left, right, op):
1283
"""
1284
Compare two matrices.
1285
1286
EXAMPLES::
1287
1288
sage: from sage.matroids.lean_matrix import *
1289
sage: A = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
1290
sage: B = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
1291
sage: C = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
1292
sage: D = GenericMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
1293
sage: E = BinaryMatrix(2, 3, Matrix(GF(2), [[1, 0, 0], [0, 1, 0]]))
1294
sage: A == B # indirect doctest
1295
True
1296
sage: A != C # indirect doctest
1297
True
1298
sage: A == D # indirect doctest
1299
False
1300
sage: E == A
1301
False
1302
"""
1303
cdef long i, j
1304
if op in [0, 1, 4, 5]: # <, <=, >, >=
1305
return NotImplemented
1306
if not isinstance(left, BinaryMatrix) or not isinstance(right, BinaryMatrix):
1307
return NotImplemented
1308
if op == 2: # ==
1309
res = True
1310
if op == 3: # !=
1311
res = False
1312
# res gets inverted if matroids are deemed different.
1313
if left.nrows() != right.nrows():
1314
return not res
1315
if left.ncols() != right.ncols():
1316
return not res
1317
for i from 0 <= i < left.nrows():
1318
if not bitset_eq((<BinaryMatrix>left)._M[i], (<BinaryMatrix>right)._M[i]):
1319
return not res
1320
return res
1321
1322
def __reduce__(self):
1323
"""
1324
Save the object.
1325
1326
EXAMPLES::
1327
1328
sage: from sage.matroids.lean_matrix import *
1329
sage: A = BinaryMatrix(2, 5)
1330
sage: A == loads(dumps(A)) # indirect doctest
1331
True
1332
sage: C = BinaryMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
1333
sage: C == loads(dumps(C))
1334
True
1335
"""
1336
import sage.matroids.unpickling
1337
version = 0
1338
M = []
1339
versionB = 0
1340
size = 0
1341
limbs = 0
1342
longsize = 0
1343
for i from 0 <= i < self.nrows():
1344
versionB, size, limbs, longsize, data = bitset_pickle(self._M[i])
1345
M.append(data)
1346
data = (self.nrows(), self.ncols(), versionB, size, limbs, longsize, M)
1347
return sage.matroids.unpickling.unpickle_binary_matrix, (version, data)
1348
1349
cdef bint GF3_not_defined = True
1350
cdef GF3, GF3_one, GF3_zero, GF3_minus_one
1351
1352
1353
cdef class TernaryMatrix(LeanMatrix):
1354
"""
1355
Ternary matrix class. Entries are stored bit-packed into integers.
1356
1357
INPUT:
1358
1359
- ``m`` -- Number of rows.
1360
- ``n`` -- Number of columns.
1361
- ``M`` -- (default: ``None``) ``Matrix`` or ``TernaryMatrix`` instance.
1362
Assumption: dimensions of ``M`` are at most ``m`` by ``n``.
1363
- ``ring`` -- (default: ``None``) ignored.
1364
1365
EXAMPLES::
1366
1367
sage: from sage.matroids.lean_matrix import *
1368
sage: A = TernaryMatrix(2, 2, Matrix(GF(7), [[0, 0], [0, 0]]))
1369
sage: B = TernaryMatrix(2, 2, ring=GF(5))
1370
sage: C = TernaryMatrix(2, 2)
1371
sage: A == B and A == C
1372
True
1373
"""
1374
def __cinit__(self, long m, long n, M=None, ring=None):
1375
"""
1376
Init internal data structures.
1377
1378
EXAMPLES::
1379
1380
sage: from sage.matroids.lean_matrix import *
1381
sage: A = TernaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
1382
sage: A.nrows()
1383
2
1384
"""
1385
cdef long i, j
1386
global GF3, GF3_zero, GF3_one, GF3_minus_one, GF3_not_defined
1387
if GF3_not_defined:
1388
GF3 = GF(3)
1389
GF3_zero = GF3(0)
1390
GF3_one = GF3(1)
1391
GF3_minus_one = GF3(2)
1392
GF3_not_defined = False
1393
1394
self._nrows = m
1395
self._ncols = n
1396
self._M0 = <bitset_t* > sage_malloc(self._nrows * sizeof(bitset_t))
1397
self._M1 = <bitset_t* > sage_malloc(self._nrows * sizeof(bitset_t))
1398
1399
if isinstance(M, TernaryMatrix):
1400
j = max(1, (<TernaryMatrix>M)._ncols)
1401
else:
1402
j = max(1, self._ncols)
1403
for i from 0 <= i < self._nrows:
1404
bitset_init(self._M0[i], j)
1405
bitset_clear(self._M0[i])
1406
bitset_init(self._M1[i], j)
1407
bitset_clear(self._M1[i])
1408
bitset_init(self._s, j)
1409
bitset_init(self._t, j)
1410
bitset_init(self._u, j)
1411
1412
def __init__(self, long m, long n, M=None, ring=None):
1413
"""
1414
See class docstring for full specification.
1415
1416
EXAMPLES::
1417
1418
sage: from sage.matroids.lean_matrix import *
1419
sage: A = TernaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
1420
sage: A.nrows()
1421
2
1422
"""
1423
cdef long i, j
1424
if M is not None:
1425
if isinstance(M, TernaryMatrix):
1426
for i from 0 <= i < (<TernaryMatrix>M)._nrows:
1427
bitset_copy(self._M0[i], (<TernaryMatrix>M)._M0[i])
1428
bitset_copy(self._M1[i], (<TernaryMatrix>M)._M1[i])
1429
j = max(1, self._ncols)
1430
for i from 0 <= i < self._nrows:
1431
bitset_realloc(self._M0[i], j)
1432
bitset_realloc(self._M1[i], j)
1433
return
1434
if isinstance(M, LeanMatrix):
1435
for i from 0 <= i < M.nrows():
1436
for j from 0 <= j < M.ncols():
1437
s = int((<LeanMatrix>M).get_unsafe(i, j)) % 3
1438
if s:
1439
bitset_add(self._M0[i], j)
1440
if s == 2:
1441
bitset_add(self._M1[i], j)
1442
return
1443
if isinstance(M, Matrix):
1444
for i from 0 <= i < M.nrows():
1445
for j from 0 <= j < M.ncols():
1446
s = int((<Matrix>M).get_unsafe(i, j)) % 3
1447
if s:
1448
bitset_add(self._M0[i], j)
1449
if s == 2:
1450
bitset_add(self._M1[i], j)
1451
1452
def __dealloc__(self):
1453
cdef long i
1454
for i from 0 <= i < self._nrows:
1455
bitset_free(self._M0[i])
1456
bitset_free(self._M1[i])
1457
sage_free(self._M0)
1458
sage_free(self._M1)
1459
bitset_free(self._s)
1460
bitset_free(self._t)
1461
bitset_free(self._u)
1462
1463
def __repr__(self):
1464
"""
1465
Print representation string
1466
1467
EXAMPLES::
1468
1469
sage: from sage.matroids.lean_matrix import *
1470
sage: A = TernaryMatrix(2, 3, Matrix(GF(4, 'x'), [[0, 0], [0, 0]]))
1471
sage: repr(A) # indirect doctest
1472
'2 x 3 TernaryMatrix\n[000]\n[000]'
1473
"""
1474
out = str(self._nrows) + ' x ' + str(self._ncols) + ' TernaryMatrix'
1475
cdef long i
1476
if self._ncols > 0:
1477
for i from 0 <= i < self._nrows:
1478
out += '\n['
1479
for j from 0 <= j < self._ncols:
1480
x = self.get(i, j)
1481
if x == 0:
1482
out += '0'
1483
if x == 1:
1484
out += '+'
1485
if x == -1:
1486
out += '-'
1487
out += ']'
1488
else:
1489
for i from 0 <= i < self._nrows:
1490
out += '[]'
1491
return out
1492
1493
def _matrix_(self):
1494
"""
1495
Return a matrix version.
1496
1497
EXAMPLES::
1498
1499
sage: from sage.matroids.lean_matrix import *
1500
sage: A = Matrix(GF(3), [[1, 0], [0, 1]])
1501
sage: A == TernaryMatrix(2, 2, A)._matrix_()
1502
True
1503
"""
1504
cdef long i, j
1505
M = sage.matrix.constructor.Matrix(GF(3), self._nrows, self._ncols)
1506
for i from 0 <= i < self._nrows:
1507
for j from 0 <= j < self._ncols:
1508
M[i, j] = self.get(i, j)
1509
return M
1510
1511
cdef get_unsafe(self, long r, long c):
1512
global GF3_zero, GF3_one, GF3_minus_one
1513
if not bitset_in(self._M0[r], c):
1514
return GF3_zero
1515
if not bitset_in(self._M1[r], c):
1516
return GF3_one
1517
return GF3_minus_one
1518
1519
cdef void set_unsafe(self, long r, long c, x):
1520
self.set(r, c, x)
1521
1522
cdef LeanMatrix copy(self): # Deprecated Sage matrix operation
1523
cdef TernaryMatrix T
1524
cdef long i
1525
T = TernaryMatrix(self._nrows, self._ncols)
1526
for i from 0 <= i < self._nrows:
1527
bitset_copy(T._M0[i], self._M0[i])
1528
bitset_copy(T._M1[i], self._M1[i])
1529
return T
1530
1531
cdef void resize(self, long k): # Not a Sage matrix operation
1532
"""
1533
Change number of rows to ``k``. Preserves data.
1534
"""
1535
cdef long i
1536
if k < self._nrows:
1537
for i from k <= i < self._nrows:
1538
bitset_free(self._M0[i])
1539
bitset_free(self._M1[i])
1540
self._nrows = k
1541
self._M0 = <bitset_t* > sage_realloc(self._M0, k * sizeof(bitset_t))
1542
self._M1 = <bitset_t* > sage_realloc(self._M1, k * sizeof(bitset_t))
1543
if k > self._nrows:
1544
self._M0 = <bitset_t* > sage_realloc(self._M0, k * sizeof(bitset_t))
1545
self._M1 = <bitset_t* > sage_realloc(self._M1, k * sizeof(bitset_t))
1546
c = max(1, self._ncols)
1547
for i from self._nrows <= i < k:
1548
bitset_init(self._M0[i], c)
1549
bitset_clear(self._M0[i])
1550
bitset_init(self._M1[i], c)
1551
bitset_clear(self._M1[i])
1552
self._nrows = k
1553
1554
cdef LeanMatrix stack(self, LeanMatrix MM):
1555
cdef TernaryMatrix R
1556
cdef TernaryMatrix M = <TernaryMatrix > MM
1557
cdef long i
1558
R = TernaryMatrix(self.nrows() + M.nrows(), self.ncols(), self)
1559
for i from 0 <= i < M.nrows():
1560
bitset_copy(R._M0[i + self.nrows()], M._M0[i])
1561
bitset_copy(R._M1[i + self.nrows()], M._M1[i])
1562
return R
1563
1564
cdef LeanMatrix augment(self, LeanMatrix MM):
1565
cdef TernaryMatrix R
1566
cdef TernaryMatrix M = <TernaryMatrix > MM
1567
cdef long i, j
1568
R = TernaryMatrix(self.nrows(), self.ncols() + M.ncols(), self)
1569
for i from 0 <= i < R.nrows():
1570
for j from 0 <= j < M.ncols():
1571
bitset_set_to(R._M0[i], self.ncols() + j, bitset_in(M._M0[i], j))
1572
bitset_set_to(R._M1[i], self.ncols() + j, bitset_in(M._M1[i], j))
1573
return R
1574
1575
cdef LeanMatrix prepend_identity(self): # Not a Sage matrix operation
1576
"""
1577
Return the matrix obtained by prepending an identity matrix.
1578
1579
Special case of ``augment``.
1580
"""
1581
cdef long i, j
1582
cdef TernaryMatrix A = TernaryMatrix(self._nrows, self._ncols + self._nrows)
1583
for i from 0 <= i < self._nrows:
1584
A.set(i, i, 1)
1585
for j from 0 <= j < self._ncols:
1586
A.set(i, self._nrows + j, self.get(i, j))
1587
return A
1588
1589
cpdef base_ring(self):
1590
"""
1591
Return GF(3).
1592
1593
EXAMPLES::
1594
1595
sage: from sage.matroids.lean_matrix import *
1596
sage: A = TernaryMatrix(3, 3)
1597
sage: A.base_ring()
1598
Finite Field of size 3
1599
"""
1600
global GF3
1601
return GF3
1602
1603
cpdef characteristic(self):
1604
"""
1605
Return the characteristic of ``self.base_ring()``.
1606
1607
EXAMPLES::
1608
1609
sage: from sage.matroids.lean_matrix import *
1610
sage: A = TernaryMatrix(3, 4)
1611
sage: A.characteristic()
1612
3
1613
"""
1614
return 3
1615
1616
cdef inline long get(self, long r, long c): # Not a Sage matrix operation
1617
if not bitset_in(self._M0[r], c):
1618
return 0
1619
if not bitset_in(self._M1[r], c):
1620
return 1
1621
return -1
1622
1623
cdef inline void set(self, long r, long c, x): # Not a Sage matrix operation
1624
if x == 0:
1625
bitset_discard(self._M0[r], c)
1626
bitset_discard(self._M1[r], c)
1627
if x == 1:
1628
bitset_add(self._M0[r], c)
1629
bitset_discard(self._M1[r], c)
1630
if x == -1:
1631
bitset_add(self._M0[r], c)
1632
bitset_add(self._M1[r], c)
1633
1634
cdef bint is_nonzero(self, long r, long c): # Not a Sage matrix operation
1635
return bitset_in(self._M0[r], c)
1636
1637
cdef bint _is_negative(self, long r, long c):
1638
return bitset_in(self._M1[r], c)
1639
1640
cdef inline long row_len(self, long i): # Not a Sage matrix operation
1641
"""
1642
Return number of nonzero entries in row ``i``.
1643
"""
1644
return bitset_len(self._M0[i])
1645
1646
cdef inline long row_inner_product(self, long i, long j): # Not a Sage matrix operation
1647
"""
1648
Return the inner product between rows ``i`` and ``j``.
1649
"""
1650
cdef long u
1651
if i == j:
1652
return self.row_len(i) % 3
1653
bitset_intersection(self._s, self._M0[i], self._M0[j])
1654
bitset_symmetric_difference(self._t, self._M1[i], self._M1[j])
1655
bitset_intersection(self._t, self._t, self._s)
1656
u = (bitset_len(self._s) + bitset_len(self._t)) % 3
1657
return u
1658
1659
cdef void add_multiple_of_row_c(self, long x, long y, s, bint col_start):
1660
"""
1661
Add ``s`` times row ``y`` to row ``x``. Argument ``col_start`` is
1662
ignored.
1663
"""
1664
if s is None:
1665
bitset_symmetric_difference(self._s, self._M0[x], self._M1[y])
1666
bitset_symmetric_difference(self._t, self._M1[x], self._M0[y])
1667
bitset_intersection(self._u, self._s, self._t)
1668
bitset_symmetric_difference(self._s, self._s, self._M1[x])
1669
bitset_symmetric_difference(self._t, self._t, self._M1[y])
1670
bitset_union(self._M0[x], self._s, self._t)
1671
bitset_copy(self._M1[x], self._u)
1672
elif s == 1:
1673
bitset_symmetric_difference(self._s, self._M0[x], self._M1[y])
1674
bitset_symmetric_difference(self._t, self._M1[x], self._M0[y])
1675
bitset_intersection(self._u, self._s, self._t)
1676
bitset_symmetric_difference(self._s, self._s, self._M1[x])
1677
bitset_symmetric_difference(self._t, self._t, self._M1[y])
1678
bitset_union(self._M0[x], self._s, self._t)
1679
bitset_copy(self._M1[x], self._u)
1680
else: # -1, since we assume no 0-multiple ever gets added.
1681
self.row_subs(x, y)
1682
1683
cdef void row_subs(self, long x, long y): # Not a Sage matrix operation
1684
"""
1685
Subtract row ``y`` from row ``x``.
1686
"""
1687
bitset_symmetric_difference(self._s, self._M1[x], self._M1[y])
1688
bitset_symmetric_difference(self._t, self._M0[x], self._M0[y])
1689
bitset_union(self._M0[x], self._s, self._t)
1690
bitset_symmetric_difference(self._t, self._M1[y], self._t)
1691
bitset_symmetric_difference(self._s, self._M0[y], self._M1[x])
1692
bitset_intersection(self._M1[x], self._s, self._t)
1693
1694
cdef void _row_negate(self, long x):
1695
bitset_symmetric_difference(self._M1[x], self._M1[x], self._M0[x])
1696
1697
cdef void swap_rows_c(self, long x, long y):
1698
bitset_copy(self._s, self._M0[x])
1699
bitset_copy(self._M0[x], self._M0[y])
1700
bitset_copy(self._M0[y], self._s)
1701
bitset_copy(self._t, self._M1[x])
1702
bitset_copy(self._M1[x], self._M1[y])
1703
bitset_copy(self._M1[y], self._t)
1704
1705
cdef void pivot(self, long x, long y): # Not a Sage matrix operation
1706
"""
1707
Row-reduce to make column ``y`` have a ``1`` in row ``x`` and zeroes
1708
elsewhere.
1709
1710
Assumption (not checked): the entry in row ``x``, column ``y`` is
1711
nonzero to start with.
1712
1713
.. NOTE::
1714
1715
This is different from what matroid theorists tend to call a
1716
pivot, as it does not involve a column exchange!
1717
"""
1718
cdef long i, j
1719
if self._is_negative(x, y):
1720
self._row_negate(x)
1721
for i from 0 <= i < self._nrows:
1722
if self.is_nonzero(i, y) and i != x:
1723
if self._is_negative(i, y):
1724
self.add_multiple_of_row_c(i, x, 1, 0)
1725
else:
1726
self.row_subs(i, x)
1727
1728
cdef list nonzero_positions_in_row(self, long r):
1729
"""
1730
Get coordinates of nonzero entries of row ``r``.
1731
"""
1732
return bitset_list(self._M0[r])
1733
1734
cdef LeanMatrix transpose(self):
1735
"""
1736
Return the transpose of the matrix.
1737
"""
1738
cdef TernaryMatrix T
1739
cdef long i, j
1740
T = TernaryMatrix(self._ncols, self._nrows)
1741
for i from 0 <= i < self._nrows:
1742
j = bitset_first(self._M0[i])
1743
while j >= 0:
1744
bitset_add(T._M0[j], i)
1745
if bitset_in(self._M1[i], j):
1746
bitset_add(T._M1[j], i)
1747
j = bitset_next(self._M0[i], j + 1)
1748
return T
1749
1750
cdef LeanMatrix _matrix_times_matrix_(self, LeanMatrix other):
1751
"""
1752
Return the product ``self * other``.
1753
"""
1754
cdef TernaryMatrix M
1755
M = TernaryMatrix(self._nrows + 1, other.ncols())
1756
cdef long i, j
1757
for i from 0 <= i < self._nrows:
1758
j = bitset_first(self._M0[i])
1759
while j >= 0:
1760
bitset_copy(M._M0[self._nrows], (<TernaryMatrix>other)._M0[j])
1761
bitset_copy(M._M1[self._nrows], (<TernaryMatrix>other)._M1[j])
1762
if bitset_in(self._M1[i], j):
1763
M.add_multiple_of_row_c(i, self._nrows, 1, 0)
1764
else:
1765
M.row_subs(i, self._nrows)
1766
j = bitset_next(self._M0[i], j + 1)
1767
M.resize(self._nrows)
1768
return M
1769
1770
def __richcmp__(left, right, op):
1771
"""
1772
Compare two matrices.
1773
1774
EXAMPLES::
1775
1776
sage: from sage.matroids.lean_matrix import *
1777
sage: A = TernaryMatrix(2, 2, Matrix(GF(3), [[1, 0], [0, 1]]))
1778
sage: B = TernaryMatrix(2, 2, Matrix(GF(3), [[1, 0], [0, 1]]))
1779
sage: C = TernaryMatrix(2, 2, Matrix(GF(3), [[1, 1], [0, 1]]))
1780
sage: D = TernaryMatrix(2, 2, Matrix(GF(3), [[1, 1], [0, 1]]))
1781
sage: E = TernaryMatrix(2, 3, Matrix(GF(3), [[1, 0, 0], [0, 1, 0]]))
1782
sage: A == B # indirect doctest
1783
True
1784
sage: A != C # indirect doctest
1785
True
1786
sage: A == D # indirect doctest
1787
False
1788
sage: E == A
1789
False
1790
"""
1791
cdef long i, j
1792
if op in [0, 1, 4, 5]: # <, <=, >, >=
1793
return NotImplemented
1794
if not isinstance(left, TernaryMatrix) or not isinstance(right, TernaryMatrix):
1795
return NotImplemented
1796
if op == 2: # ==
1797
res = True
1798
if op == 3: # !=
1799
res = False
1800
# res gets inverted if matroids are deemed different.
1801
if left.nrows() != right.nrows():
1802
return not res
1803
if left.ncols() != right.ncols():
1804
return not res
1805
for i from 0 <= i < left.nrows():
1806
if not bitset_eq((<TernaryMatrix>left)._M0[i], (<TernaryMatrix>right)._M0[i]):
1807
return not res
1808
if not bitset_eq((<TernaryMatrix>left)._M1[i], (<TernaryMatrix>right)._M1[i]):
1809
return not res
1810
return res
1811
1812
def __reduce__(self):
1813
"""
1814
Save the object.
1815
1816
EXAMPLES::
1817
1818
sage: from sage.matroids.lean_matrix import *
1819
sage: A = TernaryMatrix(2, 5)
1820
sage: A == loads(dumps(A)) # indirect doctest
1821
True
1822
sage: C = TernaryMatrix(2, 2, Matrix(GF(3), [[1, 1], [0, 1]]))
1823
sage: C == loads(dumps(C))
1824
True
1825
"""
1826
import sage.matroids.unpickling
1827
version = 0
1828
M0 = []
1829
M1 = []
1830
versionB = 0
1831
size = 0
1832
limbs = 0
1833
longsize = 0
1834
for i from 0 <= i < self.nrows():
1835
versionB, size, limbs, longsize, data = bitset_pickle(self._M0[i])
1836
M0.append(data)
1837
versionB, size, limbs, longsize, data = bitset_pickle(self._M1[i])
1838
M1.append(data)
1839
data = (self.nrows(), self.ncols(), versionB, size, limbs, longsize, M0, M1)
1840
return sage.matroids.unpickling.unpickle_ternary_matrix, (version, data)
1841
1842
cdef class QuaternaryMatrix(LeanMatrix):
1843
"""
1844
Matrices over GF(4).
1845
1846
INPUT:
1847
1848
- ``m`` -- Number of rows
1849
- ``n`` -- Number of columns
1850
- ``M`` -- (default: ``None``) A QuaternaryMatrix or LeanMatrix or (Sage)
1851
Matrix instance. If not given, new matrix will be filled with zeroes.
1852
Assumption: ``M`` has dimensions at most ``m`` times ``n``.
1853
- ``ring`` -- (default: ``None``) A copy of GF(4). Useful for specifying
1854
generator name.
1855
1856
EXAMPLES::
1857
1858
sage: from sage.matroids.lean_matrix import *
1859
sage: A = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]]))
1860
sage: B = QuaternaryMatrix(2, 2, GenericMatrix(2, 2, ring=GF(4, 'x')))
1861
sage: C = QuaternaryMatrix(2, 2, ring=GF(4, 'x'))
1862
sage: A == B and A == C
1863
True
1864
"""
1865
def __cinit__(self, long m, long n, M=None, ring=None):
1866
"""
1867
Init internal data structures.
1868
1869
EXAMPLES::
1870
1871
sage: from sage.matroids.lean_matrix import *
1872
sage: A = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
1873
sage: A.nrows()
1874
2
1875
"""
1876
cdef long i, j
1877
self._nrows = m
1878
self._ncols = n
1879
self._M0 = <bitset_t* > sage_malloc(self._nrows * sizeof(bitset_t))
1880
self._M1 = <bitset_t* > sage_malloc(self._nrows * sizeof(bitset_t))
1881
1882
if isinstance(M, QuaternaryMatrix):
1883
j = max(1, (<QuaternaryMatrix>M)._ncols)
1884
else:
1885
j = max(1, self._ncols)
1886
1887
for i from 0 <= i < self._nrows:
1888
bitset_init(self._M0[i], j)
1889
bitset_clear(self._M0[i])
1890
bitset_init(self._M1[i], j)
1891
bitset_clear(self._M1[i])
1892
bitset_init(self._s, j)
1893
bitset_init(self._t, j)
1894
bitset_init(self._u, j)
1895
1896
def __init__(self, long m, long n, M=None, ring=None):
1897
"""
1898
See class docstring for full specification.
1899
1900
EXAMPLES::
1901
1902
sage: from sage.matroids.lean_matrix import *
1903
sage: A = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
1904
sage: A.nrows()
1905
2
1906
"""
1907
cdef long i, j
1908
if M is not None:
1909
if isinstance(M, QuaternaryMatrix):
1910
self._gf4 = (<QuaternaryMatrix>M)._gf4
1911
self._zero = self._gf4(0)
1912
self._one = self._gf4(1)
1913
self._x_zero = self._gf4.gens()[0]
1914
self._x_one = self._x_zero + self._one
1915
for i from 0 <= i < (<QuaternaryMatrix>M)._nrows:
1916
bitset_copy(self._M0[i], (<QuaternaryMatrix>M)._M0[i])
1917
bitset_copy(self._M1[i], (<QuaternaryMatrix>M)._M1[i])
1918
j = max(1, self._ncols)
1919
for i from 0 <= i < self._nrows:
1920
bitset_realloc(self._M0[i], j)
1921
bitset_realloc(self._M1[i], j)
1922
elif isinstance(M, LeanMatrix):
1923
self._gf4 = (<LeanMatrix>M).base_ring()
1924
self._zero = self._gf4(0)
1925
self._one = self._gf4(1)
1926
self._x_zero = self._gf4.gens()[0]
1927
self._x_one = self._x_zero + self._one
1928
for i from 0 <= i < M.nrows():
1929
for j from 0 <= j < M.ncols():
1930
self.set(i, j, (<LeanMatrix>M).get_unsafe(i, j))
1931
elif isinstance(M, Matrix):
1932
self._gf4 = (<Matrix>M).base_ring()
1933
self._zero = self._gf4(0)
1934
self._one = self._gf4(1)
1935
self._x_zero = self._gf4.gens()[0]
1936
self._x_one = self._x_zero + self._one
1937
for i from 0 <= i < M.nrows():
1938
for j from 0 <= j < M.ncols():
1939
self.set(i, j, (<Matrix>M).get_unsafe(i, j))
1940
else:
1941
raise TypeError("unrecognized input type.")
1942
else:
1943
self._gf4 = ring
1944
self._zero = self._gf4(0)
1945
self._one = self._gf4(1)
1946
self._x_zero = self._gf4.gens()[0]
1947
self._x_one = self._x_zero + self._one
1948
1949
def __dealloc__(self):
1950
"""
1951
Free internal data structures.
1952
1953
EXAMPLES::
1954
1955
sage: from sage.matroids.lean_matrix import *
1956
sage: A = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[0, 0], [0, 0]])) # Indirect doctest
1957
sage: A.nrows()
1958
2
1959
sage: A = None
1960
"""
1961
cdef long i
1962
for i from 0 <= i < self._nrows:
1963
bitset_free(self._M0[i])
1964
bitset_free(self._M1[i])
1965
sage_free(self._M0)
1966
sage_free(self._M1)
1967
bitset_free(self._s)
1968
bitset_free(self._t)
1969
bitset_free(self._u)
1970
1971
def __repr__(self):
1972
"""
1973
Print representation string
1974
1975
EXAMPLES::
1976
1977
sage: from sage.matroids.lean_matrix import *
1978
sage: A = QuaternaryMatrix(2, 3, Matrix(GF(4, 'x'), [[0, 0], [0, 0]]))
1979
sage: repr(A) # indirect doctest
1980
'2 x 3 QuaternaryMatrix\n[000]\n[000]'
1981
"""
1982
out = str(self._nrows) + ' x ' + str(self._ncols) + ' QuaternaryMatrix'
1983
cdef long i
1984
if self._ncols > 0:
1985
for i from 0 <= i < self._nrows:
1986
out += '\n['
1987
for j from 0 <= j < self._ncols:
1988
x = self.get(i, j)
1989
if x == self._zero:
1990
out += '0'
1991
if x == self._one:
1992
out += '1'
1993
if x == self._x_zero:
1994
out += 'x'
1995
if x == self._x_one:
1996
out += 'y'
1997
out += ']'
1998
else:
1999
for i from 0 <= i < self._nrows:
2000
out += '[]'
2001
return out
2002
2003
def _matrix_(self):
2004
"""
2005
Return Sage Matrix version of ``self``.
2006
2007
EXAMPLES::
2008
2009
sage: from sage.matroids.lean_matrix import *
2010
sage: A = QuaternaryMatrix(2, 3, Matrix(GF(4, 'x'), [[0, 0], [0, 0]]))
2011
sage: A._matrix_()
2012
[0 0 0]
2013
[0 0 0]
2014
"""
2015
M = sage.matrix.constructor.Matrix(self._gf4, self._nrows, self._ncols)
2016
for i from 0 <= i < self._nrows:
2017
for j from 0 <= j < self._ncols:
2018
M[i, j] = self.get(i, j)
2019
return M
2020
2021
cdef inline get(self, long r, long c): # Not a Sage matrix operation
2022
if bitset_in(self._M0[r], c):
2023
if bitset_in(self._M1[r], c):
2024
return self._x_one
2025
else:
2026
return self._one
2027
else:
2028
if bitset_in(self._M1[r], c):
2029
return self._x_zero
2030
else:
2031
return self._zero
2032
2033
cdef inline void set(self, long r, long c, x): # Not a Sage matrix operation
2034
if x == self._zero:
2035
bitset_discard(self._M0[r], c)
2036
bitset_discard(self._M1[r], c)
2037
if x == self._one:
2038
bitset_add(self._M0[r], c)
2039
bitset_discard(self._M1[r], c)
2040
if x == self._x_zero:
2041
bitset_discard(self._M0[r], c)
2042
bitset_add(self._M1[r], c)
2043
if x == self._x_one:
2044
bitset_add(self._M0[r], c)
2045
bitset_add(self._M1[r], c)
2046
2047
cdef get_unsafe(self, long r, long c):
2048
return self.get(r, c)
2049
2050
cdef void set_unsafe(self, long r, long c, x):
2051
self.set(r, c, x)
2052
2053
cdef bint is_nonzero(self, long r, long c): # Not a Sage matrix operation
2054
return bitset_in(self._M0[r], c) or bitset_in(self._M1[r], c)
2055
2056
cdef LeanMatrix copy(self): # Deprecated Sage matrix operation
2057
cdef QuaternaryMatrix T
2058
cdef long i
2059
T = QuaternaryMatrix(self._nrows, self._ncols, ring=self._gf4)
2060
for i from 0 <= i < self._nrows:
2061
bitset_copy(T._M0[i], self._M0[i])
2062
bitset_copy(T._M1[i], self._M1[i])
2063
return T
2064
2065
cdef void resize(self, long k): # Not a Sage matrix operation
2066
"""
2067
Change number of rows to ``k``. Preserves data.
2068
"""
2069
if k < self._nrows:
2070
for i from k <= i < self._nrows:
2071
bitset_free(self._M0[i])
2072
bitset_free(self._M1[i])
2073
self._nrows = k
2074
self._M0 = <bitset_t* > sage_realloc(self._M0, k * sizeof(bitset_t))
2075
self._M1 = <bitset_t* > sage_realloc(self._M1, k * sizeof(bitset_t))
2076
if k > self._nrows:
2077
self._M0 = <bitset_t* > sage_realloc(self._M0, k * sizeof(bitset_t))
2078
self._M1 = <bitset_t* > sage_realloc(self._M1, k * sizeof(bitset_t))
2079
c = max(1, self._ncols)
2080
for i from self._nrows <= i < k:
2081
bitset_init(self._M0[i], c)
2082
bitset_clear(self._M0[i])
2083
bitset_init(self._M1[i], c)
2084
bitset_clear(self._M1[i])
2085
self._nrows = k
2086
2087
cdef LeanMatrix stack(self, LeanMatrix MM):
2088
cdef QuaternaryMatrix R
2089
cdef QuaternaryMatrix M = <QuaternaryMatrix > MM
2090
cdef long i
2091
R = QuaternaryMatrix(self.nrows() + M.nrows(), self.ncols(), self)
2092
for i from 0 <= i < self._nrows:
2093
bitset_copy(R._M0[i + self.nrows()], M._M0[i])
2094
bitset_copy(R._M1[i + self.nrows()], M._M1[i])
2095
return R
2096
2097
cdef LeanMatrix augment(self, LeanMatrix MM):
2098
cdef QuaternaryMatrix R
2099
cdef QuaternaryMatrix M = <QuaternaryMatrix > MM
2100
cdef long i, j
2101
R = QuaternaryMatrix(self.nrows(), self.ncols() + M.ncols(), self)
2102
for i from 0 <= i < R.nrows():
2103
for j from 0 <= j < M.ncols():
2104
bitset_set_to(R._M0[i], self.ncols() + j, bitset_in(M._M0[i], j))
2105
bitset_set_to(R._M1[i], self.ncols() + j, bitset_in(M._M1[i], j))
2106
return R
2107
2108
cdef LeanMatrix prepend_identity(self): # Not a Sage matrix operation
2109
"""
2110
Return the matrix obtained by prepending an identity matrix. Special
2111
case of ``augment``.
2112
"""
2113
cdef long i, j
2114
cdef QuaternaryMatrix A = QuaternaryMatrix(self._nrows, self._ncols + self._nrows, ring=self._gf4)
2115
for i from 0 <= i < self._nrows:
2116
A.set(i, i, 1)
2117
for j from 0 <= j < self._ncols:
2118
A.set(i, self._nrows + j, self.get(i, j))
2119
return A
2120
2121
cpdef base_ring(self):
2122
"""
2123
Return copy of `GF(4)` with appropriate generator.
2124
2125
EXAMPLES::
2126
2127
sage: from sage.matroids.lean_matrix import *
2128
sage: A = QuaternaryMatrix(2, 2, ring=GF(4, 'f'))
2129
sage: A.base_ring()
2130
Finite Field in f of size 2^2
2131
"""
2132
return self._gf4
2133
2134
cpdef characteristic(self):
2135
"""
2136
Return the characteristic of ``self.base_ring()``.
2137
2138
EXAMPLES::
2139
2140
sage: from sage.matroids.lean_matrix import *
2141
sage: A = QuaternaryMatrix(200, 5000, ring=GF(4, 'x'))
2142
sage: A.characteristic()
2143
2
2144
"""
2145
return 2
2146
2147
cdef inline long row_len(self, long i): # Not a Sage matrix operation
2148
"""
2149
Return number of nonzero entries in row ``i``.
2150
"""
2151
bitset_union(self._t, self._M0[i], self._M1[i])
2152
return bitset_len(self._t)
2153
2154
cdef inline row_inner_product(self, long i, long j): # Not a Sage matrix operation
2155
"""
2156
Return the inner product between rows ``i`` and ``j``.
2157
"""
2158
cdef bint a, b
2159
bitset_intersection(self._t, self._M0[i], self._M0[j])
2160
bitset_intersection(self._u, self._M0[i], self._M1[j])
2161
bitset_symmetric_difference(self._t, self._t, self._u)
2162
bitset_intersection(self._s, self._M1[i], self._M0[j])
2163
bitset_symmetric_difference(self._u, self._u, self._s)
2164
bitset_intersection(self._s, self._M1[i], self._M1[j])
2165
bitset_symmetric_difference(self._t, self._t, self._s)
2166
a = bitset_len(self._t) % 2
2167
b = bitset_len(self._u) % 2
2168
if a:
2169
if b:
2170
return self._x_one
2171
else:
2172
return self._one
2173
else:
2174
if b:
2175
return self._x_zero
2176
else:
2177
return self._zero
2178
2179
cdef void add_multiple_of_row_c(self, long x, long y, s, bint col_start):
2180
"""
2181
Add ``s`` times row ``y`` to row ``x``. Argument ``col_start`` is
2182
ignored.
2183
"""
2184
if s == self._zero:
2185
return
2186
if s == self._one or s is None:
2187
bitset_symmetric_difference(self._M0[x], self._M0[x], self._M0[y])
2188
bitset_symmetric_difference(self._M1[x], self._M1[x], self._M1[y])
2189
return
2190
if s == self._x_zero:
2191
bitset_symmetric_difference(self._M0[x], self._M0[x], self._M1[y])
2192
bitset_symmetric_difference(self._M1[x], self._M1[x], self._M0[y])
2193
bitset_symmetric_difference(self._M1[x], self._M1[x], self._M1[y])
2194
return
2195
if s == self._x_one:
2196
bitset_symmetric_difference(self._M0[x], self._M0[x], self._M0[y])
2197
bitset_symmetric_difference(self._M0[x], self._M0[x], self._M1[y])
2198
bitset_symmetric_difference(self._M1[x], self._M1[x], self._M0[y])
2199
return
2200
2201
cdef void swap_rows_c(self, long x, long y):
2202
bitset_copy(self._s, self._M0[x])
2203
bitset_copy(self._M0[x], self._M0[y])
2204
bitset_copy(self._M0[y], self._s)
2205
bitset_copy(self._t, self._M1[x])
2206
bitset_copy(self._M1[x], self._M1[y])
2207
bitset_copy(self._M1[y], self._t)
2208
2209
cdef inline void _row_div(self, long x, object s):
2210
"""
2211
Divide all entries in row ``x`` by ``s``.
2212
"""
2213
if s == self._one:
2214
return
2215
if s == self._x_zero:
2216
bitset_symmetric_difference(self._M0[x], self._M0[x], self._M1[x])
2217
bitset_symmetric_difference(self._M1[x], self._M0[x], self._M1[x])
2218
return
2219
if s == self._x_one:
2220
bitset_symmetric_difference(self._M1[x], self._M0[x], self._M1[x])
2221
bitset_symmetric_difference(self._M0[x], self._M0[x], self._M1[x])
2222
return
2223
2224
cdef void pivot(self, long x, long y): # Not a Sage matrix operation
2225
"""
2226
Row-reduce to make column ``y`` have a ``1`` in row ``x`` and zeroes
2227
elsewhere.
2228
2229
Assumption (not checked): the entry in row ``x``, column ``y`` is
2230
nonzero to start with.
2231
2232
.. NOTE::
2233
2234
This is different from what matroid theorists tend to call a
2235
pivot, as it does not involve a column exchange!
2236
"""
2237
cdef long i, j
2238
self._row_div(x, self.get(x, y))
2239
for i from 0 <= i < self._nrows:
2240
if self.is_nonzero(i, y) and i != x:
2241
self.add_multiple_of_row_c(i, x, self.get(i, y), 0)
2242
2243
cdef list nonzero_positions_in_row(self, long r):
2244
"""
2245
Get coordinates of nonzero entries of row ``r``.
2246
"""
2247
bitset_union(self._t, self._M0[r], self._M1[r])
2248
return bitset_list(self._t)
2249
2250
cdef LeanMatrix transpose(self):
2251
"""
2252
Return the transpose of the matrix.
2253
"""
2254
cdef QuaternaryMatrix T
2255
cdef long i, j
2256
T = QuaternaryMatrix(self._ncols, self._nrows, ring=self._gf4)
2257
for i from 0 <= i < self._ncols:
2258
for j from 0 <= j < self._nrows:
2259
T.set(i, j, self.get(j, i))
2260
return T
2261
2262
cdef void conjugate(self): # Not a Sage matrix operation
2263
"""
2264
Apply the nontrivial GF(4)-automorphism to the entries.
2265
"""
2266
cdef long i
2267
for i from 0 <= i < self._nrows:
2268
bitset_symmetric_difference(self._M0[i], self._M0[i], self._M1[i])
2269
2270
cdef LeanMatrix _matrix_times_matrix_(self, LeanMatrix other):
2271
"""
2272
Return the product ``self * other``.
2273
"""
2274
cdef QuaternaryMatrix M, ot
2275
ot = <QuaternaryMatrix > other
2276
M = QuaternaryMatrix(self._nrows + 1, ot._ncols, ring=self._gf4)
2277
cdef long i, j
2278
for i from 0 <= i < self._nrows:
2279
for j from 0 <= j < self._ncols:
2280
bitset_copy(M._M0[self._nrows], ot._M0[j])
2281
bitset_copy(M._M1[self._nrows], ot._M1[j])
2282
M.add_multiple_of_row_c(i, self._nrows, self.get(i, j), 0)
2283
M.resize(self._nrows)
2284
return M
2285
2286
def __neg__(self):
2287
"""
2288
Negate the matrix.
2289
2290
In characteristic 2, this does nothing.
2291
2292
EXAMPLES::
2293
2294
sage: from sage.matroids.lean_matrix import *
2295
sage: A = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[1, 0], [0, 1]]))
2296
sage: B = -A # indirect doctest
2297
sage: B == A
2298
True
2299
"""
2300
return self.copy()
2301
2302
def __richcmp__(left, right, op):
2303
"""
2304
Compare two matrices.
2305
2306
EXAMPLES::
2307
2308
sage: from sage.matroids.lean_matrix import *
2309
sage: A = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[1, 0], [0, 1]]))
2310
sage: B = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[1, 0], [0, 1]]))
2311
sage: C = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[1, 1], [0, 1]]))
2312
sage: D = QuaternaryMatrix(2, 2, Matrix(GF(4, 'y'), [[1, 0], [0, 1]]))
2313
sage: E = QuaternaryMatrix(2, 3, Matrix(GF(4, 'x'), [[1, 0, 0], [0, 1, 0]]))
2314
sage: A == B # indirect doctest
2315
True
2316
sage: A != C # indirect doctest
2317
True
2318
sage: A == D # indirect doctest
2319
False
2320
sage: E == A
2321
False
2322
"""
2323
cdef long i, j
2324
if op in [0, 1, 4, 5]: # <, <=, >, >=
2325
return NotImplemented
2326
if not isinstance(left, QuaternaryMatrix) or not isinstance(right, QuaternaryMatrix):
2327
return NotImplemented
2328
if op == 2: # ==
2329
res = True
2330
if op == 3: # !=
2331
res = False
2332
if left.base_ring() != right.base_ring():
2333
return not res
2334
# res gets inverted if matroids are deemed different.
2335
if left.nrows() != right.nrows():
2336
return not res
2337
if left.ncols() != right.ncols():
2338
return not res
2339
for i from 0 <= i < left.nrows():
2340
if not bitset_eq((<QuaternaryMatrix>left)._M0[i], (<QuaternaryMatrix>right)._M0[i]):
2341
return not res
2342
if not bitset_eq((<QuaternaryMatrix>left)._M1[i], (<QuaternaryMatrix>right)._M1[i]):
2343
return not res
2344
return res
2345
2346
def __reduce__(self):
2347
"""
2348
Save the object.
2349
2350
EXAMPLES::
2351
2352
sage: from sage.matroids.lean_matrix import *
2353
sage: A = QuaternaryMatrix(2, 5, ring=GF(4, 'x'))
2354
sage: A == loads(dumps(A)) # indirect doctest
2355
True
2356
sage: C = QuaternaryMatrix(2, 2, Matrix(GF(4, 'x'), [[1, 1], [0, 1]]))
2357
sage: C == loads(dumps(C))
2358
True
2359
"""
2360
import sage.matroids.unpickling
2361
version = 0
2362
M0 = []
2363
M1 = []
2364
versionB = 0
2365
size = 0
2366
limbs = 0
2367
longsize = 0
2368
ring = self._gf4
2369
for i from 0 <= i < self.nrows():
2370
versionB, size, limbs, longsize, data = bitset_pickle(self._M0[i])
2371
M0.append(data)
2372
versionB, size, limbs, longsize, data = bitset_pickle(self._M1[i])
2373
M1.append(data)
2374
data = (self.nrows(), self.ncols(), ring, versionB, size, limbs, longsize, M0, M1)
2375
return sage.matroids.unpickling.unpickle_quaternary_matrix, (version, data)
2376
2377
cpdef GenericMatrix generic_identity(n, ring):
2378
"""
2379
Return a GenericMatrix instance containing the `n \times n` identity
2380
matrix over ``ring``.
2381
2382
EXAMPLES::
2383
2384
sage: from sage.matroids.lean_matrix import *
2385
sage: A = generic_identity(2, QQ)
2386
sage: Matrix(A)
2387
[1 0]
2388
[0 1]
2389
"""
2390
cdef long i
2391
cdef GenericMatrix A = GenericMatrix(n, n, ring=ring)
2392
for i from 0 <= i < n:
2393
A.set_unsafe(i, i, A._one)
2394
return A
2395
2396
# Integer matrices
2397
2398
cdef class IntegerMatrix(LeanMatrix):
2399
"""
2400
Matrix over the integers.
2401
2402
INPUT:
2403
2404
- ``nrows`` -- number of rows
2405
- ``ncols`` -- number of columns
2406
- ``M`` -- (default: ``None``) a ``Matrix`` or ``GenericMatrix`` of
2407
dimensions at most ``m*n``.
2408
2409
.. NOTE::
2410
2411
This class is intended for internal use by the LinearMatroid class
2412
only. Hence it does not derive from ``SageObject``.
2413
If ``A`` is a LeanMatrix instance, and you need access from other
2414
parts of Sage, use ``Matrix(A)`` instead.
2415
2416
This class is mainly intended for use with the RegularMatroid class,
2417
so entries are assumed to be small integers. No
2418
overflow checking takes place!
2419
2420
EXAMPLES::
2421
2422
sage: M = Matroid(graphs.CompleteGraph(4).incidence_matrix(),
2423
....: regular=True) # indirect doctest
2424
sage: M.is_isomorphic(matroids.Wheel(3))
2425
True
2426
"""
2427
def __cinit__(self, long nrows, long ncols, M=None, ring=None):
2428
"""
2429
Init internal data structures.
2430
2431
EXAMPLES::
2432
2433
sage: from sage.matroids.lean_matrix import *
2434
sage: A = IntegerMatrix(2, 2, Matrix(GF(4, 'x'),
2435
....: [[0, 0], [0, 0]])) # Indirect doctest
2436
sage: A.nrows()
2437
2
2438
"""
2439
cdef long i, j
2440
self._nrows = nrows
2441
self._ncols = ncols
2442
self._entries = <int* > sage_malloc(nrows * ncols * sizeof(int))
2443
memset(self._entries, 0, nrows * ncols * sizeof(int))
2444
2445
def __init__(self, long nrows, long ncols, M=None, ring=None):
2446
"""
2447
See class docstring for full information.
2448
2449
EXAMPLES::
2450
2451
sage: from sage.matroids.lean_matrix import *
2452
sage: A = IntegerMatrix(2, 2, Matrix(GF(3),
2453
....: [[0, 0], [0, 0]])) # indirect doctest
2454
sage: B = IntegerMatrix(2, 2)
2455
sage: A == B
2456
True
2457
"""
2458
cdef long i, j
2459
if M is not None:
2460
if isinstance(M, IntegerMatrix):
2461
for i from 0 <= i < M.nrows():
2462
memcpy(self._entries + i * self._ncols, (<IntegerMatrix>M)._entries + i * (<IntegerMatrix>M)._ncols, (<IntegerMatrix>M)._ncols * sizeof(int))
2463
elif isinstance(M, LeanMatrix):
2464
for i from 0 <= i < M.nrows():
2465
for j from 0 <= j < M.ncols():
2466
self._entries[i * self._ncols + j] = int((<LeanMatrix>M).get_unsafe(i, j))
2467
else: # Sage Matrix or otherwise
2468
for i from 0 <= i < M.nrows():
2469
for j from 0 <= j < M.ncols():
2470
self._entries[i * self._ncols + j] = int(M[i, j])
2471
2472
def __dealloc__(self):
2473
"""
2474
Free internal data structures.
2475
2476
EXAMPLES::
2477
2478
sage: from sage.matroids.lean_matrix import *
2479
sage: A = IntegerMatrix(2, 2, Matrix(GF(4, 'x'),
2480
....: [[0, 0], [0, 0]])) # Indirect doctest
2481
sage: A.nrows()
2482
2
2483
sage: A = None
2484
"""
2485
sage_free(self._entries)
2486
2487
def __repr__(self):
2488
"""
2489
Print representation.
2490
2491
EXAMPLES::
2492
2493
sage: from sage.matroids.lean_matrix import *
2494
sage: A = IntegerMatrix(2, 2, Matrix(GF(3), [[0, 0], [0, 0]]))
2495
sage: repr(A) # indirect doctest
2496
'IntegerMatrix instance with 2 rows and 2 columns'
2497
"""
2498
return "IntegerMatrix instance with " + str(self._nrows) + " rows and " + str(self._ncols) + " columns"
2499
2500
cdef inline get(self, long r, long c): # Not a Sage matrix operation
2501
return self._entries[r * self._ncols + c]
2502
2503
cdef inline void set(self, long r, long c, int x): # Not a Sage matrix operation
2504
self._entries[r * self._ncols + c] = x
2505
2506
cdef get_unsafe(self, long r, long c):
2507
"""
2508
Return a Sage Integer, for safety down the line when dividing.
2509
2510
EXAMPLE:
2511
2512
By returning an Integer rather than an int, the following test no
2513
longer fails::
2514
2515
sage: from sage.matroids.advanced import *
2516
sage: M = RegularMatroid(matrix([
2517
....: (1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0),
2518
....: (0, 1, 0, 0, -1, 1, 0, 0, 0, 0, 0),
2519
....: (0, 0, 1, 0, 0, -1, 1, 0, 0, 1, 0),
2520
....: (0, 0, 0, 1, 0, 0, -1, 1, 0, 0, 0),
2521
....: (0, 0, 0, 0, 0, 0, -1, 0, 1, -1, 0),
2522
....: (0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 1)]))
2523
sage: all(N.is_valid() for N in M.linear_extensions(F=[4, 10]))
2524
True
2525
"""
2526
return Integer(self.get(r, c))
2527
2528
cdef void set_unsafe(self, long r, long c, x):
2529
self.set(r, c, x)
2530
2531
cdef bint is_nonzero(self, long r, long c): # Not a Sage matrix operation
2532
return self.get(r, c)
2533
2534
cdef LeanMatrix copy(self): # Deprecated Sage matrix operation
2535
cdef IntegerMatrix M = IntegerMatrix(self._nrows, self._ncols)
2536
memcpy(M._entries, self._entries, self._nrows * self._ncols * sizeof(int))
2537
return M
2538
2539
cdef void resize(self, long k): # Not a Sage matrix operation
2540
"""
2541
Change number of rows to ``k``. Preserves data.
2542
"""
2543
cdef long l = self._ncols * (self._nrows - k)
2544
if l > 0:
2545
sage_realloc(self._entries, self._ncols * k * sizeof(int))
2546
memset(self._entries + self._nrows * self._ncols, 0, l * self._ncols * sizeof(int))
2547
elif l < 0:
2548
sage_realloc(self._entries, self._ncols * k * sizeof(int))
2549
self._nrows = k
2550
2551
cdef LeanMatrix stack(self, LeanMatrix M):
2552
"""
2553
Warning: assumes ``M`` is an IntegerMatrix instance of right
2554
dimensions!
2555
"""
2556
cdef IntegerMatrix A
2557
A = IntegerMatrix(self._nrows + M.nrows(), self._ncols)
2558
memcpy(A._entries, self._entries, self._nrows * self._ncols * sizeof(int))
2559
memcpy(A._entries + self._nrows * self._ncols, (<IntegerMatrix>M)._entries, M.nrows() * M.ncols() * sizeof(int))
2560
return A
2561
2562
cdef LeanMatrix augment(self, LeanMatrix M):
2563
"""
2564
Warning: assumes ``M`` is a GenericMatrix instance!
2565
"""
2566
cdef IntegerMatrix A
2567
cdef long i
2568
cdef long Mn = M.ncols()
2569
A = IntegerMatrix(self._nrows, self._ncols + Mn)
2570
for i from 0 <= i < self._nrows:
2571
memcpy(A._entries + i * A._ncols, self._entries + i * self._ncols, self._ncols * sizeof(int))
2572
memcpy(A._entries + (i * A._ncols + self._ncols), (<IntegerMatrix>M)._entries + i * Mn, Mn * sizeof(int))
2573
return A
2574
2575
cdef LeanMatrix prepend_identity(self): # Not a Sage matrix operation
2576
cdef IntegerMatrix A = IntegerMatrix(self._nrows, self._ncols + self._nrows, ring=self._base_ring)
2577
cdef long i
2578
for i from 0 <= i < self._nrows:
2579
A._entries[i * A._ncols + i] = 1
2580
memcpy(A._entries + (i * A._ncols + self._nrows), self._entries + i * self._ncols, self._ncols * sizeof(int))
2581
return A
2582
2583
cpdef base_ring(self):
2584
"""
2585
Return the base ring of ``self``.
2586
2587
EXAMPLES::
2588
2589
sage: from sage.matroids.lean_matrix import *
2590
sage: A = IntegerMatrix(3, 4)
2591
sage: A.base_ring()
2592
Integer Ring
2593
"""
2594
return ZZ
2595
2596
cpdef characteristic(self):
2597
"""
2598
Return the characteristic of ``self.base_ring()``.
2599
2600
EXAMPLES::
2601
2602
sage: from sage.matroids.lean_matrix import *
2603
sage: A = GenericMatrix(3, 4)
2604
sage: A.characteristic()
2605
0
2606
"""
2607
return 0
2608
2609
cdef inline long row_len(self, long i): # Not a Sage matrix operation
2610
"""
2611
Return number of nonzero entries in row ``i``.
2612
"""
2613
cdef long k
2614
cdef long res = 0
2615
for k from 0 <= k < self._ncols:
2616
if self.get(i, k):
2617
res += 1
2618
return res
2619
2620
cdef inline row_inner_product(self, long i, long j): # Not a Sage matrix operation
2621
"""
2622
Return the inner product between rows ``i`` and ``j``.
2623
"""
2624
cdef long k
2625
cdef int res = 0
2626
for k from 0 <= k < self._ncols:
2627
res += self.get(i, k) * self.get(j, k)
2628
return res
2629
2630
cdef void add_multiple_of_row_c(self, long x, long y, s, bint col_start):
2631
"""
2632
Add ``s`` times row ``y`` to row ``x``. Argument ``col_start`` is
2633
ignored.
2634
"""
2635
cdef long i
2636
if s is None:
2637
for i from 0 <= i < self._ncols:
2638
self.set(x, i, self.get(x, i) + self.get(y, i))
2639
else:
2640
for i from 0 <= i < self._ncols:
2641
self.set(x, i, self.get(x, i) + s * self.get(y, i))
2642
2643
cdef void swap_rows_c(self, long x, long y):
2644
"""
2645
Swap rows ``x`` and ``y``.
2646
"""
2647
cdef int* tmp
2648
tmp = <int* > sage_malloc(self._ncols * sizeof(int))
2649
memcpy(tmp, self._entries + x * self._ncols, self._ncols * sizeof(int))
2650
memcpy(self._entries + x * self._ncols, self._entries + y * self._ncols, self._ncols * sizeof(int))
2651
memcpy(self._entries + y * self._ncols, tmp, self._ncols * sizeof(int))
2652
sage_free(tmp)
2653
2654
cdef void rescale_row_c(self, long x, s, bint col_start):
2655
"""
2656
Scale row ``x`` by ``s``. Argument ``col_start`` is for Sage
2657
compatibility, and is ignored.
2658
"""
2659
cdef long i
2660
# print "row-scale: ", x, ", ", s
2661
for i from 0 <= i < self._ncols:
2662
self.set(x, i, s * self.get(x, i))
2663
2664
cdef void rescale_column_c(self, long y, s, bint start_row):
2665
"""
2666
Scale column ``y`` by ``s``. Argument ``start_row`` is for Sage
2667
compatibility, and is ignored.
2668
"""
2669
cdef long j
2670
for j from 0 <= j < self._nrows:
2671
self.set(j, y, self.get(j, y) * s)
2672
2673
cdef void pivot(self, long x, long y): # Not a Sage matrix operation
2674
"""
2675
Row-reduce to make column ``y`` have a ``1`` in row ``x`` and zeroes
2676
elsewhere.
2677
2678
Assumption (not checked): the entry in row ``x``, column ``y`` is
2679
invertible (so 1 or -1) to start with.
2680
2681
.. NOTE::
2682
2683
This is different from what matroid theorists tend to call a
2684
pivot, as it does not involve a column exchange!
2685
"""
2686
cdef long i, j
2687
cdef int a, s
2688
a = self.get(x, y) # 1 or -1, so inverse is equal to itself
2689
self.rescale_row_c(x, a, 0)
2690
for i from 0 <= i < self._nrows:
2691
s = self.get(i, y)
2692
if s and i != x:
2693
self.add_multiple_of_row_c(i, x, -s, 0)
2694
2695
cdef list nonzero_positions_in_row(self, long r):
2696
"""
2697
Get coordinates of nonzero entries of row ``r``.
2698
"""
2699
cdef long j
2700
cdef list res = []
2701
for j from r * self._ncols <= j < (r + 1) * self._ncols:
2702
if self._entries[j]:
2703
res.append(j - r * self._ncols)
2704
return res
2705
2706
cdef LeanMatrix transpose(self):
2707
"""
2708
Return the transpose of the matrix.
2709
"""
2710
cdef IntegerMatrix A
2711
cdef long i, j
2712
A = IntegerMatrix(self._ncols, self._nrows)
2713
for i from 0 <= i < self._nrows:
2714
for j from 0 <= j < self._ncols:
2715
A.set(j, i, self.get(i, j))
2716
return A
2717
2718
cdef LeanMatrix _matrix_times_matrix_(self, LeanMatrix other):
2719
"""
2720
Return the product ``self * other``.
2721
"""
2722
cdef IntegerMatrix A, ot
2723
cdef long i, j, t
2724
ot = <IntegerMatrix > other
2725
A = IntegerMatrix(self._nrows, ot._ncols)
2726
for i from 0 <= i < A._nrows:
2727
for j from 0 <= j < A._ncols:
2728
s = 0
2729
for t from 0 <= t < self._ncols:
2730
s += self.get(i, t) * ot.get(t, j)
2731
A.set(i, j, s)
2732
return A
2733
2734
cdef list gauss_jordan_reduce(self, columns): # Not a Sage matrix operation
2735
"""
2736
Row-reduce so the lexicographically first basis indexes an identity
2737
submatrix.
2738
"""
2739
cdef long r = 0
2740
cdef list P = []
2741
cdef long a, c, p, row
2742
cdef bint is_pivot
2743
for c in columns:
2744
is_pivot = False
2745
for row from r <= row < self._nrows:
2746
a = self.get(row, c)
2747
if a:
2748
if a < -1 or a > 1:
2749
raise ValueError("not a totally unimodular matrix")
2750
is_pivot = True
2751
p = row
2752
break
2753
if is_pivot:
2754
self.swap_rows_c(p, r)
2755
self.rescale_row_c(r, self.get(r, c), 0) # Inverting not needed for integers -1, 1
2756
for row from 0 <= row < self._nrows:
2757
if row != r and self.is_nonzero(row, c):
2758
self.add_multiple_of_row_c(row, r, -self.get(row, c), 0)
2759
P.append(c)
2760
r += 1
2761
if r == self._nrows:
2762
break
2763
return P
2764
2765
def __richcmp__(left, right, op):
2766
"""
2767
Compare two matrices.
2768
2769
EXAMPLES::
2770
2771
sage: from sage.matroids.lean_matrix import *
2772
sage: A = IntegerMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
2773
sage: B = IntegerMatrix(2, 2, Matrix(GF(2), [[1, 0], [0, 1]]))
2774
sage: C = IntegerMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
2775
sage: D = IntegerMatrix(2, 2, Matrix(GF(2), [[1, 1], [0, 1]]))
2776
sage: E = IntegerMatrix(2, 3, Matrix(GF(2), [[1, 0, 0], [0, 1, 0]]))
2777
sage: A == B # indirect doctest
2778
True
2779
sage: A != C # indirect doctest
2780
True
2781
sage: A == D # indirect doctest
2782
False
2783
sage: E == A
2784
False
2785
"""
2786
cdef long i, j
2787
if op in [0, 1, 4, 5]: # <, <=, >, >=
2788
return NotImplemented
2789
if not isinstance(left, IntegerMatrix) or not isinstance(right, IntegerMatrix):
2790
return NotImplemented
2791
if op == 2: # ==
2792
res = True
2793
if op == 3: # !=
2794
res = False
2795
# res gets inverted if matroids are deemed different.
2796
if left.nrows() != right.nrows():
2797
return not res
2798
if left.ncols() != right.ncols():
2799
return not res
2800
for i from 0 <= i < left.nrows() * left.ncols():
2801
if (<IntegerMatrix>left)._entries[i] != (<IntegerMatrix>right)._entries[i]:
2802
return not res
2803
return res
2804
2805
def __reduce__(self):
2806
"""
2807
Save the object.
2808
2809
EXAMPLES::
2810
2811
sage: from sage.matroids.lean_matrix import *
2812
sage: A = IntegerMatrix(2, 5)
2813
sage: A == loads(dumps(A)) # indirect doctest
2814
True
2815
sage: C = IntegerMatrix(2, 2, Matrix(GF(3), [[1, 1], [0, 1]]))
2816
sage: C == loads(dumps(C))
2817
True
2818
"""
2819
import sage.matroids.unpickling
2820
cdef list entries = []
2821
cdef long i
2822
for i from 0 <= i < self._nrows * self._ncols:
2823
entries.append(self._entries[i])
2824
version = 0
2825
data = (self.nrows(), self.ncols(), entries)
2826
return sage.matroids.unpickling.unpickle_integer_matrix, (version, data)
2827
2828