Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/matrix/matrix_mod2_dense.pyx
4057 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 "../ext/interrupt.pxi"
97
include "../ext/cdefs.pxi"
98
include '../ext/stdsage.pxi'
99
include '../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._mutability._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
I = mzd_init(self._nrows,self._ncols)
967
mzd_set_ui(I, 1)
968
969
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc = False)
970
sig_on()
971
A._entries = mzd_invert_m4ri(self._entries, I, k)
972
sig_off()
973
mzd_free(I)
974
975
if A._entries==NULL:
976
raise ZeroDivisionError("input matrix must be nonsingular")
977
else:
978
return A
979
980
def __copy__(self):
981
"""
982
Returns a copy of ``self``.
983
984
EXAMPLES::
985
986
sage: MS = MatrixSpace(GF(2),3,3)
987
sage: A = MS(1)
988
sage: A.__copy__() == A
989
True
990
sage: A.__copy__() is A
991
False
992
993
sage: A = random_matrix(GF(2),100,100)
994
sage: A.__copy__() == A
995
True
996
sage: A.__copy__() is A
997
False
998
999
sage: A.echelonize()
1000
sage: A.__copy__() == A
1001
True
1002
1003
"""
1004
cdef Matrix_mod2_dense A
1005
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0)
1006
1007
if self._nrows and self._ncols:
1008
mzd_copy(A._entries, self._entries)
1009
1010
if self._subdivisions is not None:
1011
A.subdivide(*self.subdivisions())
1012
1013
return A
1014
1015
def _list(self):
1016
"""
1017
Returns list of the elements of ``self`` in row major
1018
ordering.
1019
1020
EXAMPLE::
1021
1022
sage: A = Matrix(GF(2),2,2,[1,0,1,1])
1023
sage: A
1024
[1 0]
1025
[1 1]
1026
sage: A.list() #indirect doctest
1027
[1, 0, 1, 1]
1028
1029
TESTS:
1030
sage: A = Matrix(GF(2),3,0)
1031
sage: A.list()
1032
[]
1033
"""
1034
cdef int i,j
1035
l = []
1036
for i from 0 <= i < self._nrows:
1037
for j from 0 <= j < self._ncols:
1038
if mzd_read_bit(self._entries,i,j):
1039
l.append(self._one)
1040
else:
1041
l.append(self._zero)
1042
return l
1043
1044
# def _dict(self):
1045
1046
########################################################################
1047
# LEVEL 3 functionality (Optional)
1048
# * __deepcopy__
1049
# * Matrix windows -- only if you need strassen for that base
1050
########################################################################
1051
1052
def echelonize(self, algorithm='heuristic', cutoff=0, reduced=True, **kwds):
1053
"""
1054
Puts self in (reduced) row echelon form.
1055
1056
INPUT:
1057
self -- a mutable matrix
1058
algorithm -- 'heuristic' -- uses M4RI and PLUQ (default)
1059
'm4ri' -- uses M4RI
1060
'pluq' -- uses PLUQ factorization
1061
'classical' -- uses classical Gaussian elimination
1062
k -- the parameter 'k' of the M4RI algorithm. It MUST be between
1063
1 and 16 (inclusive). If it is not specified it will be calculated as
1064
3/4 * log_2( min(nrows, ncols) ) as suggested in the M4RI paper.
1065
reduced -- return reduced row echelon form (default:True)
1066
1067
EXAMPLE::
1068
1069
sage: A = random_matrix(GF(2), 10, 10)
1070
sage: B = A.__copy__(); B.echelonize() # fastest
1071
sage: C = A.__copy__(); C.echelonize(k=2) # force k
1072
sage: E = A.__copy__(); E.echelonize(algorithm='classical') # force Gaussian elimination
1073
sage: B == C == E
1074
True
1075
1076
TESTS::
1077
1078
sage: VF2 = VectorSpace(GF(2),2)
1079
sage: WF2 = VF2.submodule([VF2([1,1])])
1080
sage: WF2
1081
Vector space of degree 2 and dimension 1 over Finite Field of size 2
1082
Basis matrix:
1083
[1 1]
1084
1085
sage: A2 = matrix(GF(2),2,[1,0,0,1])
1086
sage: A2.kernel()
1087
Vector space of degree 2 and dimension 0 over Finite Field of size 2
1088
Basis matrix:
1089
[]
1090
1091
ALGORITHM: Uses M4RI library
1092
1093
REFERENCES:
1094
[Bard06] G. Bard. 'Accelerating Cryptanalysis with the Method of Four Russians'. Cryptography
1095
E-Print Archive (http://eprint.iacr.org/2006/251.pdf), 2006.
1096
"""
1097
if self._nrows == 0 or self._ncols == 0:
1098
self.cache('in_echelon_form',True)
1099
self.cache('rank', 0)
1100
self.cache('pivots', ())
1101
return self
1102
cdef int k, n, full
1103
1104
full = int(reduced)
1105
1106
x = self.fetch('in_echelon_form')
1107
if not x is None: return # already known to be in echelon form
1108
1109
if algorithm == 'heuristic':
1110
1111
self.check_mutability()
1112
self.clear_cache()
1113
1114
sig_on()
1115
r = mzd_echelonize(self._entries, full)
1116
sig_off()
1117
1118
self.cache('in_echelon_form',True)
1119
self.cache('rank', r)
1120
self.cache('pivots', tuple(self._pivots()))
1121
1122
elif algorithm == 'm4ri':
1123
1124
self.check_mutability()
1125
self.clear_cache()
1126
1127
if 'k' in kwds:
1128
k = int(kwds['k'])
1129
1130
if k<1 or k>16:
1131
raise RuntimeError("k must be between 1 and 16")
1132
k = round(k)
1133
else:
1134
k = 0
1135
1136
sig_on()
1137
r = mzd_echelonize_m4ri(self._entries, full, k)
1138
sig_off()
1139
1140
self.cache('in_echelon_form',True)
1141
self.cache('rank', r)
1142
self.cache('pivots', tuple(self._pivots()))
1143
1144
1145
elif algorithm == 'pluq':
1146
1147
self.check_mutability()
1148
self.clear_cache()
1149
1150
sig_on()
1151
r = mzd_echelonize_pluq(self._entries, full)
1152
sig_off()
1153
1154
self.cache('in_echelon_form',True)
1155
self.cache('rank', r)
1156
self.cache('pivots', tuple(self._pivots()))
1157
1158
elif algorithm == 'linbox':
1159
1160
#self._echelonize_linbox()
1161
raise NotImplementedError
1162
1163
elif algorithm == 'classical':
1164
1165
# for debugging purposes only, it is slow
1166
self._echelon_in_place_classical()
1167
else:
1168
raise ValueError("no algorithm '%s'"%algorithm)
1169
1170
def _pivots(self):
1171
"""
1172
Returns the pivot columns of ``self`` if ``self`` is in
1173
row echelon form.
1174
1175
EXAMPLE::
1176
1177
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])
1178
sage: E = A.echelon_form()
1179
sage: E
1180
[0 1 0 0 0]
1181
[0 0 1 0 0]
1182
[0 0 0 1 0]
1183
[0 0 0 0 1]
1184
[0 0 0 0 0]
1185
sage: E._pivots()
1186
[1, 2, 3, 4]
1187
"""
1188
if not self.fetch('in_echelon_form'):
1189
raise RuntimeError("self must be in reduced row echelon form first.")
1190
pivots = []
1191
cdef Py_ssize_t i, j, nc
1192
nc = self._ncols
1193
i = 0
1194
while i < self._nrows:
1195
for j from i <= j < nc:
1196
if mzd_read_bit(self._entries, i, j):
1197
pivots.append(j)
1198
i += 1
1199
break
1200
if j == nc:
1201
break
1202
return pivots
1203
1204
def randomize(self, density=1, nonzero=False):
1205
"""
1206
Randomize ``density`` proportion of the entries of this matrix,
1207
leaving the rest unchanged.
1208
1209
INPUT:
1210
1211
- ``density`` - float; proportion (roughly) to be considered for
1212
changes
1213
- ``nonzero`` - Bool (default: ``False``); whether the new entries
1214
are forced to be non-zero
1215
1216
OUTPUT:
1217
1218
- None, the matrix is modified in-space
1219
1220
EXAMPLES::
1221
1222
sage: A = matrix(GF(2), 5, 5, 0)
1223
sage: A.randomize(0.5); A
1224
[0 0 0 1 1]
1225
[0 1 0 0 1]
1226
[1 0 0 0 0]
1227
[0 1 0 0 0]
1228
[0 0 0 1 0]
1229
sage: A.randomize(); A
1230
[0 0 1 1 0]
1231
[1 1 0 0 1]
1232
[1 1 1 1 0]
1233
[1 1 1 1 1]
1234
[0 0 1 1 0]
1235
1236
TESTS:
1237
1238
With the libc random number generator random(), we had problems
1239
where the ranks of all of these matrices would be the same
1240
(and they would all be far too low). This verifies that the
1241
problem is gone, with Mersenne Twister::
1242
1243
sage: MS2 = MatrixSpace(GF(2), 1000)
1244
sage: [MS2.random_element().rank() for i in range(5)]
1245
[999, 998, 1000, 999, 999]
1246
1247
Testing corner case::
1248
1249
sage: A = random_matrix(GF(2),3,0)
1250
sage: A
1251
[]
1252
"""
1253
if self._ncols == 0 or self._nrows == 0:
1254
return
1255
1256
density = float(density)
1257
if density <= 0:
1258
return
1259
if density > 1:
1260
density = float(1)
1261
1262
self.check_mutability()
1263
self.clear_cache()
1264
1265
cdef randstate rstate = current_randstate()
1266
1267
cdef int i, j, k
1268
cdef int nc
1269
cdef unsigned int low, high
1270
cdef m4ri_word mask = 0
1271
1272
# Original code, before adding the ``nonzero`` option.
1273
if not nonzero:
1274
if density == 1:
1275
assert(sizeof(m4ri_word) == 8)
1276
mask = __M4RI_LEFT_BITMASK(self._entries.ncols % m4ri_radix)
1277
for i from 0 <= i < self._nrows:
1278
for j from 0 <= j < self._entries.width:
1279
# for portability we get 32-bit twice rather than 64-bit once
1280
low = gmp_urandomb_ui(rstate.gmp_state, 32)
1281
high = gmp_urandomb_ui(rstate.gmp_state, 32)
1282
self._entries.rows[i][j] = m4ri_swap_bits( ((<unsigned long long>high)<<32) | (<unsigned long long>low) )
1283
self._entries.rows[i][self._entries.width - 1] &= mask
1284
else:
1285
nc = self._ncols
1286
num_per_row = int(density * nc)
1287
sig_on()
1288
for i from 0 <= i < self._nrows:
1289
for j from 0 <= j < num_per_row:
1290
k = rstate.c_random()%nc
1291
mzd_write_bit(self._entries, i, k, rstate.c_random() % 2)
1292
sig_off()
1293
1294
# New code for the case when ``nonzero`` is ``True``.
1295
else:
1296
sig_on()
1297
for i from 0 <= i < self._nrows:
1298
for j from 0 <= j < self._ncols:
1299
if rstate.c_rand_double() <= density:
1300
mzd_write_bit(self._entries, i, j, 1)
1301
sig_off()
1302
1303
cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
1304
"""
1305
EXAMPLE::
1306
1307
sage: A = random_matrix(GF(2),3,3); A
1308
[0 1 0]
1309
[0 1 1]
1310
[0 0 0]
1311
1312
sage: A.rescale_row(0,0,0); A
1313
[0 0 0]
1314
[0 1 1]
1315
[0 0 0]
1316
"""
1317
if (int(multiple)%2) == 0:
1318
mzd_row_clear_offset(self._entries, row, start_col);
1319
1320
cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple,
1321
Py_ssize_t start_col):
1322
"""
1323
EXAMPLE::
1324
1325
sage: A = random_matrix(GF(2),3,3); A
1326
[0 1 0]
1327
[0 1 1]
1328
[0 0 0]
1329
sage: A.add_multiple_of_row(0,1,1,0); A
1330
[0 0 1]
1331
[0 1 1]
1332
[0 0 0]
1333
"""
1334
if (int(multiple)%2) != 0:
1335
mzd_row_add_offset(self._entries, row_to, row_from, start_col)
1336
1337
cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
1338
"""
1339
EXAMPLE::
1340
1341
sage: A = random_matrix(GF(2),3,3); A
1342
[0 1 0]
1343
[0 1 1]
1344
[0 0 0]
1345
sage: A.swap_rows(0,1); A
1346
[0 1 1]
1347
[0 1 0]
1348
[0 0 0]
1349
"""
1350
mzd_row_swap(self._entries, row1, row2)
1351
1352
cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
1353
"""
1354
EXAMPLE::
1355
1356
sage: A = random_matrix(GF(2),3,3); A
1357
[0 1 0]
1358
[0 1 1]
1359
[0 0 0]
1360
1361
sage: A.swap_columns(0,1); A
1362
[1 0 0]
1363
[1 0 1]
1364
[0 0 0]
1365
1366
sage: A = random_matrix(GF(2),3,65)
1367
1368
sage: B = A.__copy__()
1369
sage: B.swap_columns(0,1)
1370
sage: B.swap_columns(0,1)
1371
sage: A == B
1372
True
1373
1374
sage: A.swap_columns(0,64)
1375
sage: A.column(0) == B.column(64)
1376
True
1377
sage: A.swap_columns(63,64)
1378
sage: A.column(63) == B.column(0)
1379
True
1380
"""
1381
mzd_col_swap(self._entries, col1, col2)
1382
1383
1384
1385
def _magma_init_(self, magma):
1386
"""
1387
Returns a string of self in ``Magma`` form. Does not return
1388
``Magma`` object but string.
1389
1390
EXAMPLE::
1391
1392
sage: A = random_matrix(GF(2),3,3)
1393
sage: A._magma_init_(magma) # optional - magma
1394
'Matrix(GF(2),3,3,StringToIntegerSequence("0 1 0 0 1 1 0 0 0"))'
1395
sage: A = random_matrix(GF(2),100,100)
1396
sage: B = random_matrix(GF(2),100,100)
1397
sage: magma(A*B) == magma(A) * magma(B) # optional - magma
1398
True
1399
1400
TESTS::
1401
1402
sage: A = random_matrix(GF(2),0,3)
1403
sage: magma(A) # optional - magma
1404
Matrix with 0 rows and 3 columns
1405
sage: A = matrix(GF(2),2,3,[0,1,1,1,0,0])
1406
sage: A._magma_init_(magma) # optional - magma
1407
'Matrix(GF(2),2,3,StringToIntegerSequence("0 1 1 1 0 0"))'
1408
sage: magma(A) # optional - magma
1409
[0 1 1]
1410
[1 0 0]
1411
"""
1412
s = self.base_ring()._magma_init_(magma)
1413
return 'Matrix(%s,%s,%s,StringToIntegerSequence("%s"))'%(
1414
s, self._nrows, self._ncols, self._export_as_string())
1415
1416
def determinant(self):
1417
"""
1418
Return the determinant of this matrix over GF(2).
1419
1420
EXAMPLES::
1421
1422
sage: matrix(GF(2),2,[1,1,0,1]).determinant()
1423
1
1424
sage: matrix(GF(2),2,[1,1,1,1]).determinant()
1425
0
1426
"""
1427
if not self.is_square():
1428
raise ValueError("self must be a square matrix")
1429
return self.base_ring()(1 if self.rank() == self.nrows() else 0)
1430
1431
def transpose(self):
1432
"""
1433
Returns transpose of self and leaves self untouched.
1434
1435
EXAMPLE::
1436
1437
sage: A = Matrix(GF(2),3,5,[1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0])
1438
sage: A
1439
[1 0 1 0 0]
1440
[0 1 1 0 0]
1441
[1 1 0 1 0]
1442
sage: B = A.transpose(); B
1443
[1 0 1]
1444
[0 1 1]
1445
[1 1 0]
1446
[0 0 1]
1447
[0 0 0]
1448
sage: B.transpose() == A
1449
True
1450
1451
``.T`` is a convenient shortcut for the transpose::
1452
1453
sage: A.T
1454
[1 0 1]
1455
[0 1 1]
1456
[1 1 0]
1457
[0 0 1]
1458
[0 0 0]
1459
1460
TESTS:
1461
sage: A = random_matrix(GF(2),0,40)
1462
sage: A.transpose()
1463
40 x 0 dense matrix over Finite Field of size 2
1464
1465
sage: A = Matrix(GF(2), [1,0])
1466
sage: B = A.transpose()
1467
sage: A[0,0] = 0
1468
sage: B[0,0]
1469
1
1470
"""
1471
cdef Matrix_mod2_dense A = self.new_matrix(ncols = self._nrows, nrows = self._ncols)
1472
if self._nrows == 0 or self._ncols == 0:
1473
return A
1474
1475
A._entries = mzd_transpose(A._entries, self._entries)
1476
if self._subdivisions is not None:
1477
A.subdivide(*self.subdivisions())
1478
return A
1479
1480
cdef int _cmp_c_impl(self, Element right) except -2:
1481
if self._nrows == 0 or self._ncols == 0:
1482
return 0
1483
return mzd_cmp(self._entries, (<Matrix_mod2_dense>right)._entries)
1484
1485
1486
def augment(self, right, subdivide=False):
1487
r"""
1488
Augments ``self`` with ``right``.
1489
1490
EXAMPLE::
1491
1492
sage: MS = MatrixSpace(GF(2),3,3)
1493
sage: A = MS([0, 1, 0, 1, 1, 0, 1, 1, 1]); A
1494
[0 1 0]
1495
[1 1 0]
1496
[1 1 1]
1497
sage: B = A.augment(MS(1)); B
1498
[0 1 0 1 0 0]
1499
[1 1 0 0 1 0]
1500
[1 1 1 0 0 1]
1501
sage: B.echelonize(); B
1502
[1 0 0 1 1 0]
1503
[0 1 0 1 0 0]
1504
[0 0 1 0 1 1]
1505
sage: C = B.matrix_from_columns([3,4,5]); C
1506
[1 1 0]
1507
[1 0 0]
1508
[0 1 1]
1509
sage: C == ~A
1510
True
1511
sage: C*A == MS(1)
1512
True
1513
1514
A vector may be augmented to a matrix. ::
1515
1516
sage: A = matrix(GF(2), 3, 4, range(12))
1517
sage: v = vector(GF(2), 3, range(3))
1518
sage: A.augment(v)
1519
[0 1 0 1 0]
1520
[0 1 0 1 1]
1521
[0 1 0 1 0]
1522
1523
The ``subdivide`` option will add a natural subdivision between
1524
``self`` and ``right``. For more details about how subdivisions
1525
are managed when augmenting, see
1526
:meth:`sage.matrix.matrix1.Matrix.augment`. ::
1527
1528
sage: A = matrix(GF(2), 3, 5, range(15))
1529
sage: B = matrix(GF(2), 3, 3, range(9))
1530
sage: A.augment(B, subdivide=True)
1531
[0 1 0 1 0|0 1 0]
1532
[1 0 1 0 1|1 0 1]
1533
[0 1 0 1 0|0 1 0]
1534
1535
TESTS::
1536
1537
sage: A = random_matrix(GF(2),2,3)
1538
sage: B = random_matrix(GF(2),2,0)
1539
sage: A.augment(B)
1540
[0 1 0]
1541
[0 1 1]
1542
1543
sage: B.augment(A)
1544
[0 1 0]
1545
[0 1 1]
1546
1547
sage: M = Matrix(GF(2), 0, 0, 0)
1548
sage: N = Matrix(GF(2), 0, 19, 0)
1549
sage: W = M.augment(N)
1550
sage: W.ncols()
1551
19
1552
sage: M = Matrix(GF(2), 0, 1, 0)
1553
sage: N = Matrix(GF(2), 0, 1, 0)
1554
sage: M.augment(N)
1555
[]
1556
"""
1557
if hasattr(right, '_vector_'):
1558
right = right.column()
1559
1560
cdef Matrix_mod2_dense other = right
1561
1562
if self._nrows != other._nrows:
1563
raise TypeError("Both numbers of rows must match.")
1564
1565
if self._ncols == 0:
1566
return other.__copy__()
1567
if other._ncols == 0:
1568
return self.__copy__()
1569
1570
cdef Matrix_mod2_dense Z
1571
Z = self.new_matrix(ncols = self._ncols + other._ncols)
1572
if self._nrows == 0:
1573
return Z
1574
Z._entries = mzd_concat(Z._entries, self._entries, other._entries)
1575
if subdivide:
1576
Z._subdivide_on_augment(self, other)
1577
return Z
1578
1579
def stack(self, bottom, subdivide=False):
1580
r"""
1581
Stack ``self`` on top of ``bottom``.
1582
1583
EXAMPLE::
1584
1585
sage: A = matrix(GF(2),2,2,[1,0,0,1])
1586
sage: B = matrix(GF(2),2,2,[0,1,1,0])
1587
sage: A.stack(B)
1588
[1 0]
1589
[0 1]
1590
[0 1]
1591
[1 0]
1592
sage: B.stack(A)
1593
[0 1]
1594
[1 0]
1595
[1 0]
1596
[0 1]
1597
1598
A vector may be stacked below a matrix. ::
1599
1600
sage: A = matrix(GF(2), 2, 5, range(10))
1601
sage: v = vector(GF(2), 5, range(5))
1602
sage: A.stack(v)
1603
[0 1 0 1 0]
1604
[1 0 1 0 1]
1605
[0 1 0 1 0]
1606
1607
The ``subdivide`` option will add a natural subdivision between
1608
``self`` and ``bottom``. For more details about how subdivisions
1609
are managed when stacking, see
1610
:meth:`sage.matrix.matrix1.Matrix.stack`. ::
1611
1612
sage: A = matrix(GF(2), 3, 5, range(15))
1613
sage: B = matrix(GF(2), 1, 5, range(5))
1614
sage: A.stack(B, subdivide=True)
1615
[0 1 0 1 0]
1616
[1 0 1 0 1]
1617
[0 1 0 1 0]
1618
[---------]
1619
[0 1 0 1 0]
1620
1621
TESTS::
1622
1623
sage: A = random_matrix(GF(2),0,3)
1624
sage: B = random_matrix(GF(2),3,3)
1625
sage: A.stack(B)
1626
[0 1 0]
1627
[0 1 1]
1628
[0 0 0]
1629
1630
sage: B.stack(A)
1631
[0 1 0]
1632
[0 1 1]
1633
[0 0 0]
1634
1635
sage: M = Matrix(GF(2), 0, 0, 0)
1636
sage: N = Matrix(GF(2), 19, 0, 0)
1637
sage: W = M.stack(N)
1638
sage: W.nrows()
1639
19
1640
sage: M = Matrix(GF(2), 1, 0, 0)
1641
sage: N = Matrix(GF(2), 1, 0, 0)
1642
sage: M.stack(N)
1643
[]
1644
"""
1645
if hasattr(bottom, '_vector_'):
1646
bottom = bottom.row()
1647
cdef Matrix_mod2_dense other = bottom
1648
1649
if self._ncols != other._ncols:
1650
raise TypeError("Both numbers of columns must match.")
1651
1652
if self._nrows == 0:
1653
return other.__copy__()
1654
if other._nrows == 0:
1655
return self.__copy__()
1656
1657
cdef Matrix_mod2_dense Z
1658
Z = self.new_matrix(nrows = self._nrows + other._nrows)
1659
if self._ncols == 0:
1660
return Z
1661
Z._entries = mzd_stack(Z._entries, self._entries, other._entries)
1662
if subdivide:
1663
Z._subdivide_on_stack(self, other)
1664
return Z
1665
1666
def submatrix(self, lowr, lowc, nrows , ncols):
1667
"""
1668
Return submatrix from the index lowr,lowc (inclusive) with
1669
dimension nrows x ncols.
1670
1671
INPUT:
1672
lowr -- index of start row
1673
lowc -- index of start column
1674
nrows -- number of rows of submatrix
1675
ncols -- number of columns of submatrix
1676
1677
EXAMPLES::
1678
1679
sage: A = random_matrix(GF(2),200,200)
1680
sage: A[0:2,0:2] == A.submatrix(0,0,2,2)
1681
True
1682
sage: A[0:100,0:100] == A.submatrix(0,0,100,100)
1683
True
1684
sage: A == A.submatrix(0,0,200,200)
1685
True
1686
1687
sage: A[1:3,1:3] == A.submatrix(1,1,2,2)
1688
True
1689
sage: A[1:100,1:100] == A.submatrix(1,1,99,99)
1690
True
1691
sage: A[1:200,1:200] == A.submatrix(1,1,199,199)
1692
True
1693
"""
1694
cdef Matrix_mod2_dense A
1695
1696
cdef int highr, highc
1697
1698
highr = lowr + nrows
1699
highc = lowc + ncols
1700
1701
if nrows <= 0 or ncols <= 0:
1702
raise TypeError("Expected nrows, ncols to be > 0, but got %d,%d instead."%(nrows, ncols))
1703
1704
if highc > self._entries.ncols:
1705
raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead."%(highc, self._entries.ncols))
1706
1707
if highr > self._entries.nrows:
1708
raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead."%(highr, self._entries.nrows))
1709
1710
if lowr < 0:
1711
raise TypeError("Expected lowr >= 0, but got %d instead."%lowr)
1712
1713
if lowc < 0:
1714
raise TypeError("Expected lowc >= 0, but got %d instead."%lowc)
1715
1716
A = self.new_matrix(nrows = nrows, ncols = ncols)
1717
if self._ncols == 0 or self._nrows == 0:
1718
return A
1719
A._entries = mzd_submatrix(A._entries, self._entries, lowr, lowc, highr, highc)
1720
return A
1721
1722
def __reduce__(self):
1723
"""
1724
Serialize ``self``.
1725
1726
EXAMPLE::
1727
1728
sage: A = random_matrix(GF(2),10,10)
1729
sage: f,s = A.__reduce__()
1730
sage: f(*s) == A
1731
True
1732
"""
1733
cdef int i,j, r,c, size
1734
1735
r, c = self.nrows(), self.ncols()
1736
if r == 0 or c == 0:
1737
return unpickle_matrix_mod2_dense_v1, (r, c, None, 0)
1738
1739
sig_on()
1740
cdef gdImagePtr im = gdImageCreate(c, r)
1741
sig_off()
1742
cdef int black = gdImageColorAllocate(im, 0, 0, 0)
1743
cdef int white = gdImageColorAllocate(im, 255, 255, 255)
1744
gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)
1745
for i from 0 <= i < r:
1746
for j from 0 <= j < c:
1747
if mzd_read_bit(self._entries, i, j):
1748
gdImageSetPixel(im, j, i, black )
1749
1750
cdef signed char *buf = <signed char*>gdImagePngPtr(im, &size)
1751
1752
data = [buf[i] for i in range(size)]
1753
gdFree(buf)
1754
gdImageDestroy(im)
1755
return unpickle_matrix_mod2_dense_v1, (r,c, data, size)
1756
1757
cpdef _export_as_string(self):
1758
"""
1759
Return space separated string of the entries in this matrix.
1760
1761
EXAMPLES:
1762
sage: w = matrix(GF(2),2,3,[1,0,1,1,1,0])
1763
sage: w._export_as_string()
1764
'1 0 1 1 1 0'
1765
"""
1766
cdef Py_ssize_t i, j, k, n
1767
cdef char *s, *t
1768
1769
if self._nrows == 0 or self._ncols == 0:
1770
data = ''
1771
else:
1772
n = self._nrows*self._ncols*2 + 2
1773
s = <char*> sage_malloc(n * sizeof(char))
1774
k = 0
1775
sig_on()
1776
for i in range(self._nrows):
1777
for j in range(self._ncols):
1778
s[k] = <char>(48 + (1 if mzd_read_bit(self._entries,i,j) else 0)) # "0" or "1"
1779
k += 1
1780
s[k] = <char>32 # space
1781
k += 1
1782
sig_off()
1783
s[k-1] = <char>0
1784
data = str(s)
1785
sage_free(s)
1786
return data
1787
1788
def density(self, approx=False):
1789
"""
1790
Return the density of this matrix.
1791
1792
By density we understand the ration of the number of nonzero
1793
positions and the self.nrows() * self.ncols(), i.e. the number
1794
of possible nonzero positions.
1795
1796
INPUT:
1797
approx -- return floating point approximation (default: False)
1798
1799
EXAMPLE::
1800
1801
sage: A = random_matrix(GF(2),1000,1000)
1802
sage: d = A.density(); d
1803
62483/125000
1804
1805
sage: float(d)
1806
0.499864
1807
1808
sage: A.density(approx=True)
1809
0.499864000...
1810
1811
sage: float(len(A.nonzero_positions())/1000^2)
1812
0.499864
1813
"""
1814
if approx:
1815
from sage.rings.real_mpfr import create_RealNumber
1816
return create_RealNumber(mzd_density(self._entries, 1))
1817
else:
1818
return matrix_dense.Matrix_dense.density(self)
1819
1820
def rank(self, algorithm='pls'):
1821
"""
1822
Return the rank of this matrix.
1823
1824
On average 'pls' should be faster than 'm4ri' and hence it is
1825
the default choice. However, for small - i.e. quite few
1826
thousand rows & columns - and sparse matrices 'm4ri' might be
1827
a better choice.
1828
1829
INPUT:
1830
1831
- ``algorithm`` - either "pls" or "m4ri"
1832
1833
EXAMPLE::
1834
1835
sage: A = random_matrix(GF(2), 1000, 1000)
1836
sage: A.rank()
1837
999
1838
1839
sage: A = matrix(GF(2),10, 0)
1840
sage: A.rank()
1841
0
1842
"""
1843
x = self.fetch('rank')
1844
if not x is None:
1845
return x
1846
if self._nrows == 0 or self._ncols == 0:
1847
return 0
1848
cdef mzd_t *A = mzd_copy(NULL, self._entries)
1849
cdef mzp_t *P, *Q
1850
1851
if algorithm == 'pls':
1852
P = mzp_init(self._entries.nrows)
1853
Q = mzp_init(self._entries.ncols)
1854
r = mzd_pls(A, P, Q, 0)
1855
mzp_free(P)
1856
mzp_free(Q)
1857
elif algorithm == 'm4ri':
1858
r = mzd_echelonize_m4ri(A, 0, 0)
1859
else:
1860
raise ValueError("Algorithm '%s' unknown."%algorithm)
1861
mzd_free(A)
1862
self.cache('rank', r)
1863
return r
1864
1865
def _right_kernel_matrix(self, **kwds):
1866
r"""
1867
Returns a pair that includes a matrix of basis vectors
1868
for the right kernel of ``self``.
1869
1870
INPUT:
1871
1872
- ``kwds`` - these are provided for consistency with other versions
1873
of this method. Here they are ignored as there is no optional
1874
behavior available.
1875
1876
OUTPUT:
1877
1878
Returns a pair. First item is the string 'computed-pluq'
1879
that identifies the nature of the basis vectors.
1880
1881
Second item is a matrix whose rows are a basis for the right kernel,
1882
over the integers mod 2, as computed by the M4RI library
1883
using PLUQ matrix decomposition.
1884
1885
EXAMPLES::
1886
1887
sage: A = matrix(GF(2), [
1888
... [0, 1, 0, 0, 1, 0, 1, 1],
1889
... [1, 0, 1, 0, 0, 1, 1, 0],
1890
... [0, 0, 1, 0, 0, 1, 0, 1],
1891
... [1, 0, 1, 1, 0, 1, 1, 0],
1892
... [0, 0, 1, 0, 0, 1, 0, 1],
1893
... [1, 1, 0, 1, 1, 0, 0, 0]])
1894
sage: A
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: result = A._right_kernel_matrix()
1902
sage: result[0]
1903
'computed-pluq'
1904
sage: result[1]
1905
[0 1 0 0 1 0 0 0]
1906
[0 0 1 0 0 1 0 0]
1907
[1 1 0 0 0 0 1 0]
1908
[1 1 1 0 0 0 0 1]
1909
sage: X = result[1].transpose()
1910
sage: A*X == zero_matrix(GF(2), 6, 4)
1911
True
1912
1913
TESTS:
1914
1915
We test the three trivial cases. Matrices with no rows or columns will
1916
cause segfaults in the M4RI code, so we protect against that instance.
1917
Computing a kernel or a right kernel matrix should never pass these
1918
problem matrices here. ::
1919
1920
sage: A = matrix(GF(2), 0, 2)
1921
sage: A._right_kernel_matrix()
1922
Traceback (most recent call last):
1923
...
1924
ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed
1925
sage: A = matrix(GF(2), 2, 0)
1926
sage: A._right_kernel_matrix()
1927
Traceback (most recent call last):
1928
...
1929
ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed
1930
sage: A = zero_matrix(GF(2), 4, 3)
1931
sage: A._right_kernel_matrix()[1]
1932
[1 0 0]
1933
[0 1 0]
1934
[0 0 1]
1935
"""
1936
tm = verbose("computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1)
1937
if self.nrows()==0 or self.ncols()==0:
1938
raise ValueError("kernels of matrices mod 2 with zero rows or zero columns cannot be computed")
1939
cdef Matrix_mod2_dense M
1940
cdef mzd_t *A = mzd_copy(NULL, self._entries)
1941
# Despite the name, this next call returns X such that M*X = 0
1942
cdef mzd_t *k = mzd_kernel_left_pluq(A, 0)
1943
mzd_free(A)
1944
if k != NULL:
1945
M = self.new_matrix(nrows = k.ncols, ncols = k.nrows)
1946
mzd_transpose(M._entries, k)
1947
mzd_free(k)
1948
else:
1949
M = self.new_matrix(nrows = 0, ncols = self._ncols)
1950
verbose("done computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1, t=tm)
1951
return 'computed-pluq', M
1952
1953
# Used for hashing
1954
cdef int i, k
1955
cdef unsigned long parity_table[256]
1956
for i from 0 <= i < 256:
1957
parity_table[i] = 1 & ((i) ^ (i>>1) ^ (i>>2) ^ (i>>3) ^
1958
(i>>4) ^ (i>>5) ^ (i>>6) ^ (i>>7))
1959
1960
# gmp's ULONG_PARITY may use special
1961
# assembly instructions, could be faster
1962
cpdef inline unsigned long parity(m4ri_word a):
1963
"""
1964
Returns the parity of the number of bits in a.
1965
1966
EXAMPLES::
1967
1968
sage: from sage.matrix.matrix_mod2_dense import parity
1969
sage: parity(1)
1970
1L
1971
sage: parity(3)
1972
0L
1973
sage: parity(0x10000101011)
1974
1L
1975
"""
1976
if sizeof(m4ri_word) == 8:
1977
a ^= a >> 32
1978
a ^= a >> 16
1979
a ^= a >> 8
1980
return parity_table[a & 0xFF]
1981
1982
cdef inline unsigned long parity_mask(m4ri_word a):
1983
return -parity(a)
1984
1985
1986
def unpickle_matrix_mod2_dense_v1(r, c, data, size):
1987
"""
1988
Deserialize a matrix encoded in the string ``s``.
1989
1990
INPUT:
1991
r -- number of rows of matrix
1992
c -- number of columns of matrix
1993
s -- a string
1994
size -- length of the string s
1995
1996
EXAMPLE::
1997
1998
sage: A = random_matrix(GF(2),100,101)
1999
sage: _,(r,c,s,s2) = A.__reduce__()
2000
sage: from sage.matrix.matrix_mod2_dense import unpickle_matrix_mod2_dense_v1
2001
sage: unpickle_matrix_mod2_dense_v1(r,c,s,s2) == A
2002
True
2003
sage: loads(dumps(A)) == A
2004
True
2005
"""
2006
from sage.matrix.constructor import Matrix
2007
from sage.rings.finite_rings.constructor import FiniteField as GF
2008
2009
cdef int i, j
2010
cdef Matrix_mod2_dense A
2011
2012
A = <Matrix_mod2_dense>Matrix(GF(2),r,c)
2013
if r == 0 or c == 0:
2014
return A
2015
2016
cdef signed char *buf = <signed char*>sage_malloc(size)
2017
for i from 0 <= i < size:
2018
buf[i] = data[i]
2019
2020
sig_on()
2021
cdef gdImagePtr im = gdImageCreateFromPngPtr(size, buf)
2022
sig_off()
2023
2024
sage_free(buf)
2025
2026
if gdImageSX(im) != c or gdImageSY(im) != r:
2027
raise TypeError("Pickled data dimension doesn't match.")
2028
2029
2030
for i from 0 <= i < r:
2031
for j from 0 <= j < c:
2032
mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))
2033
gdImageDestroy(im)
2034
return A
2035
2036
def from_png(filename):
2037
"""
2038
Returns a dense matrix over GF(2) from a 1-bit PNG image read from
2039
``filename``. No attempt is made to verify that the filename string
2040
actually points to a PNG image.
2041
2042
INPUT:
2043
filename -- a string
2044
2045
EXAMPLE::
2046
2047
sage: from sage.matrix.matrix_mod2_dense import from_png, to_png
2048
sage: A = random_matrix(GF(2),10,10)
2049
sage: fn = tmp_filename()
2050
sage: to_png(A, fn)
2051
sage: B = from_png(fn)
2052
sage: A == B
2053
True
2054
"""
2055
from sage.matrix.constructor import Matrix
2056
from sage.rings.finite_rings.constructor import FiniteField as GF
2057
2058
cdef int i,j,r,c
2059
cdef Matrix_mod2_dense A
2060
2061
fn = open(filename,"r") # check filename
2062
fn.close()
2063
2064
cdef FILE *f = fopen(filename, "rb")
2065
sig_on()
2066
cdef gdImagePtr im = gdImageCreateFromPng(f)
2067
sig_off()
2068
2069
c, r = gdImageSX(im), gdImageSY(im)
2070
2071
A = <Matrix_mod2_dense>Matrix(GF(2),r,c)
2072
2073
for i from 0 <= i < r:
2074
for j from 0 <= j < c:
2075
mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))
2076
fclose(f)
2077
gdImageDestroy(im)
2078
return A
2079
2080
def to_png(Matrix_mod2_dense A, filename):
2081
"""
2082
Saves the matrix ``A`` to filename as a 1-bit PNG image.
2083
2084
INPUT:
2085
2086
- ``A`` - a matrix over GF(2)
2087
- ``filename`` - a string for a file in a writable position
2088
2089
EXAMPLE::
2090
2091
sage: from sage.matrix.matrix_mod2_dense import from_png, to_png
2092
sage: A = random_matrix(GF(2),10,10)
2093
sage: fn = tmp_filename()
2094
sage: to_png(A, fn)
2095
sage: B = from_png(fn)
2096
sage: A == B
2097
True
2098
"""
2099
cdef int i,j, r,c
2100
r, c = A.nrows(), A.ncols()
2101
if r == 0 or c == 0:
2102
raise TypeError("Cannot write image with dimensions %d x %d"%(c,r))
2103
fn = open(filename,"w") # check filename
2104
fn.close()
2105
cdef gdImagePtr im = gdImageCreate(c, r)
2106
cdef FILE * out = fopen(filename, "wb")
2107
cdef int black = gdImageColorAllocate(im, 0, 0, 0)
2108
cdef int white = gdImageColorAllocate(im, 255, 255, 255)
2109
gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)
2110
for i from 0 <= i < r:
2111
for j from 0 <= j < c:
2112
if mzd_read_bit(A._entries, i, j):
2113
gdImageSetPixel(im, j, i, black )
2114
2115
gdImagePng(im, out)
2116
gdImageDestroy(im)
2117
fclose(out)
2118
2119
def pluq(Matrix_mod2_dense A, algorithm="standard", int param=0):
2120
"""
2121
Return PLUQ factorization of A.
2122
2123
INPUT:
2124
A -- matrix
2125
algorithm -- 'standard' asymptotically fast (default)
2126
'mmpf' M4RI inspired
2127
'naive' naive cubic
2128
param -- either k for 'mmpf' is chosen or matrix multiplication
2129
cutoff for 'standard' (default: 0)
2130
2131
EXAMPLE::
2132
2133
sage: from sage.matrix.matrix_mod2_dense import pluq
2134
sage: A = random_matrix(GF(2),4,4); A
2135
[0 1 0 1]
2136
[0 1 1 1]
2137
[0 0 0 1]
2138
[0 1 1 0]
2139
2140
sage: LU, P, Q = pluq(A)
2141
sage: LU
2142
[1 0 1 0]
2143
[1 1 0 0]
2144
[0 0 1 0]
2145
[1 1 1 0]
2146
2147
sage: P
2148
[0, 1, 2, 3]
2149
2150
sage: Q
2151
[1, 2, 3, 3]
2152
"""
2153
cdef Matrix_mod2_dense B = A.__copy__()
2154
cdef mzp_t *p = mzp_init(A._entries.nrows)
2155
cdef mzp_t *q = mzp_init(A._entries.ncols)
2156
2157
if algorithm == "standard":
2158
sig_on()
2159
mzd_pluq(B._entries, p, q, param)
2160
sig_off()
2161
elif algorithm == "mmpf":
2162
sig_on()
2163
_mzd_pluq_mmpf(B._entries, p, q, param)
2164
sig_off()
2165
elif algorithm == "naive":
2166
sig_on()
2167
_mzd_pluq_naive(B._entries, p, q)
2168
sig_off()
2169
else:
2170
raise ValueError("Algorithm '%s' unknown."%algorithm)
2171
2172
P = [p.values[i] for i in range(A.nrows())]
2173
Q = [q.values[i] for i in range(A.ncols())]
2174
mzp_free(p)
2175
mzp_free(q)
2176
return B,P,Q
2177
2178
def pls(Matrix_mod2_dense A, algorithm="standard", int param=0):
2179
"""
2180
Return PLS factorization of A.
2181
2182
INPUT:
2183
A -- matrix
2184
algorithm -- 'standard' asymptotically fast (default)
2185
'mmpf' M4RI inspired
2186
'naive' naive cubic
2187
param -- either k for 'mmpf' is chosen or matrix multiplication
2188
cutoff for 'standard' (default: 0)
2189
2190
EXAMPLE::
2191
2192
sage: from sage.matrix.matrix_mod2_dense import pls
2193
sage: A = random_matrix(GF(2),4,4); A
2194
[0 1 0 1]
2195
[0 1 1 1]
2196
[0 0 0 1]
2197
[0 1 1 0]
2198
2199
sage: LU, P, Q = pls(A)
2200
sage: LU
2201
[1 0 0 1]
2202
[1 1 0 0]
2203
[0 0 1 0]
2204
[1 1 1 0]
2205
2206
sage: P
2207
[0, 1, 2, 3]
2208
2209
sage: Q
2210
[1, 2, 3, 3]
2211
2212
sage: A = random_matrix(GF(2),1000,1000)
2213
sage: pls(A) == pls(A,'mmpf') == pls(A,'naive')
2214
True
2215
"""
2216
cdef Matrix_mod2_dense B = A.__copy__()
2217
cdef mzp_t *p = mzp_init(A._entries.nrows)
2218
cdef mzp_t *q = mzp_init(A._entries.ncols)
2219
2220
if algorithm == 'standard':
2221
sig_on()
2222
mzd_pls(B._entries, p, q, param)
2223
sig_off()
2224
elif algorithm == "mmpf":
2225
sig_on()
2226
_mzd_pls_mmpf(B._entries, p, q, param)
2227
sig_off()
2228
elif algorithm == "naive":
2229
sig_on()
2230
_mzd_pls_naive(B._entries, p, q)
2231
sig_off()
2232
else:
2233
raise ValueError("Algorithm '%s' unknown."%algorithm)
2234
2235
P = [p.values[i] for i in range(A.nrows())]
2236
Q = [q.values[i] for i in range(A.ncols())]
2237
mzp_free(p)
2238
mzp_free(q)
2239
return B,P,Q
2240
2241