Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matrix/matrix_mod2_dense.pyx
8815 views
1
"""
2
Dense matrices over GF(2) using the M4RI library.
3
4
AUTHOR: Martin Albrecht <[email protected]>
5
6
EXAMPLES::
7
8
sage: a = matrix(GF(2),3,range(9),sparse=False); a
9
[0 1 0]
10
[1 0 1]
11
[0 1 0]
12
sage: a.rank()
13
2
14
sage: type(a)
15
<type 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>
16
sage: a[0,0] = 1
17
sage: a.rank()
18
3
19
sage: parent(a)
20
Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 2
21
22
sage: a^2
23
[0 1 1]
24
[1 0 0]
25
[1 0 1]
26
sage: a+a
27
[0 0 0]
28
[0 0 0]
29
[0 0 0]
30
31
sage: b = a.new_matrix(2,3,range(6)); b
32
[0 1 0]
33
[1 0 1]
34
35
sage: a*b
36
Traceback (most recent call last):
37
...
38
TypeError: unsupported operand parent(s) for '*': 'Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 2' and 'Full MatrixSpace of 2 by 3 dense matrices over Finite Field of size 2'
39
sage: b*a
40
[1 0 1]
41
[1 0 0]
42
43
sage: TestSuite(a).run()
44
sage: TestSuite(b).run()
45
46
sage: a.echelonize(); a
47
[1 0 0]
48
[0 1 0]
49
[0 0 1]
50
sage: b.echelonize(); b
51
[1 0 1]
52
[0 1 0]
53
54
TESTS:
55
sage: FF = FiniteField(2)
56
sage: V = VectorSpace(FF,2)
57
sage: v = V([0,1]); v
58
(0, 1)
59
sage: W = V.subspace([v])
60
sage: W
61
Vector space of degree 2 and dimension 1 over Finite Field of size 2
62
Basis matrix:
63
[0 1]
64
sage: v in W
65
True
66
67
sage: M = Matrix(GF(2), [[1,1,0],[0,1,0]])
68
sage: M.row_space()
69
Vector space of degree 3 and dimension 2 over Finite Field of size 2
70
Basis matrix:
71
[1 0 0]
72
[0 1 0]
73
74
sage: M = Matrix(GF(2), [[1,1,0],[0,0,1]])
75
sage: M.row_space()
76
Vector space of degree 3 and dimension 2 over Finite Field of size 2
77
Basis matrix:
78
[1 1 0]
79
[0 0 1]
80
81
TODO:
82
- make LinBox frontend and use it
83
- charpoly ?
84
- minpoly ?
85
- make Matrix_modn_frontend and use it (?)
86
"""
87
88
##############################################################################
89
# Copyright (C) 2004,2005,2006 William Stein <[email protected]>
90
# Copyright (C) 2007,2008,2009 Martin Albrecht <[email protected]>
91
# Distributed under the terms of the GNU General Public License (GPL)
92
# The full text of the GPL is available at:
93
# http://www.gnu.org/licenses/
94
##############################################################################
95
96
include "sage/ext/interrupt.pxi"
97
include "sage/ext/cdefs.pxi"
98
include 'sage/ext/stdsage.pxi'
99
include 'sage/ext/random.pxi'
100
101
cimport matrix_dense
102
from sage.structure.element cimport Matrix, Vector
103
from sage.structure.element cimport ModuleElement, Element
104
105
from sage.misc.functional import log
106
107
from sage.misc.misc import verbose, get_verbose, cputime
108
109
from sage.modules.free_module import VectorSpace
110
from sage.modules.vector_mod2_dense cimport Vector_mod2_dense
111
112
cdef extern from "gd.h":
113
ctypedef struct gdImagePtr "gdImagePtr":
114
pass
115
116
gdImagePtr gdImageCreateFromPng(FILE *f)
117
gdImagePtr gdImageCreateFromPngPtr(int size, void *s)
118
gdImagePtr gdImageCreate(int x, int y)
119
void gdImagePng(gdImagePtr im, FILE *out)
120
void *gdImagePngPtr(gdImagePtr im, int *size)
121
void gdImageDestroy(gdImagePtr im)
122
int gdImageSX(gdImagePtr im)
123
int gdImageSY(gdImagePtr im)
124
int gdImageGetPixel(gdImagePtr im, int x, int y)
125
void gdImageSetPixel(gdImagePtr im, int x, int y, int value)
126
int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)
127
void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
128
void gdFree(void *m)
129
130
## from sage.libs.linbox.linbox cimport Linbox_mod2_dense
131
## cdef Linbox_mod2_dense linbox
132
## linbox = Linbox_mod2_dense()
133
134
cdef object called
135
136
cdef void init_m4ri():
137
global called
138
if called is None:
139
m4ri_build_all_codes()
140
called = True
141
142
init_m4ri()
143
144
def free_m4ri():
145
"""
146
Free global Gray code tables.
147
"""
148
m4ri_destroy_all_codes()
149
150
151
152
cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
153
"""
154
Dense matrix over GF(2).
155
"""
156
########################################################################
157
# LEVEL 1 functionality
158
########################################################################
159
def __cinit__(self, parent, entries, copy, coerce, alloc=True):
160
"""
161
Dense matrix over GF(2) constructor.
162
163
INPUT:
164
165
- ``parent`` - MatrixSpace.
166
- ``entries`` - may be list or 0 or 1
167
- ``copy`` - ignored, elements are always copied
168
- ``coerce`` - ignored, elements are always coerced to ints % 2
169
170
EXAMPLES::
171
172
sage: type(random_matrix(GF(2),2,2))
173
<type 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>
174
175
sage: Matrix(GF(2),3,3,1) # indirect doctest
176
[1 0 0]
177
[0 1 0]
178
[0 0 1]
179
180
See trac #10858:
181
182
sage: matrix(GF(2),0,[]) * vector(GF(2),0,[])
183
()
184
"""
185
matrix_dense.Matrix_dense.__init__(self, parent)
186
187
if alloc:
188
self._entries = mzd_init(self._nrows, self._ncols)
189
190
# cache elements
191
self._zero = self._base_ring(0)
192
self._one = self._base_ring(1)
193
194
def __dealloc__(self):
195
if self._entries:
196
mzd_free(self._entries)
197
self._entries = NULL
198
199
def __init__(self, parent, entries, copy, coerce):
200
"""
201
Dense matrix over GF(2) constructor.
202
203
INPUT:
204
205
- ``parent`` - MatrixSpace.
206
- ``entries`` - may be list or 0 or 1
207
- ``copy`` - ignored, elements are always copied
208
- ``coerce`` - ignored, elements are always coerced to ints % 2
209
210
EXAMPLES::
211
212
sage: type(random_matrix(GF(2),2,2))
213
<type 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>
214
215
sage: Matrix(GF(2),3,3,1)
216
[1 0 0]
217
[0 1 0]
218
[0 0 1]
219
220
sage: Matrix(GF(2),2,2,[1,1,1,0])
221
[1 1]
222
[1 0]
223
224
sage: Matrix(GF(2),2,2,4)
225
[0 0]
226
[0 0]
227
228
sage: Matrix(GF(2),1,1, 1/3)
229
[1]
230
sage: Matrix(GF(2),1,1, [1/3])
231
[1]
232
233
TESTS::
234
235
sage: Matrix(GF(2),0,0)
236
[]
237
sage: Matrix(GF(2),2,0)
238
[]
239
sage: Matrix(GF(2),0,2)
240
[]
241
"""
242
cdef int i,j,e
243
244
if entries is None:
245
return
246
247
R = self.base_ring()
248
249
# scalar ?
250
if not isinstance(entries, list):
251
if self._nrows and self._ncols and R(entries) == 1:
252
mzd_set_ui(self._entries, 1)
253
return
254
255
# all entries are given as a long list
256
if len(entries) != self._nrows * self._ncols:
257
raise IndexError("The vector of entries has the wrong length.")
258
259
k = 0
260
261
for i from 0 <= i < self._nrows:
262
sig_check()
263
for j from 0 <= j < self._ncols:
264
mzd_write_bit(self._entries,i,j, R(entries[k]))
265
k = k + 1
266
267
def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.
268
"""
269
Compares ``self`` with ``right``. While equality and
270
inequality are clearly defined, ``<`` and ``>`` are not. For
271
those first the matrix dimensions of ``self`` and ``right``
272
are compared. If these match then ``<`` means that there is a
273
position smallest (i,j) in ``self`` where ``self[i,j]`` is
274
zero but ``right[i,j]`` is one. This (i,j) is smaller than the
275
(i,j) if ``self`` and ``right`` are exchanged for the
276
comparison.
277
278
INPUT:
279
280
- ``right`` - a matrix
281
- ``op`` - comparison operation
282
283
EXAMPLE::
284
285
sage: A = random_matrix(GF(2),2,2)
286
sage: B = random_matrix(GF(2),3,3)
287
sage: A < B
288
True
289
sage: A = MatrixSpace(GF(2),3,3).one()
290
sage: B = copy(MatrixSpace(GF(2),3,3).one())
291
sage: B[0,1] = 1
292
sage: A < B
293
True
294
295
TESTS::
296
297
sage: A = matrix(GF(2),2,0)
298
sage: B = matrix(GF(2),2,0)
299
sage: A < B
300
False
301
"""
302
return self._richcmp(right, op)
303
304
def __hash__(self):
305
r"""
306
The has of a matrix is computed as `\oplus i*a_i` where the
307
`a_i` are the flattened entries in a matrix (by row, then by
308
column).
309
310
EXAMPLE::
311
312
sage: B = random_matrix(GF(2),3,3)
313
sage: B.set_immutable()
314
sage: {B:0} # indirect doctest
315
{[0 1 0]
316
[0 1 1]
317
[0 0 0]: 0}
318
sage: M = random_matrix(GF(2), 123, 321)
319
sage: M.set_immutable()
320
sage: MZ = M.change_ring(ZZ)
321
sage: MZ.set_immutable()
322
sage: hash(M) == hash(MZ)
323
True
324
sage: MS = M.sparse_matrix()
325
sage: MS.set_immutable()
326
sage: hash(M) == hash(MS)
327
True
328
329
TEST:
330
sage: A = matrix(GF(2),2,0)
331
sage: hash(A)
332
Traceback (most recent call last):
333
...
334
TypeError: mutable matrices are unhashable
335
sage: A.set_immutable()
336
sage: hash(A)
337
0
338
339
"""
340
if not self._is_immutable:
341
raise TypeError("mutable matrices are unhashable")
342
343
x = self.fetch('hash')
344
if not x is None:
345
return x
346
347
if self._nrows == 0 or self._ncols == 0:
348
return 0
349
350
cdef unsigned long i, j, truerow
351
cdef unsigned long start, shift
352
cdef m4ri_word row_xor
353
cdef m4ri_word end_mask = __M4RI_LEFT_BITMASK(self._ncols%m4ri_radix)
354
cdef m4ri_word top_mask, bot_mask
355
cdef m4ri_word cur
356
cdef m4ri_word* row
357
358
# running_xor is the xor of all words in the matrix, as if the rows
359
# in the matrix were written out consecutively, without regard to
360
# word boundaries.
361
cdef m4ri_word running_xor = 0
362
# running_parity is the number of extra words that must be xor'd.
363
cdef unsigned long running_parity = 0
364
365
366
for i from 0 <= i < self._entries.nrows:
367
368
# All this shifting and masking is because the
369
# rows are word-aligned.
370
row = self._entries.rows[i]
371
start = (i*self._entries.ncols) >> 6
372
shift = (i*self._entries.ncols) & 0x3F
373
bot_mask = __M4RI_LEFT_BITMASK(m4ri_radix - shift)
374
top_mask = ~bot_mask
375
376
if self._entries.width > 1:
377
row_xor = row[0]
378
running_parity ^= start & parity_mask(row[0] & bot_mask)
379
380
for j from 1 <= j < self._entries.width - 1:
381
row_xor ^= row[j]
382
cur = ((row[j-1] >> (63-shift)) >> 1) ^ (row[j] << shift)
383
running_parity ^= (start+j) & parity_mask(cur)
384
385
running_parity ^= (start+j) & parity_mask(row[j-1] & top_mask)
386
387
else:
388
j = 0
389
row_xor = 0
390
391
cur = row[j] & end_mask
392
row_xor ^= cur
393
running_parity ^= (start+j) & parity_mask(cur & bot_mask)
394
running_parity ^= (start+j+1) & parity_mask(cur & top_mask)
395
396
running_xor ^= (row_xor << shift) ^ ((row_xor >> (63-shift)) >> 1)
397
398
cdef unsigned long bit_is_set
399
cdef unsigned long h
400
401
# Now we assemble the running_parity and running_xor to get the hash.
402
# Viewing the flattened matrix as a list of a_i, the hash is the xor
403
# of the i for which a_i is non-zero. We split i into the lower m4ri_radix
404
# bits and the rest, so i = i1 << m4ri_radix + i0. Now two matching i0
405
# would cancel, so we only need the parity of how many of each
406
# possible i0 occur. This is stored in the bits of running_xor.
407
# Similarly, running_parity is the xor of the i1 needed. It's called
408
# parity because i1 is constant across a word, and for each word
409
# the number of i1 to add is equal to the number of set bits in that
410
# word (but because two cancel, we only need keep track of the
411
# parity.
412
413
h = m4ri_radix * running_parity
414
for i from 0 <= i < m4ri_radix:
415
bit_is_set = (running_xor >> i) & 1
416
h ^= (m4ri_radix-1) & ~(bit_is_set-1) & i
417
418
if h == -1:
419
h = -2
420
421
self.cache('hash', h)
422
return h
423
424
# this exists for compatibility with matrix_modn_dense
425
cdef set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value):
426
"""
427
Set the (i,j) entry of self to the int value.
428
"""
429
mzd_write_bit(self._entries, i, j, int(value))
430
431
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
432
mzd_write_bit(self._entries, i, j, int(value))
433
434
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
435
if mzd_read_bit(self._entries, i, j):
436
return self._one
437
else:
438
return self._zero
439
440
441
def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None):
442
"""
443
Return a nice string representation of the matrix.
444
445
INPUT:
446
447
- ``rep_mapping`` - a dictionary or callable used to override
448
the usual representation of elements. For a dictionary,
449
keys should be elements of the base ring and values the
450
desired string representation.
451
452
- ``zero`` - string (default: ``None``); if not ``None`` use
453
the value of ``zero`` as the representation of the zero
454
element.
455
456
- ``plus_one`` - string (default: ``None``); if not ``None``
457
use the value of ``plus_one`` as the representation of the
458
one element.
459
460
- ``minus_one`` - Ignored. Only for compatibility with
461
generic matrices.
462
463
EXAMPLE::
464
465
sage: B = random_matrix(GF(2),3,3)
466
sage: B # indirect doctest
467
[0 1 0]
468
[0 1 1]
469
[0 0 0]
470
sage: block_matrix([[B, 1], [0, B]])
471
[0 1 0|1 0 0]
472
[0 1 1|0 1 0]
473
[0 0 0|0 0 1]
474
[-----+-----]
475
[0 0 0|0 1 0]
476
[0 0 0|0 1 1]
477
[0 0 0|0 0 0]
478
sage: B.str(zero='.')
479
'[. 1 .]\n[. 1 1]\n[. . .]'
480
"""
481
if self._nrows ==0 or self._ncols == 0:
482
return "[]"
483
484
cdef int i,j, last_i
485
cdef list s = []
486
empty_row = " "*(self._ncols*2-1)
487
cdef char *row_s
488
cdef char *div_s
489
490
# Set the mapping based on keyword arguments
491
# We ignore minus_one (it's only there for compatibility with Matrix)
492
if rep_mapping is not None or zero is not None or plus_one is not None:
493
# Shunt mappings off to the generic code since they might not be single characters
494
return matrix_dense.Matrix_dense.str(self, rep_mapping=rep_mapping, zero=zero, plus_one=plus_one)
495
496
cdef list row_div, col_div
497
if self._subdivisions is not None:
498
row_s = empty_row
499
div_s = row_divider = b"[%s]" % ("-" * (self._ncols*2-1))
500
row_div, col_div = self.subdivisions()
501
last_i = 0
502
for i in col_div:
503
if i == last_i or i == self._ncols:
504
# Adjacent column divisions messy, use generic code
505
return matrix_dense.Matrix_dense.str(self, rep_mapping)
506
row_s[2*i-1] = '|'
507
div_s[2*i] = '+'
508
last_i = i
509
510
for i from 0 <= i < self._nrows:
511
row_s = row = b"[%s]" % empty_row
512
for j from 0 <= j < self._ncols:
513
row_s[1+2*j] = c'0' + mzd_read_bit(self._entries,i,j)
514
s.append(row)
515
516
if self._subdivisions is not None:
517
for i in reversed(row_div):
518
s.insert(i, row_divider)
519
520
return "\n".join(s)
521
522
def row(self, Py_ssize_t i, from_list=False):
523
"""
524
Return the ``i``'th row of this matrix as a vector.
525
526
This row is a dense vector if and only if the matrix is a dense
527
matrix.
528
529
INPUT:
530
531
- ``i`` - integer
532
533
- ``from_list`` - bool (default: ``False``); if ``True``,
534
returns the ``i``'th element of ``self.rows()`` (see
535
:func:`rows`), which may be faster, but requires building a
536
list of all rows the first time it is called after an entry
537
of the matrix is changed.
538
539
EXAMPLES::
540
541
sage: A = random_matrix(GF(2),10,10); A
542
[0 1 0 1 1 0 0 0 1 1]
543
[0 1 1 1 0 1 1 0 0 1]
544
[0 0 0 1 0 1 0 0 1 0]
545
[0 1 1 0 0 1 0 1 1 0]
546
[0 0 0 1 1 1 1 0 1 1]
547
[0 0 1 1 1 1 0 0 0 0]
548
[1 1 1 1 0 1 0 1 1 0]
549
[0 0 0 1 1 0 0 0 1 1]
550
[1 0 0 0 1 1 1 0 1 1]
551
[1 0 0 1 1 0 1 0 0 0]
552
553
sage: A.row(0)
554
(0, 1, 0, 1, 1, 0, 0, 0, 1, 1)
555
556
sage: A.row(-1)
557
(1, 0, 0, 1, 1, 0, 1, 0, 0, 0)
558
559
sage: A.row(2,from_list=True)
560
(0, 0, 0, 1, 0, 1, 0, 0, 1, 0)
561
562
sage: A = Matrix(GF(2),1,0)
563
sage: A.row(0)
564
()
565
"""
566
if self._nrows == 0:
567
raise IndexError("matrix has no rows")
568
if i >= self._nrows or i < -self._nrows:
569
raise IndexError("row index out of range")
570
if i < 0:
571
i = i + self._nrows
572
if from_list:
573
return self.rows(copy=False)[i]
574
cdef Py_ssize_t j
575
cdef Vector_mod2_dense z = PY_NEW(Vector_mod2_dense)
576
z._init(self._ncols, VectorSpace(self.base_ring(),self._ncols))
577
if self._ncols:
578
mzd_submatrix(z._entries, self._entries, i, 0, i+1, self._ncols)
579
return z
580
581
########################################################################
582
# LEVEL 2 functionality
583
# * def _pickle
584
# * def _unpickle
585
# * cdef _mul_
586
# * cdef _cmp_c_impl
587
# * _list -- list of underlying elements (need not be a copy)
588
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
589
########################################################################
590
# def _pickle(self):
591
# def _unpickle(self, data, int version): # use version >= 0
592
593
cpdef ModuleElement _add_(self, ModuleElement right):
594
"""
595
Matrix addition.
596
597
INPUT:
598
right -- matrix of dimension self.nrows() x self.ncols()
599
600
EXAMPLES::
601
602
sage: A = random_matrix(GF(2),10,10)
603
sage: A + A == Matrix(GF(2),10,10,0)
604
True
605
606
sage: A = random_matrix(GF(2),257,253)
607
sage: A + A == Matrix(GF(2),257,253,0) # indirect doctest
608
True
609
610
TESTS::
611
612
sage: A = matrix(GF(2),2,0)
613
sage: A+A
614
[]
615
sage: A = matrix(GF(2),0,2)
616
sage: A+A
617
[]
618
sage: A = matrix(GF(2),0,0)
619
sage: A+A
620
[]
621
"""
622
cdef Matrix_mod2_dense A
623
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc=False)
624
if self._nrows == 0 or self._ncols == 0:
625
return A
626
A._entries = mzd_add(NULL, self._entries,(<Matrix_mod2_dense>right)._entries)
627
628
return A
629
630
cpdef ModuleElement _sub_(self, ModuleElement right):
631
"""
632
Matrix addition.
633
634
INPUT:
635
right -- matrix of dimension self.nrows() x self.ncols()
636
637
EXAMPLES::
638
639
sage: A = random_matrix(GF(2),10,10)
640
sage: A - A == Matrix(GF(2),10,10,0) # indirect doctest
641
True
642
"""
643
return self._add_(right)
644
645
cdef Vector _matrix_times_vector_(self, Vector v):
646
"""
647
EXAMPLES::
648
649
sage: A = random_matrix(GF(2),10^4,10^4)
650
sage: v0 = random_matrix(GF(2),10^4,1)
651
sage: v1 = v0.column(0)
652
sage: r0 = A*v0
653
sage: r1 = A*v1
654
sage: r0.column(0) == r1
655
True
656
"""
657
cdef mzd_t *tmp
658
if not PY_TYPE_CHECK(v, Vector_mod2_dense):
659
M = VectorSpace(self._base_ring, self._nrows)
660
v = M(v)
661
if self.ncols() != v.degree():
662
raise ArithmeticError("number of columns of matrix must equal degree of vector")
663
664
VS = VectorSpace(self._base_ring, self._nrows)
665
cdef Vector_mod2_dense c = PY_NEW(Vector_mod2_dense)
666
c._init(self._nrows, VS)
667
c._entries = mzd_init(1, self._nrows)
668
if c._entries.nrows and c._entries.ncols:
669
tmp = mzd_init(self._nrows, 1)
670
_mzd_mul_naive(tmp, self._entries, (<Vector_mod2_dense>v)._entries, 0)
671
mzd_transpose(c._entries, tmp)
672
mzd_free(tmp)
673
return c
674
675
cdef Matrix _matrix_times_matrix_(self, Matrix right):
676
"""
677
Matrix multiplication.
678
679
ALGORITHM: Uses the 'Method of the Four Russians
680
Multiplication', see :func:`_multiply_m4rm`.
681
"""
682
if get_verbose() >= 2:
683
verbose('matrix multiply of %s x %s matrix by %s x %s matrix'%(
684
self._nrows, self._ncols, right._nrows, right._ncols))
685
686
return self._multiply_strassen(right, 0)
687
688
cpdef Matrix_mod2_dense _multiply_m4rm(Matrix_mod2_dense self, Matrix_mod2_dense right, int k):
689
"""
690
Multiply matrices using the 'Method of the Four Russians
691
Multiplication' (M4RM) or Konrod's method.
692
693
The algorithm is based on an algorithm by Arlazarov, Dinic,
694
Kronrod, and Faradzev [ADKF70] and appeared in [AHU]. This
695
implementation is based on a description given in Gregory
696
Bard's 'Method of the Four Russians Inversion' paper [B06].
697
698
INPUT:
699
right -- Matrix
700
k -- parameter $k$ for the Gray Code table size. If $k=0$ a
701
suitable value is chosen by the function.
702
($0<= k <= 16$, default: 0)
703
704
EXAMPLE::
705
706
sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )
707
sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )
708
sage: A
709
[0 0 0]
710
[0 1 0]
711
[0 1 1]
712
[0 0 1]
713
sage: B
714
[0 0 1 0]
715
[1 0 0 1]
716
[1 1 0 0]
717
sage: A._multiply_m4rm(B, 0)
718
[0 0 0 0]
719
[1 0 0 1]
720
[0 1 0 1]
721
[1 1 0 0]
722
723
TESTS::
724
725
sage: A = random_matrix(GF(2),0,0)
726
sage: B = random_matrix(GF(2),0,0)
727
sage: A._multiply_m4rm(B, 0)
728
[]
729
sage: A = random_matrix(GF(2),3,0)
730
sage: B = random_matrix(GF(2),0,3)
731
sage: A._multiply_m4rm(B, 0)
732
[0 0 0]
733
[0 0 0]
734
[0 0 0]
735
sage: A = random_matrix(GF(2),0,3)
736
sage: B = random_matrix(GF(2),3,0)
737
sage: A._multiply_m4rm(B, 0)
738
[]
739
740
ALGORITHM: Uses the 'Method of the Four Russians'
741
multiplication as implemented in the M4RI library.
742
743
REFERENCES:
744
[AHU] A. Aho, J. Hopcroft, and J. Ullman. 'Chapter 6:
745
Matrix Multiplication and Related Operations.'
746
The Design and Analysis of Computer
747
Algorithms. Addison-Wesley, 1974.
748
749
[ADKF70] V. Arlazarov, E. Dinic, M. Kronrod, and
750
I. Faradzev. 'On Economical Construction of the
751
Transitive Closure of a Directed Graph.'
752
Dokl. Akad. Nauk. SSSR No. 194 (in Russian),
753
English Translation in Soviet Math Dokl. No. 11,
754
1970.
755
756
[Bard06] G. Bard. 'Accelerating Cryptanalysis with the
757
Method of Four Russians'. Cryptography E-Print
758
Archive (http://eprint.iacr.org/2006/251.pdf),
759
2006.
760
"""
761
if self._ncols != right._nrows:
762
raise ArithmeticError("left ncols must match right nrows")
763
764
if get_verbose() >= 2:
765
verbose('m4rm multiply of %s x %s matrix by %s x %s matrix'%(
766
self._nrows, self._ncols, right._nrows, right._ncols))
767
768
cdef Matrix_mod2_dense ans
769
770
ans = self.new_matrix(nrows = self._nrows, ncols = right._ncols)
771
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
772
return ans
773
sig_on()
774
ans._entries = mzd_mul_m4rm(ans._entries, self._entries, right._entries, k)
775
sig_off()
776
return ans
777
778
def _multiply_classical(Matrix_mod2_dense self, Matrix_mod2_dense right):
779
"""
780
Classical `O(n^3)` multiplication.
781
782
This can be quite fast for matrix vector multiplication but
783
the other routines fall back to this implementation in that
784
case anyway.
785
786
EXAMPLE::
787
788
sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )
789
sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )
790
sage: A
791
[0 0 0]
792
[0 1 0]
793
[0 1 1]
794
[0 0 1]
795
sage: B
796
[0 0 1 0]
797
[1 0 0 1]
798
[1 1 0 0]
799
sage: A._multiply_classical(B)
800
[0 0 0 0]
801
[1 0 0 1]
802
[0 1 0 1]
803
[1 1 0 0]
804
805
TESTS:
806
sage: A = random_matrix(GF(2),0,0)
807
sage: B = random_matrix(GF(2),0,0)
808
sage: A._multiply_classical(B)
809
[]
810
sage: A = random_matrix(GF(2),3,0)
811
sage: B = random_matrix(GF(2),0,3)
812
sage: A._multiply_classical(B)
813
[0 0 0]
814
[0 0 0]
815
[0 0 0]
816
sage: A = random_matrix(GF(2),0,3)
817
sage: B = random_matrix(GF(2),3,0)
818
sage: A._multiply_classical(B)
819
[]
820
"""
821
cdef Matrix_mod2_dense A
822
A = self.new_matrix(nrows = self._nrows, ncols = right._ncols)
823
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
824
return A
825
A._entries = mzd_mul_naive(A._entries, self._entries,(<Matrix_mod2_dense>right)._entries)
826
return A
827
828
cpdef Matrix_mod2_dense _multiply_strassen(Matrix_mod2_dense self, Matrix_mod2_dense right, int cutoff):
829
r"""
830
Strassen-Winograd `O(n^{2.807})` multiplication [Str69].
831
832
This implementation in M4RI is inspired by Sage's generic
833
Strassen implementation [BHS08] but uses a more memory
834
efficient operation schedule [DP08].
835
836
The performance of this routine depends on the parameter
837
cutoff. On many modern machines 2048 should give acceptable
838
performance, a good rule of thumb for calculating the optimal
839
cutoff would that two matrices of the cutoff size should fit
840
in L2 cache, so: `cutoff = \sqrt{L2 * 8 * 1024^2 / 2}` where
841
`L2` is the size of the L2 cache in MB.
842
843
INPUT:
844
845
- ``right`` - a matrix of matching dimensions.
846
- ``cutoff`` - matrix dimension where M4RM should be used
847
instead of Strassen (default: let M4RI decide)
848
849
EXAMPLE::
850
851
sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )
852
sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )
853
sage: A
854
[0 0 0]
855
[0 1 0]
856
[0 1 1]
857
[0 0 1]
858
sage: B
859
[0 0 1 0]
860
[1 0 0 1]
861
[1 1 0 0]
862
sage: A._multiply_strassen(B, 0)
863
[0 0 0 0]
864
[1 0 0 1]
865
[0 1 0 1]
866
[1 1 0 0]
867
sage: A = random_matrix(GF(2),2701,3000)
868
sage: B = random_matrix(GF(2),3000,3172)
869
sage: A._multiply_strassen(B, 256) == A._multiply_m4rm(B, 0)
870
True
871
872
TESTS:
873
sage: A = random_matrix(GF(2),0,0)
874
sage: B = random_matrix(GF(2),0,0)
875
sage: A._multiply_strassen(B, 0)
876
[]
877
sage: A = random_matrix(GF(2),3,0)
878
sage: B = random_matrix(GF(2),0,3)
879
sage: A._multiply_strassen(B, 0)
880
[0 0 0]
881
[0 0 0]
882
[0 0 0]
883
sage: A = random_matrix(GF(2),0,3)
884
sage: B = random_matrix(GF(2),3,0)
885
sage: A._multiply_strassen(B, 0)
886
[]
887
888
ALGORITHM: Uses Strassen-Winograd matrix multiplication with
889
M4RM as base case as implemented in the M4RI library.
890
891
REFERENCES:
892
[Str69] Volker Strassen. Gaussian elimination is not
893
optimal. Numerische Mathematik, 13:354-356, 1969.
894
895
[BHS08] Robert Bradshaw, David Harvey and William
896
Stein. strassen_window_multiply_c. strassen.pyx,
897
Sage 3.0, 2008. http://www.sagemath.org
898
899
[DP08] Jean-Guillaume Dumas and Clement Pernet. Memory
900
efficient scheduling of Strassen-Winograd's matrix
901
multiplication algorithm. arXiv:0707.2347v1, 2008.
902
"""
903
if self._ncols != right._nrows:
904
raise ArithmeticError("left ncols must match right nrows")
905
906
cdef Matrix_mod2_dense ans
907
#ans = self.new_matrix(nrows = self._nrows, ncols = right._ncols)
908
# The following is a little faster:
909
ans = self.matrix_space(self._nrows, right._ncols, sparse=False).zero_matrix().__copy__()
910
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
911
return ans
912
913
sig_on()
914
ans._entries = mzd_mul(ans._entries, self._entries, right._entries, cutoff)
915
sig_off()
916
return ans
917
918
def __neg__(self):
919
"""
920
EXAMPLES::
921
922
sage: A = random_matrix(GF(2),100,100)
923
sage: A - A == A - -A
924
True
925
"""
926
return self.__copy__()
927
928
def __invert__(self):
929
"""
930
Inverts self using the 'Method of the Four Russians'
931
inversion.
932
933
If ``self`` is not invertible a ``ZeroDivisionError`` is
934
raised.
935
936
EXAMPLE::
937
938
sage: A = Matrix(GF(2),3,3, [0, 0, 1, 0, 1, 1, 1, 0, 1])
939
sage: MS = A.parent()
940
sage: A
941
[0 0 1]
942
[0 1 1]
943
[1 0 1]
944
sage: ~A
945
[1 0 1]
946
[1 1 0]
947
[1 0 0]
948
sage: A * ~A == ~A * A == MS(1)
949
True
950
951
TESTS:
952
sage: A = matrix(GF(2),0,0)
953
sage: A^(-1)
954
[]
955
"""
956
cdef int k = 0
957
cdef mzd_t *I
958
cdef Matrix_mod2_dense A
959
960
if self._nrows != self._ncols:
961
raise ArithmeticError("self must be a square matrix")
962
963
if self._ncols == 0:
964
return self.__copy__()
965
966
if self.rank() != self._nrows:
967
raise ZeroDivisionError("Matrix does not have full rank.")
968
969
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc = False)
970
sig_on()
971
A._entries = mzd_inv_m4ri(NULL, self._entries, 0)
972
sig_off()
973
974
if A._entries==NULL:
975
raise ZeroDivisionError("input matrix must be nonsingular")
976
else:
977
return A
978
979
def __copy__(self):
980
"""
981
Returns a copy of ``self``.
982
983
EXAMPLES::
984
985
sage: MS = MatrixSpace(GF(2),3,3)
986
sage: A = MS(1)
987
sage: A.__copy__() == A
988
True
989
sage: A.__copy__() is A
990
False
991
992
sage: A = random_matrix(GF(2),100,100)
993
sage: A.__copy__() == A
994
True
995
sage: A.__copy__() is A
996
False
997
998
sage: A.echelonize()
999
sage: A.__copy__() == A
1000
True
1001
1002
"""
1003
cdef Matrix_mod2_dense A
1004
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0)
1005
1006
if self._nrows and self._ncols:
1007
mzd_copy(A._entries, self._entries)
1008
1009
if self._subdivisions is not None:
1010
A.subdivide(*self.subdivisions())
1011
1012
return A
1013
1014
def _list(self):
1015
"""
1016
Returns list of the elements of ``self`` in row major
1017
ordering.
1018
1019
EXAMPLE::
1020
1021
sage: A = Matrix(GF(2),2,2,[1,0,1,1])
1022
sage: A
1023
[1 0]
1024
[1 1]
1025
sage: A.list() #indirect doctest
1026
[1, 0, 1, 1]
1027
1028
TESTS:
1029
sage: A = Matrix(GF(2),3,0)
1030
sage: A.list()
1031
[]
1032
"""
1033
cdef int i,j
1034
l = []
1035
for i from 0 <= i < self._nrows:
1036
for j from 0 <= j < self._ncols:
1037
if mzd_read_bit(self._entries,i,j):
1038
l.append(self._one)
1039
else:
1040
l.append(self._zero)
1041
return l
1042
1043
# def _dict(self):
1044
1045
########################################################################
1046
# LEVEL 3 functionality (Optional)
1047
# * __deepcopy__
1048
# * Matrix windows -- only if you need strassen for that base
1049
########################################################################
1050
1051
def echelonize(self, algorithm='heuristic', cutoff=0, reduced=True, **kwds):
1052
"""
1053
Puts self in (reduced) row echelon form.
1054
1055
INPUT:
1056
self -- a mutable matrix
1057
algorithm -- 'heuristic' -- uses M4RI and PLUQ (default)
1058
'm4ri' -- uses M4RI
1059
'pluq' -- uses PLUQ factorization
1060
'classical' -- uses classical Gaussian elimination
1061
k -- the parameter 'k' of the M4RI algorithm. It MUST be between
1062
1 and 16 (inclusive). If it is not specified it will be calculated as
1063
3/4 * log_2( min(nrows, ncols) ) as suggested in the M4RI paper.
1064
reduced -- return reduced row echelon form (default:True)
1065
1066
EXAMPLE::
1067
1068
sage: A = random_matrix(GF(2), 10, 10)
1069
sage: B = A.__copy__(); B.echelonize() # fastest
1070
sage: C = A.__copy__(); C.echelonize(k=2) # force k
1071
sage: E = A.__copy__(); E.echelonize(algorithm='classical') # force Gaussian elimination
1072
sage: B == C == E
1073
True
1074
1075
TESTS::
1076
1077
sage: VF2 = VectorSpace(GF(2),2)
1078
sage: WF2 = VF2.submodule([VF2([1,1])])
1079
sage: WF2
1080
Vector space of degree 2 and dimension 1 over Finite Field of size 2
1081
Basis matrix:
1082
[1 1]
1083
1084
sage: A2 = matrix(GF(2),2,[1,0,0,1])
1085
sage: A2.kernel()
1086
Vector space of degree 2 and dimension 0 over Finite Field of size 2
1087
Basis matrix:
1088
[]
1089
1090
ALGORITHM: Uses M4RI library
1091
1092
REFERENCES:
1093
[Bard06] G. Bard. 'Accelerating Cryptanalysis with the Method of Four Russians'. Cryptography
1094
E-Print Archive (http://eprint.iacr.org/2006/251.pdf), 2006.
1095
"""
1096
if self._nrows == 0 or self._ncols == 0:
1097
self.cache('in_echelon_form',True)
1098
self.cache('rank', 0)
1099
self.cache('pivots', ())
1100
return self
1101
cdef int k, n, full
1102
1103
full = int(reduced)
1104
1105
x = self.fetch('in_echelon_form')
1106
if not x is None: return # already known to be in echelon form
1107
1108
if algorithm == 'heuristic':
1109
1110
self.check_mutability()
1111
self.clear_cache()
1112
1113
sig_on()
1114
r = mzd_echelonize(self._entries, full)
1115
sig_off()
1116
1117
self.cache('in_echelon_form',True)
1118
self.cache('rank', r)
1119
self.cache('pivots', tuple(self._pivots()))
1120
1121
elif algorithm == 'm4ri':
1122
1123
self.check_mutability()
1124
self.clear_cache()
1125
1126
if 'k' in kwds:
1127
k = int(kwds['k'])
1128
1129
if k<1 or k>16:
1130
raise RuntimeError("k must be between 1 and 16")
1131
k = round(k)
1132
else:
1133
k = 0
1134
1135
sig_on()
1136
r = mzd_echelonize_m4ri(self._entries, full, k)
1137
sig_off()
1138
1139
self.cache('in_echelon_form',True)
1140
self.cache('rank', r)
1141
self.cache('pivots', tuple(self._pivots()))
1142
1143
1144
elif algorithm == 'pluq':
1145
1146
self.check_mutability()
1147
self.clear_cache()
1148
1149
sig_on()
1150
r = mzd_echelonize_pluq(self._entries, full)
1151
sig_off()
1152
1153
self.cache('in_echelon_form',True)
1154
self.cache('rank', r)
1155
self.cache('pivots', tuple(self._pivots()))
1156
1157
elif algorithm == 'linbox':
1158
1159
#self._echelonize_linbox()
1160
raise NotImplementedError
1161
1162
elif algorithm == 'classical':
1163
1164
# for debugging purposes only, it is slow
1165
self._echelon_in_place_classical()
1166
else:
1167
raise ValueError("no algorithm '%s'"%algorithm)
1168
1169
def _pivots(self):
1170
"""
1171
Returns the pivot columns of ``self`` if ``self`` is in
1172
row echelon form.
1173
1174
EXAMPLE::
1175
1176
sage: A = matrix(GF(2),5,5,[0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,1])
1177
sage: E = A.echelon_form()
1178
sage: E
1179
[0 1 0 0 0]
1180
[0 0 1 0 0]
1181
[0 0 0 1 0]
1182
[0 0 0 0 1]
1183
[0 0 0 0 0]
1184
sage: E._pivots()
1185
[1, 2, 3, 4]
1186
"""
1187
if not self.fetch('in_echelon_form'):
1188
raise RuntimeError("self must be in reduced row echelon form first.")
1189
pivots = []
1190
cdef Py_ssize_t i, j, nc
1191
nc = self._ncols
1192
i = 0
1193
while i < self._nrows:
1194
for j from i <= j < nc:
1195
if mzd_read_bit(self._entries, i, j):
1196
pivots.append(j)
1197
i += 1
1198
break
1199
if j == nc:
1200
break
1201
return pivots
1202
1203
def randomize(self, density=1, nonzero=False):
1204
"""
1205
Randomize ``density`` proportion of the entries of this matrix,
1206
leaving the rest unchanged.
1207
1208
INPUT:
1209
1210
- ``density`` - float; proportion (roughly) to be considered for
1211
changes
1212
- ``nonzero`` - Bool (default: ``False``); whether the new entries
1213
are forced to be non-zero
1214
1215
OUTPUT:
1216
1217
- None, the matrix is modified in-space
1218
1219
EXAMPLES::
1220
1221
sage: A = matrix(GF(2), 5, 5, 0)
1222
sage: A.randomize(0.5); A
1223
[0 0 0 1 1]
1224
[0 1 0 0 1]
1225
[1 0 0 0 0]
1226
[0 1 0 0 0]
1227
[0 0 0 1 0]
1228
sage: A.randomize(); A
1229
[0 0 1 1 0]
1230
[1 1 0 0 1]
1231
[1 1 1 1 0]
1232
[1 1 1 1 1]
1233
[0 0 1 1 0]
1234
1235
TESTS:
1236
1237
With the libc random number generator random(), we had problems
1238
where the ranks of all of these matrices would be the same
1239
(and they would all be far too low). This verifies that the
1240
problem is gone, with Mersenne Twister::
1241
1242
sage: MS2 = MatrixSpace(GF(2), 1000)
1243
sage: [MS2.random_element().rank() for i in range(5)]
1244
[999, 998, 1000, 999, 999]
1245
1246
Testing corner case::
1247
1248
sage: A = random_matrix(GF(2),3,0)
1249
sage: A
1250
[]
1251
"""
1252
if self._ncols == 0 or self._nrows == 0:
1253
return
1254
1255
density = float(density)
1256
if density <= 0:
1257
return
1258
if density > 1:
1259
density = float(1)
1260
1261
self.check_mutability()
1262
self.clear_cache()
1263
1264
cdef randstate rstate = current_randstate()
1265
1266
cdef int i, j, k
1267
cdef int nc
1268
cdef unsigned int low, high
1269
cdef m4ri_word mask = 0
1270
1271
# Original code, before adding the ``nonzero`` option.
1272
if not nonzero:
1273
if density == 1:
1274
assert(sizeof(m4ri_word) == 8)
1275
mask = __M4RI_LEFT_BITMASK(self._entries.ncols % m4ri_radix)
1276
for i from 0 <= i < self._nrows:
1277
for j from 0 <= j < self._entries.width:
1278
# for portability we get 32-bit twice rather than 64-bit once
1279
low = gmp_urandomb_ui(rstate.gmp_state, 32)
1280
high = gmp_urandomb_ui(rstate.gmp_state, 32)
1281
self._entries.rows[i][j] = m4ri_swap_bits( ((<unsigned long long>high)<<32) | (<unsigned long long>low) )
1282
self._entries.rows[i][self._entries.width - 1] &= mask
1283
else:
1284
nc = self._ncols
1285
num_per_row = int(density * nc)
1286
sig_on()
1287
for i from 0 <= i < self._nrows:
1288
for j from 0 <= j < num_per_row:
1289
k = rstate.c_random()%nc
1290
mzd_write_bit(self._entries, i, k, rstate.c_random() % 2)
1291
sig_off()
1292
1293
# New code for the case when ``nonzero`` is ``True``.
1294
else:
1295
sig_on()
1296
for i from 0 <= i < self._nrows:
1297
for j from 0 <= j < self._ncols:
1298
if rstate.c_rand_double() <= density:
1299
mzd_write_bit(self._entries, i, j, 1)
1300
sig_off()
1301
1302
cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
1303
"""
1304
EXAMPLE::
1305
1306
sage: A = random_matrix(GF(2),3,3); A
1307
[0 1 0]
1308
[0 1 1]
1309
[0 0 0]
1310
1311
sage: A.rescale_row(0,0,0); A
1312
[0 0 0]
1313
[0 1 1]
1314
[0 0 0]
1315
"""
1316
if (int(multiple)%2) == 0:
1317
mzd_row_clear_offset(self._entries, row, start_col);
1318
1319
cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple,
1320
Py_ssize_t start_col):
1321
"""
1322
EXAMPLE::
1323
1324
sage: A = random_matrix(GF(2),3,3); A
1325
[0 1 0]
1326
[0 1 1]
1327
[0 0 0]
1328
sage: A.add_multiple_of_row(0,1,1,0); A
1329
[0 0 1]
1330
[0 1 1]
1331
[0 0 0]
1332
"""
1333
if (int(multiple)%2) != 0:
1334
mzd_row_add_offset(self._entries, row_to, row_from, start_col)
1335
1336
cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
1337
"""
1338
EXAMPLE::
1339
1340
sage: A = random_matrix(GF(2),3,3); A
1341
[0 1 0]
1342
[0 1 1]
1343
[0 0 0]
1344
sage: A.swap_rows(0,1); A
1345
[0 1 1]
1346
[0 1 0]
1347
[0 0 0]
1348
"""
1349
mzd_row_swap(self._entries, row1, row2)
1350
1351
cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
1352
"""
1353
EXAMPLE::
1354
1355
sage: A = random_matrix(GF(2),3,3); A
1356
[0 1 0]
1357
[0 1 1]
1358
[0 0 0]
1359
1360
sage: A.swap_columns(0,1); A
1361
[1 0 0]
1362
[1 0 1]
1363
[0 0 0]
1364
1365
sage: A = random_matrix(GF(2),3,65)
1366
1367
sage: B = A.__copy__()
1368
sage: B.swap_columns(0,1)
1369
sage: B.swap_columns(0,1)
1370
sage: A == B
1371
True
1372
1373
sage: A.swap_columns(0,64)
1374
sage: A.column(0) == B.column(64)
1375
True
1376
sage: A.swap_columns(63,64)
1377
sage: A.column(63) == B.column(0)
1378
True
1379
"""
1380
mzd_col_swap(self._entries, col1, col2)
1381
1382
1383
1384
def _magma_init_(self, magma):
1385
"""
1386
Returns a string of self in ``Magma`` form. Does not return
1387
``Magma`` object but string.
1388
1389
EXAMPLE::
1390
1391
sage: A = random_matrix(GF(2),3,3)
1392
sage: A._magma_init_(magma) # optional - magma
1393
'Matrix(GF(2),3,3,StringToIntegerSequence("0 1 0 0 1 1 0 0 0"))'
1394
sage: A = random_matrix(GF(2),100,100)
1395
sage: B = random_matrix(GF(2),100,100)
1396
sage: magma(A*B) == magma(A) * magma(B) # optional - magma
1397
True
1398
1399
TESTS::
1400
1401
sage: A = random_matrix(GF(2),0,3)
1402
sage: magma(A) # optional - magma
1403
Matrix with 0 rows and 3 columns
1404
sage: A = matrix(GF(2),2,3,[0,1,1,1,0,0])
1405
sage: A._magma_init_(magma) # optional - magma
1406
'Matrix(GF(2),2,3,StringToIntegerSequence("0 1 1 1 0 0"))'
1407
sage: magma(A) # optional - magma
1408
[0 1 1]
1409
[1 0 0]
1410
"""
1411
s = self.base_ring()._magma_init_(magma)
1412
return 'Matrix(%s,%s,%s,StringToIntegerSequence("%s"))'%(
1413
s, self._nrows, self._ncols, self._export_as_string())
1414
1415
def determinant(self):
1416
"""
1417
Return the determinant of this matrix over GF(2).
1418
1419
EXAMPLES::
1420
1421
sage: matrix(GF(2),2,[1,1,0,1]).determinant()
1422
1
1423
sage: matrix(GF(2),2,[1,1,1,1]).determinant()
1424
0
1425
"""
1426
if not self.is_square():
1427
raise ValueError("self must be a square matrix")
1428
return self.base_ring()(1 if self.rank() == self.nrows() else 0)
1429
1430
def transpose(self):
1431
"""
1432
Returns transpose of self and leaves self untouched.
1433
1434
EXAMPLE::
1435
1436
sage: A = Matrix(GF(2),3,5,[1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0])
1437
sage: A
1438
[1 0 1 0 0]
1439
[0 1 1 0 0]
1440
[1 1 0 1 0]
1441
sage: B = A.transpose(); B
1442
[1 0 1]
1443
[0 1 1]
1444
[1 1 0]
1445
[0 0 1]
1446
[0 0 0]
1447
sage: B.transpose() == A
1448
True
1449
1450
``.T`` is a convenient shortcut for the transpose::
1451
1452
sage: A.T
1453
[1 0 1]
1454
[0 1 1]
1455
[1 1 0]
1456
[0 0 1]
1457
[0 0 0]
1458
1459
TESTS:
1460
sage: A = random_matrix(GF(2),0,40)
1461
sage: A.transpose()
1462
40 x 0 dense matrix over Finite Field of size 2
1463
1464
sage: A = Matrix(GF(2), [1,0])
1465
sage: B = A.transpose()
1466
sage: A[0,0] = 0
1467
sage: B[0,0]
1468
1
1469
"""
1470
cdef Matrix_mod2_dense A = self.new_matrix(ncols = self._nrows, nrows = self._ncols)
1471
if self._nrows == 0 or self._ncols == 0:
1472
return A
1473
1474
A._entries = mzd_transpose(A._entries, self._entries)
1475
if self._subdivisions is not None:
1476
A.subdivide(*self.subdivisions())
1477
return A
1478
1479
cdef int _cmp_c_impl(self, Element right) except -2:
1480
if self._nrows == 0 or self._ncols == 0:
1481
return 0
1482
return mzd_cmp(self._entries, (<Matrix_mod2_dense>right)._entries)
1483
1484
1485
def augment(self, right, subdivide=False):
1486
r"""
1487
Augments ``self`` with ``right``.
1488
1489
EXAMPLE::
1490
1491
sage: MS = MatrixSpace(GF(2),3,3)
1492
sage: A = MS([0, 1, 0, 1, 1, 0, 1, 1, 1]); A
1493
[0 1 0]
1494
[1 1 0]
1495
[1 1 1]
1496
sage: B = A.augment(MS(1)); B
1497
[0 1 0 1 0 0]
1498
[1 1 0 0 1 0]
1499
[1 1 1 0 0 1]
1500
sage: B.echelonize(); B
1501
[1 0 0 1 1 0]
1502
[0 1 0 1 0 0]
1503
[0 0 1 0 1 1]
1504
sage: C = B.matrix_from_columns([3,4,5]); C
1505
[1 1 0]
1506
[1 0 0]
1507
[0 1 1]
1508
sage: C == ~A
1509
True
1510
sage: C*A == MS(1)
1511
True
1512
1513
A vector may be augmented to a matrix. ::
1514
1515
sage: A = matrix(GF(2), 3, 4, range(12))
1516
sage: v = vector(GF(2), 3, range(3))
1517
sage: A.augment(v)
1518
[0 1 0 1 0]
1519
[0 1 0 1 1]
1520
[0 1 0 1 0]
1521
1522
The ``subdivide`` option will add a natural subdivision between
1523
``self`` and ``right``. For more details about how subdivisions
1524
are managed when augmenting, see
1525
:meth:`sage.matrix.matrix1.Matrix.augment`. ::
1526
1527
sage: A = matrix(GF(2), 3, 5, range(15))
1528
sage: B = matrix(GF(2), 3, 3, range(9))
1529
sage: A.augment(B, subdivide=True)
1530
[0 1 0 1 0|0 1 0]
1531
[1 0 1 0 1|1 0 1]
1532
[0 1 0 1 0|0 1 0]
1533
1534
TESTS::
1535
1536
sage: A = random_matrix(GF(2),2,3)
1537
sage: B = random_matrix(GF(2),2,0)
1538
sage: A.augment(B)
1539
[0 1 0]
1540
[0 1 1]
1541
1542
sage: B.augment(A)
1543
[0 1 0]
1544
[0 1 1]
1545
1546
sage: M = Matrix(GF(2), 0, 0, 0)
1547
sage: N = Matrix(GF(2), 0, 19, 0)
1548
sage: W = M.augment(N)
1549
sage: W.ncols()
1550
19
1551
sage: M = Matrix(GF(2), 0, 1, 0)
1552
sage: N = Matrix(GF(2), 0, 1, 0)
1553
sage: M.augment(N)
1554
[]
1555
"""
1556
if hasattr(right, '_vector_'):
1557
right = right.column()
1558
1559
cdef Matrix_mod2_dense other = right
1560
1561
if self._nrows != other._nrows:
1562
raise TypeError("Both numbers of rows must match.")
1563
1564
if self._ncols == 0:
1565
return other.__copy__()
1566
if other._ncols == 0:
1567
return self.__copy__()
1568
1569
cdef Matrix_mod2_dense Z
1570
Z = self.new_matrix(ncols = self._ncols + other._ncols)
1571
if self._nrows == 0:
1572
return Z
1573
Z._entries = mzd_concat(Z._entries, self._entries, other._entries)
1574
if subdivide:
1575
Z._subdivide_on_augment(self, other)
1576
return Z
1577
1578
def stack(self, bottom, subdivide=False):
1579
r"""
1580
Stack ``self`` on top of ``bottom``.
1581
1582
EXAMPLE::
1583
1584
sage: A = matrix(GF(2),2,2,[1,0,0,1])
1585
sage: B = matrix(GF(2),2,2,[0,1,1,0])
1586
sage: A.stack(B)
1587
[1 0]
1588
[0 1]
1589
[0 1]
1590
[1 0]
1591
sage: B.stack(A)
1592
[0 1]
1593
[1 0]
1594
[1 0]
1595
[0 1]
1596
1597
A vector may be stacked below a matrix. ::
1598
1599
sage: A = matrix(GF(2), 2, 5, range(10))
1600
sage: v = vector(GF(2), 5, range(5))
1601
sage: A.stack(v)
1602
[0 1 0 1 0]
1603
[1 0 1 0 1]
1604
[0 1 0 1 0]
1605
1606
The ``subdivide`` option will add a natural subdivision between
1607
``self`` and ``bottom``. For more details about how subdivisions
1608
are managed when stacking, see
1609
:meth:`sage.matrix.matrix1.Matrix.stack`. ::
1610
1611
sage: A = matrix(GF(2), 3, 5, range(15))
1612
sage: B = matrix(GF(2), 1, 5, range(5))
1613
sage: A.stack(B, subdivide=True)
1614
[0 1 0 1 0]
1615
[1 0 1 0 1]
1616
[0 1 0 1 0]
1617
[---------]
1618
[0 1 0 1 0]
1619
1620
TESTS::
1621
1622
sage: A = random_matrix(GF(2),0,3)
1623
sage: B = random_matrix(GF(2),3,3)
1624
sage: A.stack(B)
1625
[0 1 0]
1626
[0 1 1]
1627
[0 0 0]
1628
1629
sage: B.stack(A)
1630
[0 1 0]
1631
[0 1 1]
1632
[0 0 0]
1633
1634
sage: M = Matrix(GF(2), 0, 0, 0)
1635
sage: N = Matrix(GF(2), 19, 0, 0)
1636
sage: W = M.stack(N)
1637
sage: W.nrows()
1638
19
1639
sage: M = Matrix(GF(2), 1, 0, 0)
1640
sage: N = Matrix(GF(2), 1, 0, 0)
1641
sage: M.stack(N)
1642
[]
1643
"""
1644
if hasattr(bottom, '_vector_'):
1645
bottom = bottom.row()
1646
cdef Matrix_mod2_dense other = bottom
1647
1648
if self._ncols != other._ncols:
1649
raise TypeError("Both numbers of columns must match.")
1650
1651
if self._nrows == 0:
1652
return other.__copy__()
1653
if other._nrows == 0:
1654
return self.__copy__()
1655
1656
cdef Matrix_mod2_dense Z
1657
Z = self.new_matrix(nrows = self._nrows + other._nrows)
1658
if self._ncols == 0:
1659
return Z
1660
Z._entries = mzd_stack(Z._entries, self._entries, other._entries)
1661
if subdivide:
1662
Z._subdivide_on_stack(self, other)
1663
return Z
1664
1665
def submatrix(self, lowr, lowc, nrows , ncols):
1666
"""
1667
Return submatrix from the index lowr,lowc (inclusive) with
1668
dimension nrows x ncols.
1669
1670
INPUT:
1671
lowr -- index of start row
1672
lowc -- index of start column
1673
nrows -- number of rows of submatrix
1674
ncols -- number of columns of submatrix
1675
1676
EXAMPLES::
1677
1678
sage: A = random_matrix(GF(2),200,200)
1679
sage: A[0:2,0:2] == A.submatrix(0,0,2,2)
1680
True
1681
sage: A[0:100,0:100] == A.submatrix(0,0,100,100)
1682
True
1683
sage: A == A.submatrix(0,0,200,200)
1684
True
1685
1686
sage: A[1:3,1:3] == A.submatrix(1,1,2,2)
1687
True
1688
sage: A[1:100,1:100] == A.submatrix(1,1,99,99)
1689
True
1690
sage: A[1:200,1:200] == A.submatrix(1,1,199,199)
1691
True
1692
"""
1693
cdef Matrix_mod2_dense A
1694
1695
cdef int highr, highc
1696
1697
highr = lowr + nrows
1698
highc = lowc + ncols
1699
1700
if nrows <= 0 or ncols <= 0:
1701
raise TypeError("Expected nrows, ncols to be > 0, but got %d,%d instead."%(nrows, ncols))
1702
1703
if highc > self._entries.ncols:
1704
raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead."%(highc, self._entries.ncols))
1705
1706
if highr > self._entries.nrows:
1707
raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead."%(highr, self._entries.nrows))
1708
1709
if lowr < 0:
1710
raise TypeError("Expected lowr >= 0, but got %d instead."%lowr)
1711
1712
if lowc < 0:
1713
raise TypeError("Expected lowc >= 0, but got %d instead."%lowc)
1714
1715
A = self.new_matrix(nrows = nrows, ncols = ncols)
1716
if self._ncols == 0 or self._nrows == 0:
1717
return A
1718
A._entries = mzd_submatrix(A._entries, self._entries, lowr, lowc, highr, highc)
1719
return A
1720
1721
def __reduce__(self):
1722
"""
1723
Serialize ``self``.
1724
1725
EXAMPLE::
1726
1727
sage: A = random_matrix(GF(2),10,10)
1728
sage: f,s = A.__reduce__()
1729
sage: f(*s) == A
1730
True
1731
"""
1732
cdef int i,j, r,c, size
1733
1734
r, c = self.nrows(), self.ncols()
1735
if r == 0 or c == 0:
1736
return unpickle_matrix_mod2_dense_v1, (r, c, None, 0)
1737
1738
sig_on()
1739
cdef gdImagePtr im = gdImageCreate(c, r)
1740
sig_off()
1741
cdef int black = gdImageColorAllocate(im, 0, 0, 0)
1742
cdef int white = gdImageColorAllocate(im, 255, 255, 255)
1743
gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)
1744
for i from 0 <= i < r:
1745
for j from 0 <= j < c:
1746
if mzd_read_bit(self._entries, i, j):
1747
gdImageSetPixel(im, j, i, black )
1748
1749
cdef signed char *buf = <signed char*>gdImagePngPtr(im, &size)
1750
1751
data = [buf[i] for i in range(size)]
1752
gdFree(buf)
1753
gdImageDestroy(im)
1754
return unpickle_matrix_mod2_dense_v1, (r,c, data, size)
1755
1756
cpdef _export_as_string(self):
1757
"""
1758
Return space separated string of the entries in this matrix.
1759
1760
EXAMPLES:
1761
sage: w = matrix(GF(2),2,3,[1,0,1,1,1,0])
1762
sage: w._export_as_string()
1763
'1 0 1 1 1 0'
1764
"""
1765
cdef Py_ssize_t i, j, k, n
1766
cdef char *s, *t
1767
1768
if self._nrows == 0 or self._ncols == 0:
1769
data = ''
1770
else:
1771
n = self._nrows*self._ncols*2 + 2
1772
s = <char*> sage_malloc(n * sizeof(char))
1773
k = 0
1774
sig_on()
1775
for i in range(self._nrows):
1776
for j in range(self._ncols):
1777
s[k] = <char>(48 + (1 if mzd_read_bit(self._entries,i,j) else 0)) # "0" or "1"
1778
k += 1
1779
s[k] = <char>32 # space
1780
k += 1
1781
sig_off()
1782
s[k-1] = <char>0
1783
data = str(s)
1784
sage_free(s)
1785
return data
1786
1787
def density(self, approx=False):
1788
"""
1789
Return the density of this matrix.
1790
1791
By density we understand the ration of the number of nonzero
1792
positions and the self.nrows() * self.ncols(), i.e. the number
1793
of possible nonzero positions.
1794
1795
INPUT:
1796
approx -- return floating point approximation (default: False)
1797
1798
EXAMPLE::
1799
1800
sage: A = random_matrix(GF(2),1000,1000)
1801
sage: d = A.density(); d
1802
62483/125000
1803
1804
sage: float(d)
1805
0.499864
1806
1807
sage: A.density(approx=True)
1808
0.499864000...
1809
1810
sage: float(len(A.nonzero_positions())/1000^2)
1811
0.499864
1812
"""
1813
if approx:
1814
from sage.rings.real_mpfr import create_RealNumber
1815
return create_RealNumber(mzd_density(self._entries, 1))
1816
else:
1817
return matrix_dense.Matrix_dense.density(self)
1818
1819
def rank(self, algorithm='ple'):
1820
"""
1821
Return the rank of this matrix.
1822
1823
On average 'ple' should be faster than 'm4ri' and hence it is
1824
the default choice. However, for small - i.e. quite few
1825
thousand rows & columns - and sparse matrices 'm4ri' might be
1826
a better choice.
1827
1828
INPUT:
1829
1830
- ``algorithm`` - either "ple" or "m4ri"
1831
1832
EXAMPLE::
1833
1834
sage: A = random_matrix(GF(2), 1000, 1000)
1835
sage: A.rank()
1836
999
1837
1838
sage: A = matrix(GF(2),10, 0)
1839
sage: A.rank()
1840
0
1841
"""
1842
x = self.fetch('rank')
1843
if not x is None:
1844
return x
1845
if self._nrows == 0 or self._ncols == 0:
1846
return 0
1847
cdef mzd_t *A = mzd_copy(NULL, self._entries)
1848
cdef mzp_t *P, *Q
1849
1850
if algorithm == 'pls':
1851
from sage.misc.superseded import deprecation
1852
deprecation(12840, "Parameter 'pls' is deprecated, use 'ple' instead.")
1853
P = mzp_init(self._entries.nrows)
1854
Q = mzp_init(self._entries.ncols)
1855
r = mzd_ple(A, P, Q, 0)
1856
mzp_free(P)
1857
mzp_free(Q)
1858
elif algorithm == 'ple':
1859
P = mzp_init(self._entries.nrows)
1860
Q = mzp_init(self._entries.ncols)
1861
r = mzd_ple(A, P, Q, 0)
1862
mzp_free(P)
1863
mzp_free(Q)
1864
elif algorithm == 'm4ri':
1865
r = mzd_echelonize_m4ri(A, 0, 0)
1866
else:
1867
raise ValueError("Algorithm '%s' unknown."%algorithm)
1868
mzd_free(A)
1869
self.cache('rank', r)
1870
return r
1871
1872
def _right_kernel_matrix(self, **kwds):
1873
r"""
1874
Returns a pair that includes a matrix of basis vectors
1875
for the right kernel of ``self``.
1876
1877
INPUT:
1878
1879
- ``kwds`` - these are provided for consistency with other versions
1880
of this method. Here they are ignored as there is no optional
1881
behavior available.
1882
1883
OUTPUT:
1884
1885
Returns a pair. First item is the string 'computed-pluq'
1886
that identifies the nature of the basis vectors.
1887
1888
Second item is a matrix whose rows are a basis for the right kernel,
1889
over the integers mod 2, as computed by the M4RI library
1890
using PLUQ matrix decomposition.
1891
1892
EXAMPLES::
1893
1894
sage: A = matrix(GF(2), [
1895
... [0, 1, 0, 0, 1, 0, 1, 1],
1896
... [1, 0, 1, 0, 0, 1, 1, 0],
1897
... [0, 0, 1, 0, 0, 1, 0, 1],
1898
... [1, 0, 1, 1, 0, 1, 1, 0],
1899
... [0, 0, 1, 0, 0, 1, 0, 1],
1900
... [1, 1, 0, 1, 1, 0, 0, 0]])
1901
sage: A
1902
[0 1 0 0 1 0 1 1]
1903
[1 0 1 0 0 1 1 0]
1904
[0 0 1 0 0 1 0 1]
1905
[1 0 1 1 0 1 1 0]
1906
[0 0 1 0 0 1 0 1]
1907
[1 1 0 1 1 0 0 0]
1908
sage: result = A._right_kernel_matrix()
1909
sage: result[0]
1910
'computed-pluq'
1911
sage: result[1]
1912
[0 1 0 0 1 0 0 0]
1913
[0 0 1 0 0 1 0 0]
1914
[1 1 0 0 0 0 1 0]
1915
[1 1 1 0 0 0 0 1]
1916
sage: X = result[1].transpose()
1917
sage: A*X == zero_matrix(GF(2), 6, 4)
1918
True
1919
1920
TESTS:
1921
1922
We test the three trivial cases. Matrices with no rows or columns will
1923
cause segfaults in the M4RI code, so we protect against that instance.
1924
Computing a kernel or a right kernel matrix should never pass these
1925
problem matrices here. ::
1926
1927
sage: A = matrix(GF(2), 0, 2)
1928
sage: A._right_kernel_matrix()
1929
Traceback (most recent call last):
1930
...
1931
ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed
1932
sage: A = matrix(GF(2), 2, 0)
1933
sage: A._right_kernel_matrix()
1934
Traceback (most recent call last):
1935
...
1936
ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed
1937
sage: A = zero_matrix(GF(2), 4, 3)
1938
sage: A._right_kernel_matrix()[1]
1939
[1 0 0]
1940
[0 1 0]
1941
[0 0 1]
1942
"""
1943
tm = verbose("computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1)
1944
if self.nrows()==0 or self.ncols()==0:
1945
raise ValueError("kernels of matrices mod 2 with zero rows or zero columns cannot be computed")
1946
cdef Matrix_mod2_dense M
1947
cdef mzd_t *A = mzd_copy(NULL, self._entries)
1948
# Despite the name, this next call returns X such that M*X = 0
1949
cdef mzd_t *k = mzd_kernel_left_pluq(A, 0)
1950
mzd_free(A)
1951
if k != NULL:
1952
M = self.new_matrix(nrows = k.ncols, ncols = k.nrows)
1953
mzd_transpose(M._entries, k)
1954
mzd_free(k)
1955
else:
1956
M = self.new_matrix(nrows = 0, ncols = self._ncols)
1957
verbose("done computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1, t=tm)
1958
return 'computed-pluq', M
1959
1960
# Used for hashing
1961
cdef int i, k
1962
cdef unsigned long parity_table[256]
1963
for i from 0 <= i < 256:
1964
parity_table[i] = 1 & ((i) ^ (i>>1) ^ (i>>2) ^ (i>>3) ^
1965
(i>>4) ^ (i>>5) ^ (i>>6) ^ (i>>7))
1966
1967
# gmp's ULONG_PARITY may use special
1968
# assembly instructions, could be faster
1969
cpdef inline unsigned long parity(m4ri_word a):
1970
"""
1971
Returns the parity of the number of bits in a.
1972
1973
EXAMPLES::
1974
1975
sage: from sage.matrix.matrix_mod2_dense import parity
1976
sage: parity(1)
1977
1L
1978
sage: parity(3)
1979
0L
1980
sage: parity(0x10000101011)
1981
1L
1982
"""
1983
if sizeof(m4ri_word) == 8:
1984
a ^= a >> 32
1985
a ^= a >> 16
1986
a ^= a >> 8
1987
return parity_table[a & 0xFF]
1988
1989
cdef inline unsigned long parity_mask(m4ri_word a):
1990
return -parity(a)
1991
1992
1993
def unpickle_matrix_mod2_dense_v1(r, c, data, size):
1994
"""
1995
Deserialize a matrix encoded in the string ``s``.
1996
1997
INPUT:
1998
r -- number of rows of matrix
1999
c -- number of columns of matrix
2000
s -- a string
2001
size -- length of the string s
2002
2003
EXAMPLE::
2004
2005
sage: A = random_matrix(GF(2),100,101)
2006
sage: _,(r,c,s,s2) = A.__reduce__()
2007
sage: from sage.matrix.matrix_mod2_dense import unpickle_matrix_mod2_dense_v1
2008
sage: unpickle_matrix_mod2_dense_v1(r,c,s,s2) == A
2009
True
2010
sage: loads(dumps(A)) == A
2011
True
2012
"""
2013
from sage.matrix.constructor import Matrix
2014
from sage.rings.finite_rings.constructor import FiniteField as GF
2015
2016
cdef int i, j
2017
cdef Matrix_mod2_dense A
2018
2019
A = <Matrix_mod2_dense>Matrix(GF(2),r,c)
2020
if r == 0 or c == 0:
2021
return A
2022
2023
cdef signed char *buf = <signed char*>sage_malloc(size)
2024
for i from 0 <= i < size:
2025
buf[i] = data[i]
2026
2027
sig_on()
2028
cdef gdImagePtr im = gdImageCreateFromPngPtr(size, buf)
2029
sig_off()
2030
2031
sage_free(buf)
2032
2033
if gdImageSX(im) != c or gdImageSY(im) != r:
2034
raise TypeError("Pickled data dimension doesn't match.")
2035
2036
2037
for i from 0 <= i < r:
2038
for j from 0 <= j < c:
2039
mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))
2040
gdImageDestroy(im)
2041
return A
2042
2043
def from_png(filename):
2044
"""
2045
Returns a dense matrix over GF(2) from a 1-bit PNG image read from
2046
``filename``. No attempt is made to verify that the filename string
2047
actually points to a PNG image.
2048
2049
INPUT:
2050
filename -- a string
2051
2052
EXAMPLE::
2053
2054
sage: from sage.matrix.matrix_mod2_dense import from_png, to_png
2055
sage: A = random_matrix(GF(2),10,10)
2056
sage: fn = tmp_filename()
2057
sage: to_png(A, fn)
2058
sage: B = from_png(fn)
2059
sage: A == B
2060
True
2061
"""
2062
from sage.matrix.constructor import Matrix
2063
from sage.rings.finite_rings.constructor import FiniteField as GF
2064
2065
cdef int i,j,r,c
2066
cdef Matrix_mod2_dense A
2067
2068
fn = open(filename,"r") # check filename
2069
fn.close()
2070
2071
cdef FILE *f = fopen(filename, "rb")
2072
sig_on()
2073
cdef gdImagePtr im = gdImageCreateFromPng(f)
2074
sig_off()
2075
2076
c, r = gdImageSX(im), gdImageSY(im)
2077
2078
A = <Matrix_mod2_dense>Matrix(GF(2),r,c)
2079
2080
for i from 0 <= i < r:
2081
for j from 0 <= j < c:
2082
mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))
2083
fclose(f)
2084
gdImageDestroy(im)
2085
return A
2086
2087
def to_png(Matrix_mod2_dense A, filename):
2088
"""
2089
Saves the matrix ``A`` to filename as a 1-bit PNG image.
2090
2091
INPUT:
2092
2093
- ``A`` - a matrix over GF(2)
2094
- ``filename`` - a string for a file in a writable position
2095
2096
EXAMPLE::
2097
2098
sage: from sage.matrix.matrix_mod2_dense import from_png, to_png
2099
sage: A = random_matrix(GF(2),10,10)
2100
sage: fn = tmp_filename()
2101
sage: to_png(A, fn)
2102
sage: B = from_png(fn)
2103
sage: A == B
2104
True
2105
"""
2106
cdef int i,j, r,c
2107
r, c = A.nrows(), A.ncols()
2108
if r == 0 or c == 0:
2109
raise TypeError("Cannot write image with dimensions %d x %d"%(c,r))
2110
fn = open(filename,"w") # check filename
2111
fn.close()
2112
cdef gdImagePtr im = gdImageCreate(c, r)
2113
cdef FILE * out = fopen(filename, "wb")
2114
cdef int black = gdImageColorAllocate(im, 0, 0, 0)
2115
cdef int white = gdImageColorAllocate(im, 255, 255, 255)
2116
gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)
2117
for i from 0 <= i < r:
2118
for j from 0 <= j < c:
2119
if mzd_read_bit(A._entries, i, j):
2120
gdImageSetPixel(im, j, i, black )
2121
2122
gdImagePng(im, out)
2123
gdImageDestroy(im)
2124
fclose(out)
2125
2126
def pluq(Matrix_mod2_dense A, algorithm="standard", int param=0):
2127
"""
2128
Return PLUQ factorization of A.
2129
2130
INPUT:
2131
A -- matrix
2132
algorithm -- 'standard' asymptotically fast (default)
2133
'mmpf' M4RI inspired
2134
'naive' naive cubic
2135
param -- either k for 'mmpf' is chosen or matrix multiplication
2136
cutoff for 'standard' (default: 0)
2137
2138
EXAMPLE::
2139
2140
sage: from sage.matrix.matrix_mod2_dense import pluq
2141
sage: A = random_matrix(GF(2),4,4); A
2142
[0 1 0 1]
2143
[0 1 1 1]
2144
[0 0 0 1]
2145
[0 1 1 0]
2146
2147
sage: LU, P, Q = pluq(A)
2148
sage: LU
2149
[1 0 1 0]
2150
[1 1 0 0]
2151
[0 0 1 0]
2152
[1 1 1 0]
2153
2154
sage: P
2155
[0, 1, 2, 3]
2156
2157
sage: Q
2158
[1, 2, 3, 3]
2159
"""
2160
cdef Matrix_mod2_dense B = A.__copy__()
2161
cdef mzp_t *p = mzp_init(A._entries.nrows)
2162
cdef mzp_t *q = mzp_init(A._entries.ncols)
2163
2164
if algorithm == "standard":
2165
sig_on()
2166
mzd_pluq(B._entries, p, q, param)
2167
sig_off()
2168
elif algorithm == "mmpf":
2169
sig_on()
2170
_mzd_pluq_russian(B._entries, p, q, param)
2171
sig_off()
2172
elif algorithm == "naive":
2173
sig_on()
2174
_mzd_pluq_naive(B._entries, p, q)
2175
sig_off()
2176
else:
2177
raise ValueError("Algorithm '%s' unknown."%algorithm)
2178
2179
P = [p.values[i] for i in range(A.nrows())]
2180
Q = [q.values[i] for i in range(A.ncols())]
2181
mzp_free(p)
2182
mzp_free(q)
2183
return B,P,Q
2184
2185
def ple(Matrix_mod2_dense A, algorithm="standard", int param=0):
2186
"""
2187
Return PLE factorization of A.
2188
2189
INPUT:
2190
A -- matrix
2191
algorithm -- 'standard' asymptotically fast (default)
2192
'russian' M4RI inspired
2193
'naive' naive cubic
2194
param -- either k for 'mmpf' is chosen or matrix multiplication
2195
cutoff for 'standard' (default: 0)
2196
2197
EXAMPLE::
2198
2199
sage: from sage.matrix.matrix_mod2_dense import ple
2200
sage: A = random_matrix(GF(2),4,4); A
2201
[0 1 0 1]
2202
[0 1 1 1]
2203
[0 0 0 1]
2204
[0 1 1 0]
2205
2206
sage: LU, P, Q = ple(A)
2207
sage: LU
2208
[1 0 0 1]
2209
[1 1 0 0]
2210
[0 0 1 0]
2211
[1 1 1 0]
2212
2213
sage: P
2214
[0, 1, 2, 3]
2215
2216
sage: Q
2217
[1, 2, 3, 3]
2218
2219
sage: A = random_matrix(GF(2),1000,1000)
2220
sage: ple(A) == ple(A,'russian') == ple(A,'naive')
2221
True
2222
"""
2223
cdef Matrix_mod2_dense B = A.__copy__()
2224
cdef mzp_t *p = mzp_init(A._entries.nrows)
2225
cdef mzp_t *q = mzp_init(A._entries.ncols)
2226
2227
if algorithm == 'standard':
2228
sig_on()
2229
mzd_ple(B._entries, p, q, param)
2230
sig_off()
2231
2232
elif algorithm == "mmpf":
2233
from sage.misc.superseded import deprecation
2234
deprecation(12840, "Parameter 'mmpf' is deprecated, use 'russian' instead.")
2235
sig_on()
2236
_mzd_ple_russian(B._entries, p, q, param)
2237
sig_off()
2238
elif algorithm == "russian":
2239
sig_on()
2240
_mzd_ple_russian(B._entries, p, q, param)
2241
sig_off()
2242
elif algorithm == "naive":
2243
sig_on()
2244
_mzd_ple_naive(B._entries, p, q)
2245
sig_off()
2246
else:
2247
raise ValueError("Algorithm '%s' unknown."%algorithm)
2248
2249
P = [p.values[i] for i in range(A.nrows())]
2250
Q = [q.values[i] for i in range(A.ncols())]
2251
mzp_free(p)
2252
mzp_free(q)
2253
return B,P,Q
2254
2255