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