Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matrix/matrix_mod2e_dense.pyx
8815 views
1
"""
2
Dense matrices over `\GF{2^e}` for `2 <= e <= 10` using the M4RIE library.
3
4
The M4RIE library offers two matrix representations:
5
6
1) ``mzed_t``
7
8
m x n matrices over `\GF{2^e}` are internally represented roughly as
9
m x (en) matrices over `\GF{2}`. Several elements are packed into
10
words such that each element is filled with zeroes until the next
11
power of two. Thus, for example, elements of `\GF{2^3}` are
12
represented as ``[0xxx|0xxx|0xxx|0xxx|...]``. This representation is
13
wrapped as :class:`Matrix_mod2e_dense` in Sage.
14
15
Multiplication and elimination both use "Newton-John" tables. These
16
tables are simply all possible multiples of a given row in a matrix
17
such that a scale+add operation is reduced to a table lookup +
18
add. On top of Newton-John multiplication M4RIE implements
19
asymptotically fast Strassen-Winograd multiplication. Elimination
20
uses simple Gaussian elimination which requires `O(n^3)` additions
21
but only `O(n^2 * 2^e)` multiplications.
22
23
2) ``mzd_slice_t``
24
25
m x n matrices over `\GF{2^e}` are internally represented as slices
26
of m x n matrices over `\GF{2}`. This representation allows for very
27
fast matrix times matrix products using Karatsuba's polynomial
28
multiplication for polynomials over matrices. However, it is not
29
feature complete yet and hence not wrapped in Sage for now.
30
31
See http://m4ri.sagemath.org for more details on the M4RIE library.
32
33
EXAMPLE::
34
35
sage: K.<a> = GF(2^8)
36
sage: A = random_matrix(K, 3,4)
37
sage: A
38
[ a^6 + a^5 + a^4 + a^2 a^6 + a^3 + a + 1 a^5 + a^3 + a^2 + a + 1 a^7 + a^6 + a + 1]
39
[ a^7 + a^6 + a^3 a^7 + a^6 + a^5 + 1 a^5 + a^4 + a^3 + a + 1 a^6 + a^5 + a^4 + a^3 + a^2 + 1]
40
[ a^6 + a^5 + a + 1 a^7 + a^3 + 1 a^7 + a^3 + a + 1 a^7 + a^6 + a^3 + a^2 + a + 1]
41
42
sage: A.echelon_form()
43
[ 1 0 0 a^6 + a^5 + a^4 + a^2]
44
[ 0 1 0 a^7 + a^5 + a^3 + a + 1]
45
[ 0 0 1 a^6 + a^4 + a^3 + a^2 + 1]
46
47
AUTHOR:
48
49
* Martin Albrecht <[email protected]>
50
51
TESTS::
52
53
sage: TestSuite(sage.matrix.matrix_mod2e_dense.Matrix_mod2e_dense).run(verbose=True)
54
running ._test_pickling() . . . pass
55
56
TODO:
57
58
- wrap ``mzd_slice_t``
59
60
61
REFERENCES:
62
63
.. [BB09] Tomas J. Boothby and Robert W. Bradshaw. *Bitslicing
64
and the Method of Four Russians Over Larger Finite Fields*. arXiv:0901.1413v1,
65
2009. http://arxiv.org/abs/0901.1413
66
"""
67
68
include "sage/ext/interrupt.pxi"
69
include "sage/ext/cdefs.pxi"
70
include 'sage/ext/stdsage.pxi'
71
include 'sage/ext/random.pxi'
72
73
cimport matrix_dense
74
from sage.structure.element cimport Matrix, Vector
75
from sage.structure.element cimport ModuleElement, Element, RingElement
76
77
from sage.rings.all import FiniteField as GF
78
from sage.misc.randstate cimport randstate, current_randstate
79
80
from sage.matrix.matrix_mod2_dense cimport Matrix_mod2_dense
81
82
from sage.libs.m4ri cimport m4ri_word, mzd_copy, mzd_init
83
from sage.libs.m4rie cimport *
84
from sage.libs.m4rie cimport mzed_t
85
86
87
# we must keep a copy of the internal finite field representation
88
# around to avoid re-creating it over and over again. Furthermore,
89
# M4RIE assumes pointer equivalence of identical fields.
90
91
_m4rie_finite_field_cache = {}
92
93
cdef class M4RIE_finite_field:
94
"""
95
A thin wrapper around the M4RIE finite field class such that we
96
can put it in a hash table. This class is not meant for public
97
consumption.
98
"""
99
cdef gf2e *ff
100
101
def __cinit__(self):
102
"""
103
EXAMPLE::
104
105
sage: from sage.matrix.matrix_mod2e_dense import M4RIE_finite_field
106
sage: K = M4RIE_finite_field(); K
107
<sage.matrix.matrix_mod2e_dense.M4RIE_finite_field object at 0x...>
108
"""
109
pass
110
111
def __dealloc__(self):
112
"""
113
EXAMPLE::
114
115
sage: from sage.matrix.matrix_mod2e_dense import M4RIE_finite_field
116
sage: K = M4RIE_finite_field()
117
sage: del K
118
"""
119
if self.ff:
120
gf2e_free(self.ff)
121
122
cdef m4ri_word poly_to_word(f):
123
return f.integer_representation()
124
125
cdef object word_to_poly(w, F):
126
return F.fetch_int(w)
127
128
cdef class Matrix_mod2e_dense(matrix_dense.Matrix_dense):
129
########################################################################
130
# LEVEL 1 functionality
131
########################################################################
132
def __cinit__(self, parent, entries, copy, coerce, alloc=True):
133
"""
134
Create new matrix over `GF(2^e)` for 2<=e<=10.
135
136
INPUT:
137
138
- ``parent`` - a :class:`MatrixSpace`.
139
- ``entries`` - may be list or a finite field element.
140
- ``copy`` - ignored, elements are always copied
141
- ``coerce`` - ignored, elements are always coerced
142
- ``alloc`` - if ``True`` the matrix is allocated first (default: ``True``)
143
144
EXAMPLES::
145
146
sage: K.<a> = GF(2^4)
147
sage: A = Matrix(K, 3, 4); A
148
[0 0 0 0]
149
[0 0 0 0]
150
[0 0 0 0]
151
152
sage: A.randomize(); A
153
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
154
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
155
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
156
157
sage: K.<a> = GF(2^3)
158
sage: A = Matrix(K,3,4); A
159
[0 0 0 0]
160
[0 0 0 0]
161
[0 0 0 0]
162
163
sage: A.randomize(); A
164
[ a^2 + a a^2 + 1 a^2 + a a^2 + a]
165
[ a^2 + 1 a^2 + a + 1 a^2 + 1 a^2]
166
[ a^2 + a a^2 + 1 a^2 + a + 1 a + 1]
167
"""
168
matrix_dense.Matrix_dense.__init__(self, parent)
169
170
cdef M4RIE_finite_field FF
171
172
R = parent.base_ring()
173
174
f = R.polynomial()
175
cdef m4ri_word poly = sum(int(c)*2**i for i,c in enumerate(f))
176
177
if alloc and self._nrows and self._ncols:
178
if poly in _m4rie_finite_field_cache:
179
self._entries = mzed_init((<M4RIE_finite_field>_m4rie_finite_field_cache[poly]).ff, self._nrows, self._ncols)
180
else:
181
FF = PY_NEW(M4RIE_finite_field)
182
FF.ff = gf2e_init(poly)
183
self._entries = mzed_init(FF.ff, self._nrows, self._ncols)
184
_m4rie_finite_field_cache[poly] = FF
185
186
# cache elements
187
self._zero = self._base_ring(0)
188
self._one = self._base_ring(1)
189
190
def __dealloc__(self):
191
"""
192
TESTS::
193
194
sage: K.<a> = GF(2^4)
195
sage: A = Matrix(K, 1000, 1000)
196
sage: del A
197
sage: A = Matrix(K, 1000, 1000)
198
sage: del A
199
"""
200
if self._entries:
201
mzed_free(self._entries)
202
self._entries = NULL
203
204
def __init__(self, parent, entries, copy, coerce):
205
"""
206
Create new matrix over `GF(2^e)` for 2<=e<=10.
207
208
INPUT:
209
210
- ``parent`` - a :class:`MatrixSpace`.
211
- ``entries`` - may be list or a finite field element.
212
- ``copy`` - ignored, elements are always copied
213
- ``coerce`` - ignored, elements are always coerced
214
215
EXAMPLE::
216
217
sage: K.<a> = GF(2^4)
218
sage: l = [K.random_element() for _ in range(3*4)]; l
219
[a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a]
220
221
sage: A = Matrix(K, 3, 4, l); A
222
[ a^2 + 1 a^3 + 1 0 0]
223
[ a a^3 + a + 1 a + 1 a + 1]
224
[ a^2 a^3 + a + 1 a^3 + a a^3 + a]
225
226
sage: A.list()
227
[a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a]
228
229
sage: l[0], A[0,0]
230
(a^2 + 1, a^2 + 1)
231
232
sage: A = Matrix(K, 3, 3, a); A
233
[a 0 0]
234
[0 a 0]
235
[0 0 a]
236
"""
237
cdef int i,j
238
239
if entries is None:
240
return
241
242
R = self.base_ring()
243
244
# scalar ?
245
if not isinstance(entries, list):
246
if entries != 0:
247
if self.nrows() != self.ncols():
248
raise TypeError("self must be a square matrices for scalar assignment")
249
for i in range(self.nrows()):
250
self.set_unsafe(i,i, R(entries))
251
return
252
253
# all entries are given as a long list
254
if len(entries) != self._nrows * self._ncols:
255
raise IndexError("The vector of entries has the wrong length.")
256
257
k = 0
258
259
for i from 0 <= i < self._nrows:
260
sig_check()
261
for j from 0 <= j < self._ncols:
262
e = R(entries[k])
263
mzed_write_elem(self._entries, i, j, poly_to_word(e))
264
k = k + 1
265
266
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
267
"""
268
A[i,j] = value without bound checks
269
270
INPUT:
271
- ``i`` - row index
272
- ``j`` - column index
273
- ``value`` - a finite field element (not checked but assumed)
274
275
EXAMPLE::
276
277
sage: K.<a> = GF(2^4)
278
sage: A = Matrix(K,3,4,[K.random_element() for _ in range(3*4)]); A
279
[ a^2 + 1 a^3 + 1 0 0]
280
[ a a^3 + a + 1 a + 1 a + 1]
281
[ a^2 a^3 + a + 1 a^3 + a a^3 + a]
282
283
sage: A[0,0] = a # indirect doctest
284
sage: A
285
[ a a^3 + 1 0 0]
286
[ a a^3 + a + 1 a + 1 a + 1]
287
[ a^2 a^3 + a + 1 a^3 + a a^3 + a]
288
"""
289
mzed_write_elem(self._entries, i, j, poly_to_word(value))
290
291
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
292
"""
293
Get A[i,j] without bound checks.
294
295
INPUT:
296
- ``i`` - row index
297
- ``j`` - column index
298
299
EXAMPLE::
300
301
sage: K.<a> = GF(2^4)
302
sage: A = random_matrix(K,3,4)
303
sage: A[2,3] # indirect doctest
304
a^3 + a^2 + a + 1
305
sage: K.<a> = GF(2^3)
306
sage: m,n = 3, 4
307
sage: A = random_matrix(K,3,4); A
308
[ a^2 + a a^2 + 1 a^2 + a a^2 + a]
309
[ a^2 + 1 a^2 + a + 1 a^2 + 1 a^2]
310
[ a^2 + a a^2 + 1 a^2 + a + 1 a + 1]
311
"""
312
cdef int r = mzed_read_elem(self._entries, i, j)
313
return word_to_poly(r, self._base_ring)
314
315
cpdef ModuleElement _add_(self, ModuleElement right):
316
"""
317
Return A+B
318
319
INPUT:
320
321
- ``right`` - a matrix
322
323
EXAMPLE::
324
325
sage: K.<a> = GF(2^4)
326
sage: A = random_matrix(K,3,4); A
327
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
328
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
329
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
330
331
sage: B = random_matrix(K,3,4); B
332
[ a^2 + a a^2 + 1 a^2 + a a^3 + a^2 + a]
333
[ a^2 + 1 a^3 + a^2 + a + 1 a^2 + 1 a^2]
334
[ a^3 + a^2 + a a^2 + 1 a^2 + a + 1 a^3 + a + 1]
335
336
sage: C = A + B; C # indirect doctest
337
[ a a^3 + a^2 + a a^3 + 1 a^3 + a^2 + 1]
338
[a^3 + a^2 + 1 a^3 + a^2 + a a^3 + a^2 + a a^3 + 1]
339
[a^3 + a^2 + 1 a^3 + a^2 a^3 + a^2 a^2]
340
"""
341
cdef Matrix_mod2e_dense A
342
A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0, alloc=False)
343
if self._nrows == 0 or self._ncols == 0:
344
return A
345
A._entries = mzed_add(NULL, self._entries, (<Matrix_mod2e_dense>right)._entries)
346
347
return A
348
349
cpdef ModuleElement _sub_(self, ModuleElement right):
350
"""
351
EXAMPLE::
352
353
sage: from sage.matrix.matrix_mod2e_dense import Matrix_mod2e_dense
354
sage: K.<a> = GF(2^4)
355
sage: m,n = 3, 4
356
sage: MS = MatrixSpace(K,m,n)
357
sage: A = random_matrix(K,3,4); A
358
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
359
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
360
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
361
362
sage: B = random_matrix(K,3,4); B
363
[ a^2 + a a^2 + 1 a^2 + a a^3 + a^2 + a]
364
[ a^2 + 1 a^3 + a^2 + a + 1 a^2 + 1 a^2]
365
[ a^3 + a^2 + a a^2 + 1 a^2 + a + 1 a^3 + a + 1]
366
367
sage: C = A - B; C # indirect doctest
368
[ a a^3 + a^2 + a a^3 + 1 a^3 + a^2 + 1]
369
[a^3 + a^2 + 1 a^3 + a^2 + a a^3 + a^2 + a a^3 + 1]
370
[a^3 + a^2 + 1 a^3 + a^2 a^3 + a^2 a^2]
371
"""
372
return self._add_(right)
373
374
def _multiply_classical(self, Matrix right):
375
"""
376
Classical cubic matrix multiplication.
377
378
EXAMPLES::
379
380
sage: K.<a> = GF(2^2)
381
sage: A = random_matrix(K, 50, 50)
382
sage: B = random_matrix(K, 50, 50)
383
sage: A*B == A._multiply_classical(B)
384
True
385
386
sage: K.<a> = GF(2^4)
387
sage: A = random_matrix(K, 50, 50)
388
sage: B = random_matrix(K, 50, 50)
389
sage: A*B == A._multiply_classical(B)
390
True
391
392
sage: K.<a> = GF(2^8)
393
sage: A = random_matrix(K, 50, 50)
394
sage: B = random_matrix(K, 50, 50)
395
sage: A*B == A._multiply_classical(B)
396
True
397
398
sage: K.<a> = GF(2^10)
399
sage: A = random_matrix(K, 50, 50)
400
sage: B = random_matrix(K, 50, 50)
401
sage: A*B == A._multiply_classical(B)
402
True
403
404
.. note::
405
406
This function is very slow. Use ``*`` operator instead.
407
"""
408
if self._ncols != right._nrows:
409
raise ArithmeticError("left ncols must match right nrows")
410
411
cdef Matrix_mod2e_dense ans
412
413
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
414
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
415
return ans
416
sig_on()
417
ans._entries = mzed_mul_naive(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)
418
sig_off()
419
return ans
420
421
cdef Matrix _matrix_times_matrix_(self, Matrix right):
422
"""
423
Return A*B
424
425
Uses the M4RIE machinery to decide which function to call.
426
427
INPUT:
428
429
- ``right`` - a matrix
430
431
EXAMPLES::
432
433
sage: K.<a> = GF(2^3)
434
sage: A = random_matrix(K, 51, 50)
435
sage: B = random_matrix(K, 50, 52)
436
sage: A*B == A._multiply_newton_john(B) # indirect doctest
437
True
438
439
sage: K.<a> = GF(2^5)
440
sage: A = random_matrix(K, 10, 50)
441
sage: B = random_matrix(K, 50, 12)
442
sage: A*B == A._multiply_newton_john(B)
443
True
444
445
sage: K.<a> = GF(2^7)
446
sage: A = random_matrix(K,100, 50)
447
sage: B = random_matrix(K, 50, 17)
448
sage: A*B == A._multiply_classical(B)
449
True
450
"""
451
if self._ncols != right._nrows:
452
raise ArithmeticError("left ncols must match right nrows")
453
454
cdef Matrix_mod2e_dense ans
455
456
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
457
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
458
return ans
459
sig_on()
460
ans._entries = mzed_mul(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)
461
sig_off()
462
return ans
463
464
cpdef Matrix_mod2e_dense _multiply_newton_john(Matrix_mod2e_dense self, Matrix_mod2e_dense right):
465
"""
466
Return A*B using Newton-John tables.
467
468
We can write classical cubic multiplication (``C=A*B``) as::
469
470
for i in range(A.ncols()):
471
for j in range(A.nrows()):
472
C[j] += A[j,i] * B[j]
473
474
Hence, in the inner-most loop we compute multiples of ``B[j]``
475
by the values ``A[j,i]``. If the matrix ``A`` is big and the
476
finite field is small, there is a very high chance that
477
``e * B[j]`` is computed more than once for any ``e`` in the finite
478
field. Instead, we compute all possible
479
multiples of ``B[j]`` and re-use this data in the inner loop.
480
This is what is called a "Newton-John" table in M4RIE.
481
482
INPUT:
483
484
- ``right`` - a matrix
485
486
EXAMPLES::
487
488
sage: K.<a> = GF(2^2)
489
sage: A = random_matrix(K, 50, 50)
490
sage: B = random_matrix(K, 50, 50)
491
sage: A._multiply_newton_john(B) == A._multiply_classical(B) # indirect doctest
492
True
493
494
sage: K.<a> = GF(2^4)
495
sage: A = random_matrix(K, 50, 50)
496
sage: B = random_matrix(K, 50, 50)
497
sage: A._multiply_newton_john(B) == A._multiply_classical(B)
498
True
499
500
sage: K.<a> = GF(2^8)
501
sage: A = random_matrix(K, 50, 50)
502
sage: B = random_matrix(K, 50, 50)
503
sage: A._multiply_newton_john(B) == A._multiply_classical(B)
504
True
505
506
sage: K.<a> = GF(2^10)
507
sage: A = random_matrix(K, 50, 50)
508
sage: B = random_matrix(K, 50, 50)
509
sage: A._multiply_newton_john(B) == A._multiply_classical(B)
510
True
511
"""
512
if self._ncols != right._nrows:
513
raise ArithmeticError("left ncols must match right nrows")
514
515
cdef Matrix_mod2e_dense ans
516
517
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
518
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
519
return ans
520
521
sig_on()
522
ans._entries = mzed_mul_newton_john(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)
523
sig_off()
524
return ans
525
526
cpdef Matrix_mod2e_dense _multiply_karatsuba(Matrix_mod2e_dense self, Matrix_mod2e_dense right):
527
"""
528
Matrix multiplication using Karatsuba over polynomials with
529
matrix coefficients over GF(2).
530
531
The idea behind Karatsuba multiplication for matrices over
532
`\GF{p^n}` is to treat these matrices as polynomials with
533
coefficients of matrices over `\GF{p}`. Then, Karatsuba-style
534
formulas can be used to perform multiplication, cf. [BB09]_.
535
536
INPUT:
537
538
- ``right`` - a matrix
539
540
EXAMPLES::
541
542
sage: K.<a> = GF(2^2)
543
sage: A = random_matrix(K, 50, 50)
544
sage: B = random_matrix(K, 50, 50)
545
sage: A._multiply_karatsuba(B) == A._multiply_classical(B) # indirect doctest
546
True
547
548
sage: K.<a> = GF(2^2)
549
sage: A = random_matrix(K, 137, 11)
550
sage: B = random_matrix(K, 11, 23)
551
sage: A._multiply_karatsuba(B) == A._multiply_classical(B)
552
True
553
554
sage: K.<a> = GF(2^10)
555
sage: A = random_matrix(K, 50, 50)
556
sage: B = random_matrix(K, 50, 50)
557
sage: A._multiply_karatsuba(B) == A._multiply_classical(B)
558
True
559
"""
560
if self._ncols != right._nrows:
561
raise ArithmeticError("left ncols must match right nrows")
562
563
cdef Matrix_mod2e_dense ans
564
565
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
566
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
567
return ans
568
569
sig_on()
570
ans._entries = mzed_mul_karatsuba(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)
571
sig_off()
572
return ans
573
574
cpdef Matrix_mod2e_dense _multiply_strassen(Matrix_mod2e_dense self, Matrix_mod2e_dense right, cutoff=0):
575
"""
576
Winograd-Strassen matrix multiplication with Newton-John
577
multiplication as base case.
578
579
INPUT:
580
581
- ``right`` - a matrix
582
- ``cutoff`` - row or column dimension to switch over to
583
Newton-John multiplication (default: 64)
584
585
EXAMPLES::
586
587
sage: K.<a> = GF(2^2)
588
sage: A = random_matrix(K, 50, 50)
589
sage: B = random_matrix(K, 50, 50)
590
sage: A._multiply_strassen(B) == A._multiply_classical(B) # indirect doctest
591
True
592
593
sage: K.<a> = GF(2^4)
594
sage: A = random_matrix(K, 50, 50)
595
sage: B = random_matrix(K, 50, 50)
596
sage: A._multiply_strassen(B) == A._multiply_classical(B)
597
True
598
599
sage: K.<a> = GF(2^8)
600
sage: A = random_matrix(K, 50, 50)
601
sage: B = random_matrix(K, 50, 50)
602
sage: A._multiply_strassen(B) == A._multiply_classical(B)
603
True
604
605
sage: K.<a> = GF(2^10)
606
sage: A = random_matrix(K, 50, 50)
607
sage: B = random_matrix(K, 50, 50)
608
sage: A._multiply_strassen(B) == A._multiply_classical(B)
609
True
610
"""
611
if self._ncols != right._nrows:
612
raise ArithmeticError("left ncols must match right nrows")
613
614
cdef Matrix_mod2e_dense ans
615
616
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
617
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
618
return ans
619
620
if cutoff == 0:
621
cutoff = _mzed_strassen_cutoff(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)
622
623
sig_on()
624
ans._entries = mzed_mul_strassen(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries, cutoff)
625
sig_off()
626
return ans
627
628
cpdef ModuleElement _lmul_(self, RingElement right):
629
"""
630
Return ``a*B`` for ``a`` an element of the base field.
631
632
INPUT:
633
634
- ``right`` - an element of the base field
635
636
EXAMPLES::
637
638
sage: K.<a> = GF(4)
639
sage: A = random_matrix(K,10,10)
640
sage: A
641
[ 0 a + 1 a + 1 a + 1 0 1 a + 1 1 a + 1 1]
642
[a + 1 a + 1 a 1 a a 1 a + 1 1 0]
643
[ a 1 a + 1 a + 1 0 a + 1 a 1 a a]
644
[a + 1 a 0 0 1 a + 1 a + 1 0 a + 1 1]
645
[ a 0 a + 1 a a 0 a + 1 a 1 a + 1]
646
[ a 0 a a + 1 a 1 a + 1 a a a]
647
[a + 1 a 0 1 0 a + 1 a + 1 a 0 a + 1]
648
[a + 1 a + 1 0 a + 1 a 1 a + 1 a + 1 a + 1 0]
649
[ 0 0 0 a + 1 1 a + 1 0 a + 1 1 0]
650
[ 1 a + 1 0 1 a 0 0 a a + 1 a]
651
652
sage: a*A # indirect doctest
653
[ 0 1 1 1 0 a 1 a 1 a]
654
[ 1 1 a + 1 a a + 1 a + 1 a 1 a 0]
655
[a + 1 a 1 1 0 1 a + 1 a a + 1 a + 1]
656
[ 1 a + 1 0 0 a 1 1 0 1 a]
657
[a + 1 0 1 a + 1 a + 1 0 1 a + 1 a 1]
658
[a + 1 0 a + 1 1 a + 1 a 1 a + 1 a + 1 a + 1]
659
[ 1 a + 1 0 a 0 1 1 a + 1 0 1]
660
[ 1 1 0 1 a + 1 a 1 1 1 0]
661
[ 0 0 0 1 a 1 0 1 a 0]
662
[ a 1 0 a a + 1 0 0 a + 1 1 a + 1]
663
"""
664
cdef m4ri_word a = poly_to_word(right)
665
cdef Matrix_mod2e_dense C = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0)
666
mzed_mul_scalar(C._entries, a, self._entries)
667
return C
668
669
def __neg__(self):
670
"""
671
EXAMPLE::
672
673
sage: K.<a> = GF(2^4)
674
sage: A = random_matrix(K, 3, 4); A
675
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
676
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
677
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
678
679
sage: -A
680
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
681
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
682
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
683
"""
684
return self.__copy__()
685
686
def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.
687
"""
688
EXAMPLE::
689
690
sage: K.<a> = GF(2^4)
691
sage: A = random_matrix(K,3,4)
692
sage: B = copy(A)
693
sage: A == B
694
True
695
sage: A[0,0] = a
696
sage: A == B
697
False
698
"""
699
return self._richcmp(right, op)
700
701
cdef int _cmp_c_impl(self, Element right) except -2:
702
if self._nrows == 0 or self._ncols == 0:
703
return 0
704
return mzed_cmp(self._entries, (<Matrix_mod2e_dense>right)._entries)
705
706
def __copy__(self):
707
"""
708
EXAMPLE::
709
710
sage: K.<a> = GF(2^4)
711
sage: m,n = 3, 4
712
sage: A = random_matrix(K,3,4); A
713
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
714
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
715
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
716
717
sage: A2 = copy(A); A2
718
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
719
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
720
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
721
722
sage: A[0,0] = 1
723
sage: A2[0,0]
724
a^2
725
"""
726
cdef Matrix_mod2e_dense A
727
A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0)
728
729
if self._nrows and self._ncols:
730
mzed_copy(A._entries, <const_mzed_t *>self._entries)
731
732
return A
733
734
def _list(self):
735
"""
736
EXAMPLE::
737
738
sage: K.<a> = GF(2^4)
739
sage: m,n = 3, 4
740
sage: A = random_matrix(K,3,4); A
741
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]
742
[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]
743
[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
744
745
sage: A.list() # indirect doctest
746
[a^2, a^3 + a + 1, a^3 + a^2 + a + 1, a + 1, a^3, 1, a^3 + a + 1, a^3 + a^2 + 1, a + 1, a^3 + 1, a^3 + a + 1, a^3 + a^2 + a + 1]
747
"""
748
cdef int i,j
749
l = []
750
for i from 0 <= i < self._nrows:
751
for j from 0 <= j < self._ncols:
752
l.append(self.get_unsafe(i,j))
753
return l
754
755
def randomize(self, density=1, nonzero=False, *args, **kwds):
756
"""
757
Randomize ``density`` proportion of the entries of this matrix,
758
leaving the rest unchanged.
759
760
INPUT:
761
762
- ``density`` - float; proportion (roughly) to be considered for
763
changes
764
- ``nonzero`` - Bool (default: ``False``); whether the new entries
765
are forced to be non-zero
766
767
OUTPUT:
768
769
- None, the matrix is modified in-place
770
771
EXAMPLE::
772
773
sage: K.<a> = GF(2^4)
774
sage: A = Matrix(K,3,3)
775
776
sage: A.randomize(); A
777
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1]
778
[ a + 1 a^3 1]
779
[ a^3 + a + 1 a^3 + a^2 + 1 a + 1]
780
781
sage: K.<a> = GF(2^4)
782
sage: A = random_matrix(K,1000,1000,density=0.1)
783
sage: float(A.density())
784
0.0999...
785
786
sage: A = random_matrix(K,1000,1000,density=1.0)
787
sage: float(A.density())
788
1.0
789
790
sage: A = random_matrix(K,1000,1000,density=0.5)
791
sage: float(A.density())
792
0.4996...
793
794
Note, that the matrix is updated and not zero-ed out before
795
being randomized::
796
797
sage: A = matrix(K,1000,1000)
798
sage: A.randomize(nonzero=False, density=0.1)
799
sage: float(A.density())
800
0.0936...
801
802
sage: A.randomize(nonzero=False, density=0.05)
803
sage: float(A.density())
804
0.135854
805
"""
806
if self._ncols == 0 or self._nrows == 0:
807
return
808
809
cdef Py_ssize_t i,j
810
self.check_mutability()
811
self.clear_cache()
812
813
cdef m4ri_word mask = (1<<(self._parent.base_ring().degree())) - 1
814
815
cdef randstate rstate = current_randstate()
816
K = self._parent.base_ring()
817
818
if self._ncols == 0 or self._nrows == 0:
819
return
820
821
cdef float _density = density
822
if _density <= 0:
823
return
824
if _density > 1:
825
_density = 1.0
826
827
if _density == 1:
828
if nonzero == False:
829
sig_on()
830
for i in range(self._nrows):
831
for j in range(self._ncols):
832
tmp = rstate.c_random() & mask
833
mzed_write_elem(self._entries, i, j, tmp)
834
sig_off()
835
else:
836
sig_on()
837
for i in range(self._nrows):
838
for j in range(self._ncols):
839
tmp = rstate.c_random() & mask
840
while tmp == 0:
841
tmp = rstate.c_random() & mask
842
mzed_write_elem(self._entries, i, j, tmp)
843
sig_off()
844
else:
845
if nonzero == False:
846
sig_on()
847
for i in range(self._nrows):
848
for j in range(self._ncols):
849
if rstate.c_rand_double() <= _density:
850
tmp = rstate.c_random() & mask
851
mzed_write_elem(self._entries, i, j, tmp)
852
sig_off()
853
else:
854
sig_on()
855
for i in range(self._nrows):
856
for j in range(self._ncols):
857
if rstate.c_rand_double() <= _density:
858
tmp = rstate.c_random() & mask
859
while tmp == 0:
860
tmp = rstate.c_random() & mask
861
mzed_write_elem(self._entries, i, j, tmp)
862
sig_off()
863
864
def echelonize(self, algorithm='heuristic', reduced=True, **kwds):
865
"""
866
Compute the row echelon form of ``self`` in place.
867
868
INPUT:
869
870
- ``algorithm`` - one of the following
871
- ``heuristic`` - let M4RIE decide (default)
872
- ``newton_john`` - use newton_john table based algorithm
873
- ``ple`` - use PLE decomposition
874
- ``naive`` - use naive cubic Gaussian elimination (M4RIE implementation)
875
- ``builtin`` - use naive cubic Gaussian elimination (Sage implementation)
876
- ``reduced`` - if ``True`` return reduced echelon form. No
877
guarantee is given that the matrix is *not* reduced if
878
``False`` (default: ``True``)
879
880
EXAMPLE::
881
882
sage: K.<a> = GF(2^4)
883
sage: m,n = 3, 5
884
sage: A = random_matrix(K, 3, 5); A
885
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1 a^3]
886
[ 1 a^3 + a + 1 a^3 + a^2 + 1 a + 1 a^3 + 1]
887
[ a^3 + a + 1 a^3 + a^2 + a + 1 a^2 + a a^2 + 1 a^2 + a]
888
889
sage: A.echelonize(); A
890
[ 1 0 0 a + 1 a^2 + 1]
891
[ 0 1 0 a^2 a + 1]
892
[ 0 0 1 a^3 + a^2 + a a^3]
893
894
sage: K.<a> = GF(2^3)
895
sage: m,n = 3, 5
896
sage: MS = MatrixSpace(K,m,n)
897
sage: A = random_matrix(K, 3, 5)
898
899
sage: copy(A).echelon_form('newton_john')
900
[ 1 0 a + 1 0 a^2 + 1]
901
[ 0 1 a^2 + a + 1 0 a]
902
[ 0 0 0 1 a^2 + a + 1]
903
904
sage: copy(A).echelon_form('naive');
905
[ 1 0 a + 1 0 a^2 + 1]
906
[ 0 1 a^2 + a + 1 0 a]
907
[ 0 0 0 1 a^2 + a + 1]
908
909
sage: copy(A).echelon_form('builtin');
910
[ 1 0 a + 1 0 a^2 + 1]
911
[ 0 1 a^2 + a + 1 0 a]
912
[ 0 0 0 1 a^2 + a + 1]
913
"""
914
if self._nrows == 0 or self._ncols == 0:
915
self.cache('in_echelon_form',True)
916
self.cache('rank', 0)
917
self.cache('pivots', [])
918
return self
919
920
cdef int k, n, full
921
922
full = int(reduced)
923
924
x = self.fetch('in_echelon_form')
925
if not x is None: return # already known to be in echelon form
926
927
self.check_mutability()
928
self.clear_cache()
929
930
if algorithm == 'naive':
931
sig_on()
932
r = mzed_echelonize_naive(self._entries, full)
933
sig_off()
934
935
elif algorithm == 'newton_john':
936
sig_on()
937
r = mzed_echelonize_newton_john(self._entries, full)
938
sig_off()
939
940
elif algorithm == 'ple':
941
sig_on()
942
r = mzed_echelonize_ple(self._entries, full)
943
sig_off()
944
945
elif algorithm == 'heuristic':
946
sig_on()
947
r = mzed_echelonize(self._entries, full)
948
sig_off()
949
950
elif algorithm == 'builtin':
951
self._echelon_in_place_classical()
952
953
else:
954
raise ValueError("No algorithm '%s'."%algorithm)
955
956
self.cache('in_echelon_form',True)
957
self.cache('rank', r)
958
self.cache('pivots', self._pivots())
959
960
def _pivots(self):
961
"""
962
EXAMPLE::
963
964
sage: K.<a> = GF(2^8)
965
sage: A = random_matrix(K, 15, 15)
966
sage: A.pivots() # indirect doctest
967
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
968
"""
969
if not self.fetch('in_echelon_form'):
970
raise ValueError("self must be in reduced row echelon form first.")
971
pivots = []
972
cdef Py_ssize_t i, j, nc
973
nc = self._ncols
974
i = 0
975
while i < self._nrows:
976
for j from i <= j < nc:
977
if self.get_unsafe(i,j):
978
pivots.append(j)
979
i += 1
980
break
981
if j == nc:
982
break
983
return pivots
984
985
def __invert__(self):
986
"""
987
EXAMPLE::
988
989
sage: K.<a> = GF(2^3)
990
sage: A = random_matrix(K,3,3); A
991
[ a^2 a + 1 a^2 + a + 1]
992
[ a + 1 0 1]
993
[ a + 1 a^2 + 1 a + 1]
994
995
sage: B = ~A; B
996
[a^2 + a + 1 a^2 a^2]
997
[ a + 1 a^2 + a + 1 a + 1]
998
[ a a^2 + a a^2 + a + 1]
999
1000
sage: A*B
1001
[1 0 0]
1002
[0 1 0]
1003
[0 0 1]
1004
"""
1005
cdef Matrix_mod2e_dense A
1006
A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0)
1007
1008
if self._nrows and self._nrows == self._ncols:
1009
mzed_invert_newton_john(A._entries, self._entries)
1010
1011
return A
1012
1013
cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
1014
"""
1015
Return ``multiple * self[row][start_col:]``
1016
1017
INPUT:
1018
1019
- ``row`` - row index for row to rescale
1020
- ``multiple`` - finite field element to scale by
1021
- ``start_col`` - only start at this column index.
1022
1023
EXAMPLE::
1024
1025
sage: K.<a> = GF(2^3)
1026
sage: A = random_matrix(K,3,3); A
1027
[ a^2 a + 1 a^2 + a + 1]
1028
[ a + 1 0 1]
1029
[ a + 1 a^2 + 1 a + 1]
1030
1031
sage: A.rescale_row(0, a , 0); A
1032
[ a + 1 a^2 + a a^2 + 1]
1033
[ a + 1 0 1]
1034
[ a + 1 a^2 + 1 a + 1]
1035
1036
sage: A.rescale_row(0,0,0); A
1037
[ 0 0 0]
1038
[ a + 1 0 1]
1039
[ a + 1 a^2 + 1 a + 1]
1040
"""
1041
cdef m4ri_word x = poly_to_word(multiple)
1042
mzed_rescale_row(self._entries, row, start_col, x)
1043
1044
1045
cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple, Py_ssize_t start_col):
1046
"""
1047
Compute ``self[row_to][start_col:] += multiple * self[row_from][start_col:]``.
1048
1049
INPUT:
1050
1051
- ``row_to`` - row index of source
1052
- ``row_from`` - row index of destination
1053
- ``multiple`` - finite field element
1054
- ``start_col`` - only start at this column index
1055
1056
EXAMPLE::
1057
1058
sage: K.<a> = GF(2^3)
1059
sage: A = random_matrix(K,3,3); A
1060
[ a^2 a + 1 a^2 + a + 1]
1061
[ a + 1 0 1]
1062
[ a + 1 a^2 + 1 a + 1]
1063
1064
sage: A.add_multiple_of_row(0,1,a,0); A
1065
[ a a + 1 a^2 + 1]
1066
[ a + 1 0 1]
1067
[ a + 1 a^2 + 1 a + 1]
1068
"""
1069
1070
cdef m4ri_word x = poly_to_word(multiple)
1071
mzed_add_multiple_of_row(self._entries, row_to, self._entries, row_from, x, start_col)
1072
1073
1074
cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
1075
"""
1076
Swap rows ``row1`` and ``row2``.
1077
1078
INPUT:
1079
1080
- ``row1`` - row index
1081
- ``row2`` - row index
1082
1083
EXAMPLE::
1084
1085
sage: K.<a> = GF(2^3)
1086
sage: A = random_matrix(K,3,3)
1087
sage: A
1088
[ a^2 a + 1 a^2 + a + 1]
1089
[ a + 1 0 1]
1090
[ a + 1 a^2 + 1 a + 1]
1091
1092
sage: A.swap_rows(0,1); A
1093
[ a + 1 0 1]
1094
[ a^2 a + 1 a^2 + a + 1]
1095
[ a + 1 a^2 + 1 a + 1]
1096
1097
"""
1098
mzed_row_swap(self._entries, row1, row2)
1099
1100
cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
1101
"""
1102
Swap columns ``col1`` and ``col2``.
1103
1104
INPUT:
1105
1106
- ``col1`` - column index
1107
- ``col2`` - column index
1108
1109
EXAMPLE::
1110
1111
sage: K.<a> = GF(2^3)
1112
sage: A = random_matrix(K,3,3)
1113
sage: A
1114
[ a^2 a + 1 a^2 + a + 1]
1115
[ a + 1 0 1]
1116
[ a + 1 a^2 + 1 a + 1]
1117
1118
sage: A.swap_columns(0,1); A
1119
[ a + 1 a^2 a^2 + a + 1]
1120
[ 0 a + 1 1]
1121
[ a^2 + 1 a + 1 a + 1]
1122
1123
sage: A = random_matrix(K,4,16)
1124
1125
sage: B = copy(A)
1126
sage: B.swap_columns(0,1)
1127
sage: B.swap_columns(0,1)
1128
sage: A == B
1129
True
1130
1131
sage: A.swap_columns(0,15)
1132
sage: A.column(0) == B.column(15)
1133
True
1134
sage: A.swap_columns(14,15)
1135
sage: A.column(14) == B.column(0)
1136
True
1137
"""
1138
mzed_col_swap(self._entries, col1, col2)
1139
1140
def augment(self, Matrix_mod2e_dense right):
1141
"""
1142
Augments ``self`` with ``right``.
1143
1144
INPUT:
1145
1146
- ``right`` - a matrix
1147
1148
EXAMPLE::
1149
1150
sage: K.<a> = GF(2^4)
1151
sage: MS = MatrixSpace(K,3,3)
1152
sage: A = random_matrix(K,3,3)
1153
sage: B = A.augment(MS(1)); B
1154
[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 1 0 0]
1155
[ a + 1 a^3 1 0 1 0]
1156
[ a^3 + a + 1 a^3 + a^2 + 1 a + 1 0 0 1]
1157
1158
sage: B.echelonize(); B
1159
[ 1 0 0 a^2 + a a^3 + 1 a^3 + a]
1160
[ 0 1 0 a^3 + a^2 + a a^3 + a^2 + a + 1 a^2 + a]
1161
[ 0 0 1 a + 1 a^3 a^3]
1162
1163
sage: C = B.matrix_from_columns([3,4,5]); C
1164
[ a^2 + a a^3 + 1 a^3 + a]
1165
[ a^3 + a^2 + a a^3 + a^2 + a + 1 a^2 + a]
1166
[ a + 1 a^3 a^3]
1167
1168
sage: C == ~A
1169
True
1170
1171
sage: C*A == MS(1)
1172
True
1173
1174
TESTS::
1175
1176
sage: K.<a> = GF(2^4)
1177
sage: A = random_matrix(K,2,3)
1178
sage: B = random_matrix(K,2,0)
1179
sage: A.augment(B)
1180
[ a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
1181
[ a^2 + a a^2 + 1 a^2 + a]
1182
1183
sage: B.augment(A)
1184
[ a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]
1185
[ a^2 + a a^2 + 1 a^2 + a]
1186
1187
sage: M = Matrix(K, 0, 0, 0)
1188
sage: N = Matrix(K, 0, 19, 0)
1189
sage: W = M.augment(N)
1190
sage: W.ncols()
1191
19
1192
1193
sage: M = Matrix(K, 0, 1, 0)
1194
sage: N = Matrix(K, 0, 1, 0)
1195
sage: M.augment(N)
1196
[]
1197
"""
1198
cdef Matrix_mod2e_dense A
1199
1200
if self._nrows != right._nrows:
1201
raise TypeError, "Both numbers of rows must match."
1202
1203
if self._ncols == 0:
1204
return right.__copy__()
1205
if right._ncols == 0:
1206
return self.__copy__()
1207
1208
A = self.new_matrix(ncols = self._ncols + right._ncols)
1209
if self._nrows == 0:
1210
return A
1211
A._entries = mzed_concat(A._entries, self._entries, right._entries)
1212
return A
1213
1214
def stack(self, Matrix_mod2e_dense other):
1215
"""
1216
Stack ``self`` on top of ``other``.
1217
1218
INPUT:
1219
1220
- ``other`` - a matrix
1221
1222
EXAMPLE::
1223
1224
sage: K.<a> = GF(2^4)
1225
sage: A = random_matrix(K,2,2); A
1226
[ a^2 a^3 + a + 1]
1227
[a^3 + a^2 + a + 1 a + 1]
1228
1229
sage: B = random_matrix(K,2,2); B
1230
[ a^3 1]
1231
[ a^3 + a + 1 a^3 + a^2 + 1]
1232
1233
sage: A.stack(B)
1234
[ a^2 a^3 + a + 1]
1235
[a^3 + a^2 + a + 1 a + 1]
1236
[ a^3 1]
1237
[ a^3 + a + 1 a^3 + a^2 + 1]
1238
1239
sage: B.stack(A)
1240
[ a^3 1]
1241
[ a^3 + a + 1 a^3 + a^2 + 1]
1242
[ a^2 a^3 + a + 1]
1243
[a^3 + a^2 + a + 1 a + 1]
1244
1245
TESTS::
1246
1247
sage: A = random_matrix(K,0,3)
1248
sage: B = random_matrix(K,3,3)
1249
sage: A.stack(B)
1250
[ a + 1 a^3 + 1 a^3 + a + 1]
1251
[a^3 + a^2 + a + 1 a^2 + a a^2 + 1]
1252
[ a^2 + a a^3 + a^2 + a a^2 + 1]
1253
1254
sage: B.stack(A)
1255
[ a + 1 a^3 + 1 a^3 + a + 1]
1256
[a^3 + a^2 + a + 1 a^2 + a a^2 + 1]
1257
[ a^2 + a a^3 + a^2 + a a^2 + 1]
1258
1259
sage: M = Matrix(K, 0, 0, 0)
1260
sage: N = Matrix(K, 19, 0, 0)
1261
sage: W = M.stack(N)
1262
sage: W.nrows()
1263
19
1264
sage: M = Matrix(K, 1, 0, 0)
1265
sage: N = Matrix(K, 1, 0, 0)
1266
sage: M.stack(N)
1267
[]
1268
"""
1269
if self._ncols != other._ncols:
1270
raise TypeError, "Both numbers of columns must match."
1271
1272
if self._nrows == 0:
1273
return other.__copy__()
1274
if other._nrows == 0:
1275
return self.__copy__()
1276
1277
cdef Matrix_mod2e_dense A
1278
A = self.new_matrix(nrows = self._nrows + other._nrows)
1279
if self._ncols == 0:
1280
return A
1281
A._entries = mzed_stack(A._entries, self._entries, other._entries)
1282
return A
1283
1284
def submatrix(self, lowr, lowc, nrows , ncols):
1285
"""
1286
Return submatrix from the index ``lowr,lowc`` (inclusive) with
1287
dimension ``nrows x ncols``.
1288
1289
INPUT:
1290
1291
- ``lowr`` -- index of start row
1292
- ``lowc`` -- index of start column
1293
- ``nrows`` -- number of rows of submatrix
1294
- ``ncols`` -- number of columns of submatrix
1295
1296
EXAMPLES::
1297
1298
sage: K.<a> = GF(2^10)
1299
sage: A = random_matrix(K,200,200)
1300
sage: A[0:2,0:2] == A.submatrix(0,0,2,2)
1301
True
1302
sage: A[0:100,0:100] == A.submatrix(0,0,100,100)
1303
True
1304
sage: A == A.submatrix(0,0,200,200)
1305
True
1306
1307
sage: A[1:3,1:3] == A.submatrix(1,1,2,2)
1308
True
1309
sage: A[1:100,1:100] == A.submatrix(1,1,99,99)
1310
True
1311
sage: A[1:200,1:200] == A.submatrix(1,1,199,199)
1312
True
1313
"""
1314
cdef int highr = lowr + nrows
1315
cdef int highc = lowc + ncols
1316
1317
if nrows <= 0 or ncols <= 0:
1318
raise TypeError("Expected nrows, ncols to be > 0, but got %d,%d instead."%(nrows, ncols))
1319
1320
if highc > self._entries.ncols:
1321
raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead."%(highc, self._entries.ncols))
1322
1323
if highr > self._entries.nrows:
1324
raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead."%(highr, self._entries.nrows))
1325
1326
if lowr < 0:
1327
raise TypeError("Expected lowr >= 0, but got %d instead."%lowr)
1328
1329
if lowc < 0:
1330
raise TypeError("Expected lowc >= 0, but got %d instead."%lowc)
1331
1332
cdef Matrix_mod2e_dense A = self.new_matrix(nrows = nrows, ncols = ncols)
1333
if self._ncols == 0 or self._nrows == 0:
1334
return A
1335
A._entries = mzed_submatrix(A._entries, self._entries, lowr, lowc, highr, highc)
1336
return A
1337
1338
def rank(self):
1339
"""
1340
Return the rank of this matrix (cached).
1341
1342
EXAMPLE::
1343
1344
sage: K.<a> = GF(2^4)
1345
sage: A = random_matrix(K, 1000, 1000)
1346
sage: A.rank()
1347
1000
1348
1349
sage: A = matrix(K, 10, 0)
1350
sage: A.rank()
1351
0
1352
"""
1353
x = self.fetch('rank')
1354
if not x is None:
1355
return x
1356
if self._nrows == 0 or self._ncols == 0:
1357
return 0
1358
cdef mzed_t *A = mzed_copy(NULL, self._entries)
1359
1360
cdef size_t r = mzed_echelonize(A, 0)
1361
mzed_free(A)
1362
self.cache('rank', r)
1363
return r
1364
1365
def __hash__(self):
1366
"""
1367
EXAMPLE::
1368
1369
sage: K.<a> = GF(2^4)
1370
sage: A = random_matrix(K, 1000, 1000)
1371
sage: A.set_immutable()
1372
sage: {A:1} #indirect doctest
1373
{1000 x 1000 dense matrix over Finite Field in a of size 2^4 (type 'print A.str()' to see all of the entries): 1}
1374
1375
"""
1376
return self._hash()
1377
1378
def __reduce__(self):
1379
"""
1380
EXAMPLE::
1381
1382
sage: K.<a> = GF(2^8)
1383
sage: A = random_matrix(K,70,70)
1384
sage: f, s= A.__reduce__()
1385
sage: from sage.matrix.matrix_mod2e_dense import unpickle_matrix_mod2e_dense_v0
1386
sage: f == unpickle_matrix_mod2e_dense_v0
1387
True
1388
sage: f(*s) == A
1389
True
1390
"""
1391
from sage.matrix.matrix_space import MatrixSpace
1392
1393
cdef Matrix_mod2_dense A
1394
MS = MatrixSpace(GF(2), self._entries.x.nrows, self._entries.x.ncols)
1395
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = False)
1396
A._entries = mzd_copy( NULL, self._entries.x)
1397
return unpickle_matrix_mod2e_dense_v0, (A, self.base_ring(), self.nrows(), self.ncols())
1398
1399
def slice(self):
1400
r"""
1401
Unpack this matrix into matrices over `\GF{2}`.
1402
1403
Elements in `\GF{2^e}` can be represented as `\sum c_i a^i`
1404
where `a` is a root the minimal polynomial. This function
1405
returns a tuple of matrices `C` whose entry `C_i[x,y]` is the
1406
coefficient of `c_i` in `A[x,y]` if this matrix is `A`.
1407
1408
EXAMPLE::
1409
1410
sage: K.<a> = GF(2^2)
1411
sage: A = random_matrix(K, 5, 5); A
1412
[ 0 a + 1 a + 1 a + 1 0]
1413
[ 1 a + 1 1 a + 1 1]
1414
[a + 1 a + 1 a 1 a]
1415
[ a 1 a + 1 1 0]
1416
[ a 1 a + 1 a + 1 0]
1417
1418
sage: A1,A0 = A.slice()
1419
sage: A0
1420
[0 1 1 1 0]
1421
[0 1 0 1 0]
1422
[1 1 1 0 1]
1423
[1 0 1 0 0]
1424
[1 0 1 1 0]
1425
1426
sage: A1
1427
[0 1 1 1 0]
1428
[1 1 1 1 1]
1429
[1 1 0 1 0]
1430
[0 1 1 1 0]
1431
[0 1 1 1 0]
1432
1433
sage: A0[2,4]*a + A1[2,4], A[2,4]
1434
(a, a)
1435
1436
sage: K.<a> = GF(2^3)
1437
sage: A = random_matrix(K, 5, 5); A
1438
[ a + 1 a^2 + a 1 a a^2 + a]
1439
[ a + 1 a^2 + a a^2 a^2 a^2 + 1]
1440
[a^2 + a + 1 a^2 + a + 1 0 a^2 + a + 1 a^2 + 1]
1441
[ a^2 + a 0 a^2 + a + 1 a a]
1442
[ a^2 a + 1 a a^2 + 1 a^2 + a + 1]
1443
1444
sage: A0,A1,A2 = A.slice()
1445
sage: A0
1446
[1 0 1 0 0]
1447
[1 0 0 0 1]
1448
[1 1 0 1 1]
1449
[0 0 1 0 0]
1450
[0 1 0 1 1]
1451
1452
Slicing and clinging are inverse operations::
1453
1454
sage: B = matrix(K, 5, 5)
1455
sage: B.cling(A0,A1,A2)
1456
sage: B == A
1457
True
1458
"""
1459
if self._entries.finite_field.degree > 4:
1460
raise NotImplementedError("Slicing is only implemented for degree <= 4.")
1461
1462
from sage.matrix.matrix_space import MatrixSpace
1463
1464
MS = MatrixSpace(GF(2), self._nrows, self._ncols)
1465
cdef mzd_slice_t *a = mzed_slice(NULL, self._entries)
1466
1467
cdef Matrix_mod2_dense A0, A1, A2, A3
1468
A0 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
1469
A1 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
1470
mzd_copy(A0._entries, a.x[0])
1471
mzd_copy(A1._entries, a.x[1])
1472
if self._entries.finite_field.degree > 2:
1473
A2 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
1474
mzd_copy(A2._entries, a.x[2])
1475
if self._entries.finite_field.degree > 3:
1476
A3 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
1477
mzd_copy(A3._entries, a.x[3])
1478
1479
mzd_slice_free(a)
1480
if self._entries.finite_field.degree == 2:
1481
return A0,A1
1482
elif self._entries.finite_field.degree == 3:
1483
return A0,A1,A2
1484
elif self._entries.finite_field.degree == 4:
1485
return A0,A1,A2,A3
1486
1487
def cling(self, *C):
1488
r"""
1489
Pack the matrices over `\GF{2}` into this matrix over `\GF{2^e}`.
1490
1491
Elements in `\GF{2^e}` can be represented as `\sum c_i a^i` where
1492
`a` is a root the minimal polynomial. If this matrix is `A`
1493
then this function writes `c_i a^i` to the entry `A[x,y]`
1494
where `c_i` is the entry `C_i[x,y]`.
1495
1496
INPUT:
1497
1498
- ``C`` - a list of matrices over GF(2)
1499
1500
EXAMPLE::
1501
1502
sage: K.<a> = GF(2^2)
1503
sage: A = matrix(K, 5, 5)
1504
sage: A0 = random_matrix(GF(2), 5, 5); A0
1505
[0 1 0 1 1]
1506
[0 1 1 1 0]
1507
[0 0 0 1 0]
1508
[0 1 1 0 0]
1509
[0 0 0 1 1]
1510
1511
sage: A1 = random_matrix(GF(2), 5, 5); A1
1512
[0 0 1 1 1]
1513
[1 1 1 1 0]
1514
[0 0 0 1 1]
1515
[1 0 0 0 1]
1516
[1 0 0 1 1]
1517
1518
sage: A.cling(A1, A0); A
1519
[ 0 a 1 a + 1 a + 1]
1520
[ 1 a + 1 a + 1 a + 1 0]
1521
[ 0 0 0 a + 1 1]
1522
[ 1 a a 0 1]
1523
[ 1 0 0 a + 1 a + 1]
1524
1525
sage: A0[0,3]*a + A1[0,3], A[0,3]
1526
(a + 1, a + 1)
1527
1528
Slicing and clinging are inverse operations::
1529
1530
sage: B1, B0 = A.slice()
1531
sage: B0 == A0 and B1 == A1
1532
True
1533
1534
TESTS::
1535
1536
sage: K.<a> = GF(2^2)
1537
sage: A = matrix(K, 5, 5)
1538
sage: A0 = random_matrix(GF(2), 5, 5)
1539
sage: A1 = random_matrix(GF(2), 5, 5)
1540
sage: A.cling(A0, A1)
1541
sage: B = copy(A)
1542
sage: A.cling(A0, A1)
1543
sage: A == B
1544
True
1545
1546
sage: A.cling(A0)
1547
Traceback (most recent call last):
1548
...
1549
ValueError: The number of input matrices must be equal to the degree of the base field.
1550
1551
sage: K.<a> = GF(2^5)
1552
sage: A = matrix(K, 5, 5)
1553
sage: A0 = random_matrix(GF(2), 5, 5)
1554
sage: A1 = random_matrix(GF(2), 5, 5)
1555
sage: A2 = random_matrix(GF(2), 5, 5)
1556
sage: A3 = random_matrix(GF(2), 5, 5)
1557
sage: A4 = random_matrix(GF(2), 5, 5)
1558
sage: A.cling(A0, A1, A2, A3, A4)
1559
Traceback (most recent call last):
1560
...
1561
NotImplementedError: Cling is only implemented for degree <= 4.
1562
"""
1563
cdef Py_ssize_t i
1564
1565
if self._entries.finite_field.degree > 4:
1566
raise NotImplementedError("Cling is only implemented for degree <= 4.")
1567
1568
if self._is_immutable:
1569
raise TypeError("Immutable matrices cannot be modified.")
1570
1571
if len(C) != self._entries.finite_field.degree:
1572
raise ValueError("The number of input matrices must be equal to the degree of the base field.")
1573
1574
cdef mzd_slice_t *v = mzd_slice_init(self._entries.finite_field, self._nrows, self._ncols)
1575
for i in range(self._entries.finite_field.degree):
1576
if not PY_TYPE_CHECK(C[i], Matrix_mod2_dense):
1577
mzd_slice_free(v)
1578
raise TypeError("All input matrices must be over GF(2).")
1579
mzd_copy(v.x[i], (<Matrix_mod2_dense>C[i])._entries)
1580
mzed_set_ui(self._entries, 0)
1581
mzed_cling(self._entries, v)
1582
mzd_slice_free(v)
1583
1584
def unpickle_matrix_mod2e_dense_v0(Matrix_mod2_dense a, base_ring, nrows, ncols):
1585
"""
1586
EXAMPLE::
1587
1588
sage: K.<a> = GF(2^2)
1589
sage: A = random_matrix(K,10,10)
1590
sage: f, s= A.__reduce__()
1591
sage: from sage.matrix.matrix_mod2e_dense import unpickle_matrix_mod2e_dense_v0
1592
sage: f == unpickle_matrix_mod2e_dense_v0
1593
True
1594
sage: f(*s) == A
1595
True
1596
"""
1597
from sage.matrix.matrix_space import MatrixSpace
1598
1599
MS = MatrixSpace(base_ring, nrows, ncols)
1600
cdef Matrix_mod2e_dense A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, MS, 0, 0, 0)
1601
mzd_copy(A._entries.x, a._entries)
1602
return A
1603
1604