Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matrix/matrix_modn_dense.pyx
8815 views
1
r"""
2
Dense matrices over `\ZZ/n\ZZ` for `n` small
3
4
AUTHORS:
5
6
- William Stein
7
8
- Robert Bradshaw
9
10
This is a compiled implementation of dense matrices over
11
`\ZZ/n\ZZ` for `n` small.
12
13
EXAMPLES::
14
15
sage: a = matrix(Integers(37),3,range(9),sparse=False); a
16
[0 1 2]
17
[3 4 5]
18
[6 7 8]
19
sage: a.rank()
20
2
21
sage: type(a)
22
<type 'sage.matrix.matrix_modn_dense_float.Matrix_modn_dense_float'>
23
sage: a[0,0] = 5
24
sage: a.rank()
25
3
26
sage: parent(a)
27
Full MatrixSpace of 3 by 3 dense matrices over Ring of integers modulo 37
28
29
::
30
31
sage: a^2
32
[ 3 23 31]
33
[20 17 29]
34
[25 16 0]
35
sage: a+a
36
[10 2 4]
37
[ 6 8 10]
38
[12 14 16]
39
40
::
41
42
sage: b = a.new_matrix(2,3,range(6)); b
43
[0 1 2]
44
[3 4 5]
45
sage: a*b
46
Traceback (most recent call last):
47
...
48
TypeError: unsupported operand parent(s) for '*': 'Full MatrixSpace of 3 by 3 dense matrices over Ring of integers modulo 37' and 'Full MatrixSpace of 2 by 3 dense matrices over Ring of integers modulo 37'
49
sage: b*a
50
[15 18 21]
51
[20 17 29]
52
53
::
54
55
sage: TestSuite(a).run()
56
sage: TestSuite(b).run()
57
58
::
59
60
sage: a.echelonize(); a
61
[1 0 0]
62
[0 1 0]
63
[0 0 1]
64
sage: b.echelonize(); b
65
[ 1 0 36]
66
[ 0 1 2]
67
68
We create a matrix group::
69
70
sage: M = MatrixSpace(GF(3),3,3)
71
sage: G = MatrixGroup([M([[0,1,0],[0,0,1],[1,0,0]]), M([[0,1,0],[1,0,0],[0,0,1]])])
72
sage: G
73
Matrix group over Finite Field of size 3 with 2 generators (
74
[0 1 0] [0 1 0]
75
[0 0 1] [1 0 0]
76
[1 0 0], [0 0 1]
77
)
78
sage: G.gap()
79
Group([ [ [ 0*Z(3), Z(3)^0, 0*Z(3) ], [ 0*Z(3), 0*Z(3), Z(3)^0 ], [ Z(3)^0, 0*Z(3), 0*Z(3) ] ],
80
[ [ 0*Z(3), Z(3)^0, 0*Z(3) ], [ Z(3)^0, 0*Z(3), 0*Z(3) ], [ 0*Z(3), 0*Z(3), Z(3)^0 ] ] ])
81
82
TESTS::
83
84
sage: M = MatrixSpace(GF(5),2,2)
85
sage: A = M([1,0,0,1])
86
sage: A - int(-1)
87
[2 0]
88
[0 2]
89
sage: B = M([4,0,0,1])
90
sage: B - int(-1)
91
[0 0]
92
[0 2]
93
sage: Matrix(GF(5),0,0, sparse=False).inverse()
94
[]
95
"""
96
97
98
include "sage/ext/interrupt.pxi"
99
include "sage/ext/cdefs.pxi"
100
include 'sage/ext/stdsage.pxi'
101
include 'sage/ext/random.pxi'
102
from cpython.string cimport *
103
104
cimport sage.rings.fast_arith
105
import sage.rings.fast_arith
106
cdef sage.rings.fast_arith.arith_int ArithIntObj
107
ArithIntObj = sage.rings.fast_arith.arith_int()
108
109
110
# TODO: DO NOT change this back until all the ints, etc., below are changed
111
# and get_unsafe is rewritten to return the right thing. E.g., with
112
# the above uncommented, on 64-bit,
113
# m =matrix(Integers(101^3),2,[824362, 606695, 641451, 205942])
114
# m.det()
115
# --> gives 0, which is totally wrong.
116
117
import matrix_window_modn_dense
118
119
from sage.modules.vector_modn_dense cimport Vector_modn_dense
120
121
from sage.rings.arith import is_prime
122
from sage.structure.element cimport ModuleElement
123
124
cimport matrix_dense
125
cimport matrix
126
cimport matrix0
127
cimport sage.structure.element
128
129
from sage.matrix.matrix_modn_dense_float cimport Matrix_modn_dense_float
130
from sage.matrix.matrix_modn_dense_double cimport Matrix_modn_dense_double
131
132
from sage.structure.element cimport Matrix
133
134
from sage.rings.finite_rings.integer_mod cimport IntegerMod_int, IntegerMod_abstract
135
136
from sage.misc.misc import verbose, get_verbose, cputime
137
138
from sage.rings.integer cimport Integer
139
140
from sage.structure.element cimport ModuleElement, RingElement, Element, Vector
141
142
from matrix_integer_dense cimport Matrix_integer_dense
143
from sage.rings.integer_ring import ZZ
144
145
################
146
from sage.rings.fast_arith cimport arith_int
147
cdef arith_int ai
148
ai = arith_int()
149
################
150
151
from sage.structure.proof.proof import get_flag as get_proof_flag
152
153
cdef long num = 1
154
cdef bint little_endian = (<char*>(&num))[0]
155
156
def __matrix_from_rows_of_matrices(X):
157
"""
158
Return a matrix whose ith row is ``X[i].list()``.
159
160
INPUT:
161
162
- ``X`` - a nonempty list of matrices of the same size mod a
163
single modulus ``p``
164
165
OUTPUT: A single matrix mod p whose ith row is ``X[i].list()``.
166
167
168
"""
169
# The code below is just a fast version of the following:
170
## from constructor import matrix
171
## K = X[0].base_ring()
172
## v = sum([y.list() for y in X],[])
173
## return matrix(K, len(X), X[0].nrows()*X[0].ncols(), v)
174
175
from matrix_space import MatrixSpace
176
cdef Matrix_modn_dense A, T
177
cdef Py_ssize_t i, n, m
178
n = len(X)
179
180
T = X[0]
181
m = T._nrows * T._ncols
182
A = Matrix_modn_dense.__new__(Matrix_modn_dense, MatrixSpace(X[0].base_ring(), n, m), 0, 0, 0)
183
A.p = T.p
184
A.gather = T.gather
185
186
for i from 0 <= i < n:
187
T = X[i]
188
memcpy(A._entries + i*m, T._entries, sizeof(mod_int)*m)
189
return A
190
191
cpdef is_Matrix_modn_dense(self):
192
"""
193
"""
194
return isinstance(self, Matrix_modn_dense) | isinstance(self, Matrix_modn_dense_float) | isinstance(self, Matrix_modn_dense_double)
195
196
##############################################################################
197
# Copyright (C) 2004,2005,2006 William Stein <[email protected]>
198
# Distributed under the terms of the GNU General Public License (GPL)
199
# The full text of the GPL is available at:
200
# http://www.gnu.org/licenses/
201
##############################################################################
202
203
cdef class Matrix_modn_dense(matrix_dense.Matrix_dense):
204
########################################################################
205
# LEVEL 1 functionality
206
# x * __cinit__
207
# x * __dealloc__
208
# x * __init__
209
# x * set_unsafe
210
# x * get_unsafe
211
# x * __richcmp__ -- always the same
212
########################################################################
213
def __cinit__(self, parent, entries, copy, coerce):
214
from sage.misc.superseded import deprecation
215
deprecation(4260, "This class is replaced by Matrix_modn_dense_float/Matrix_modn_dense_double.")
216
217
matrix_dense.Matrix_dense.__init__(self, parent)
218
219
cdef long p
220
p = self._base_ring.characteristic()
221
self.p = p
222
MAX_MODULUS = 2**23
223
if p >= MAX_MODULUS:
224
raise OverflowError, "p (=%s) must be < %s"%(p, MAX_MODULUS)
225
self.gather = MOD_INT_OVERFLOW/<mod_int>(p*p)
226
227
if not isinstance(entries, list):
228
sig_on()
229
self._entries = <mod_int *> sage_calloc(self._nrows*self._ncols,sizeof(mod_int))
230
sig_off()
231
else:
232
sig_on()
233
self._entries = <mod_int *> sage_malloc(self._nrows*self._ncols * sizeof(mod_int))
234
sig_off()
235
236
if self._entries == NULL:
237
raise MemoryError, "Error allocating matrix"
238
239
self._matrix = <mod_int **> sage_malloc(sizeof(mod_int*)*self._nrows)
240
if self._matrix == NULL:
241
sage_free(self._entries)
242
self._entries = NULL
243
raise MemoryError, "Error allocating memory"
244
245
cdef mod_int k
246
cdef Py_ssize_t i
247
k = 0
248
for i from 0 <= i < self._nrows:
249
self._matrix[i] = self._entries + k
250
k = k + self._ncols
251
252
def __dealloc__(self):
253
if self._entries == NULL:
254
return
255
sage_free(self._entries)
256
sage_free(self._matrix)
257
258
def __init__(self, parent, entries, copy, coerce):
259
"""
260
TESTS::
261
262
sage: matrix(GF(7), 2, 2, [-1, int(-2), GF(7)(-3), 1/4])
263
[6 5]
264
[4 2]
265
"""
266
cdef mod_int e
267
cdef Py_ssize_t i, j, k
268
cdef mod_int *v
269
cdef long p
270
p = self._base_ring.characteristic()
271
272
R = self.base_ring()
273
274
# scalar?
275
if not isinstance(entries, list):
276
# sig_on()
277
# for i from 0 <= i < self._nrows*self._ncols:
278
# self._entries[i] = 0
279
# sig_off()
280
if entries is None:
281
# zero matrix
282
pass
283
else:
284
e = R(entries)
285
if e != 0:
286
for i from 0 <= i < min(self._nrows, self._ncols):
287
self._matrix[i][i] = e
288
return
289
290
# all entries are given as a long list
291
if len(entries) != self._nrows * self._ncols:
292
raise IndexError, "The vector of entries has the wrong length."
293
294
k = 0
295
cdef mod_int n
296
cdef long tmp
297
298
for i from 0 <= i < self._nrows:
299
sig_check()
300
v = self._matrix[i]
301
for j from 0 <= j < self._ncols:
302
x = entries[k]
303
if PY_TYPE_CHECK_EXACT(x, int):
304
tmp = (<long>x) % p
305
v[j] = tmp + (tmp<0)*p
306
elif PY_TYPE_CHECK_EXACT(x, IntegerMod_int) and (<IntegerMod_int>x)._parent is R:
307
v[j] = (<IntegerMod_int>x).ivalue
308
elif PY_TYPE_CHECK_EXACT(x, Integer):
309
if coerce:
310
v[j] = mpz_fdiv_ui((<Integer>x).value, p)
311
else:
312
v[j] = mpz_get_ui((<Integer>x).value)
313
elif coerce:
314
v[j] = R(entries[k])
315
else:
316
v[j] = <long>(entries[k])
317
k = k + 1
318
319
def __richcmp__(Matrix_modn_dense self, right, int op): # always need for mysterious reasons.
320
return self._richcmp(right, op)
321
322
def __hash__(self):
323
"""
324
EXAMPLE::
325
326
sage: B = random_matrix(GF(127),3,3)
327
sage: B.set_immutable()
328
sage: {B:0} # indirect doctest
329
{[ 9 75 94]
330
[ 4 57 112]
331
[ 59 85 45]: 0}
332
333
::
334
335
sage: M = random_matrix(GF(7), 10, 10)
336
sage: M.set_immutable()
337
sage: hash(M)
338
143
339
sage: MZ = M.change_ring(ZZ)
340
sage: MZ.set_immutable()
341
sage: hash(MZ)
342
143
343
sage: MS = M.sparse_matrix()
344
sage: MS.set_immutable()
345
sage: hash(MS)
346
143
347
348
TEST::
349
350
sage: A = matrix(GF(2),2,0)
351
sage: hash(A)
352
Traceback (most recent call last):
353
...
354
TypeError: mutable matrices are unhashable
355
sage: A.set_immutable()
356
sage: hash(A)
357
0
358
"""
359
if self.is_mutable():
360
raise TypeError("mutable matrices are unhashable")
361
x = self.fetch('hash')
362
if not x is None:
363
return x
364
365
cdef long _hash = 0
366
cdef mod_int *_matrix
367
cdef long n = 0
368
cdef Py_ssize_t i, j
369
370
if self._nrows == 0 or self._ncols == 0:
371
return 0
372
373
sig_on()
374
for i from 0 <= i < self._nrows:
375
_matrix = self._matrix[i]
376
for j from 0 <= j < self._ncols:
377
_hash ^= (n * _matrix[j])
378
n+=1
379
sig_off()
380
381
if _hash == -1:
382
return -2
383
384
self.cache('hash', _hash)
385
386
return _hash
387
388
cdef set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value):
389
"""
390
Set the (i,j) entry of self to the int value.
391
"""
392
self._matrix[i][j] = value
393
394
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
395
"""
396
Set the (i,j) entry of self to the value, which is assumed
397
to be of type IntegerMod_int.
398
"""
399
self._matrix[i][j] = (<IntegerMod_int> value).ivalue
400
401
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
402
cdef IntegerMod_int n
403
n = IntegerMod_int.__new__(IntegerMod_int)
404
IntegerMod_abstract.__init__(n, self._base_ring)
405
n.ivalue = self._matrix[i][j]
406
return n
407
408
########################################################################
409
# LEVEL 2 functionality
410
# * cdef _pickle
411
# * cdef _unpickle
412
# x * cdef _add_
413
# * cdef _mul_
414
# x * cdef _matrix_times_matrix_
415
# x * cdef _cmp_c_impl
416
# x * __neg__
417
# * __invert__
418
# x * __copy__
419
# * _multiply_classical
420
# * _list -- list of underlying elements (need not be a copy)
421
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
422
########################################################################
423
# def _pickle(self):
424
# def _unpickle(self, data, int version): # use version >= 0
425
# cpdef ModuleElement _add_(self, ModuleElement right):
426
# cdef _mul_(self, Matrix right):
427
# cdef int _cmp_c_impl(self, Matrix right) except -2:
428
# def __invert__(self):
429
# def _multiply_classical(left, matrix.Matrix _right):
430
# def _list(self):
431
# def _dict(self):
432
433
def _pickle(self):
434
"""
435
Utility function for pickling.
436
437
If the prime is small enough to fit in a byte, then it is stored as
438
a contiguous string of bytes (to save space). Otherwise, memcpy is
439
used to copy the raw data in the platforms native format. Any
440
byte-swapping or word size difference is taken care of in
441
unpickling (optimizing for unpickling on the same platform things
442
were pickled on).
443
444
The upcoming buffer protocol would be useful to not have to do any
445
copying.
446
447
EXAMPLES::
448
449
sage: m = matrix(Integers(128), 3, 3, [ord(c) for c in "Hi there!"]); m
450
[ 72 105 32]
451
[116 104 101]
452
[114 101 33]
453
sage: m._pickle()
454
((1, ..., 'Hi there!'), 10)
455
"""
456
cdef Py_ssize_t i, j
457
cdef unsigned char* ss
458
cdef unsigned char* row_ss
459
cdef long word_size
460
cdef mod_int *row_self
461
462
if self.p <= 0xFF:
463
word_size = sizeof(char)
464
else:
465
word_size = sizeof(mod_int)
466
467
s = PyString_FromStringAndSize(NULL, word_size * self._nrows * self._ncols)
468
ss = <unsigned char*><char *>s
469
470
sig_on()
471
if word_size == sizeof(char):
472
for i from 0 <= i < self._nrows:
473
row_self = self._matrix[i]
474
row_ss = &ss[i*self._ncols]
475
for j from 0<= j < self._ncols:
476
row_ss[j] = row_self[j]
477
else:
478
for i from 0 <= i < self._nrows:
479
memcpy(&ss[i*sizeof(mod_int)*self._ncols], self._matrix[i], sizeof(mod_int) * self._ncols)
480
sig_off()
481
482
return (word_size, little_endian, s), 10
483
484
485
def _unpickle(self, data, int version):
486
r"""
487
TESTS:
488
489
Test for char-sized modulus::
490
491
sage: A = random_matrix(GF(7), 5, 9)
492
sage: data, version = A._pickle()
493
sage: B = A.parent()(0)
494
sage: B._unpickle(data, version)
495
sage: B == A
496
True
497
498
And for larger modulus::
499
500
sage: A = random_matrix(GF(1009), 51, 5)
501
sage: data, version = A._pickle()
502
sage: B = A.parent()(0)
503
sage: B._unpickle(data, version)
504
sage: B == A
505
True
506
507
Now test all the bit-packing options::
508
509
sage: A = matrix(Integers(1000), 2, 2)
510
sage: A._unpickle((1, True, '\x01\x02\xFF\x00'), 10)
511
sage: A
512
[ 1 2]
513
[255 0]
514
515
::
516
517
sage: A = matrix(Integers(1000), 1, 2)
518
sage: A._unpickle((4, True, '\x02\x01\x00\x00\x01\x00\x00\x00'), 10)
519
sage: A
520
[258 1]
521
sage: A._unpickle((4, False, '\x00\x00\x02\x01\x00\x00\x01\x03'), 10)
522
sage: A
523
[513 259]
524
sage: A._unpickle((8, True, '\x03\x01\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00'), 10)
525
sage: A
526
[259 5]
527
sage: A._unpickle((8, False, '\x00\x00\x00\x00\x00\x00\x02\x08\x00\x00\x00\x00\x00\x00\x01\x04'), 10)
528
sage: A
529
[520 260]
530
531
Now make sure it works in context::
532
533
sage: A = random_matrix(Integers(33), 31, 31)
534
sage: loads(dumps(A)) == A
535
True
536
sage: A = random_matrix(Integers(3333), 31, 31)
537
sage: loads(dumps(A)) == A
538
True
539
"""
540
541
if version < 10:
542
return matrix_dense.Matrix_dense._unpickle(self, data, version)
543
544
cdef Py_ssize_t i, j
545
cdef unsigned char* ss
546
cdef unsigned char* row_ss
547
cdef long word_size
548
cdef mod_int *row_self
549
cdef bint little_endian_data
550
cdef unsigned char* udata
551
552
if version == 10:
553
word_size, little_endian_data, s = data
554
ss = <unsigned char*><char *>s
555
556
sig_on()
557
if word_size == sizeof(char):
558
for i from 0 <= i < self._nrows:
559
row_self = self._matrix[i]
560
row_ss = &ss[i*self._ncols]
561
for j from 0<= j < self._ncols:
562
row_self[j] = row_ss[j]
563
564
elif word_size == sizeof(mod_int) and little_endian == little_endian_data:
565
for i from 0 <= i < self._nrows:
566
memcpy(self._matrix[i], &ss[i*sizeof(mod_int)*self._ncols], sizeof(mod_int) * self._ncols)
567
568
# Note that mod_int is at least 32 bits, and never stores more than 32 bits of info
569
elif little_endian_data:
570
for i from 0 <= i < self._nrows:
571
row_self = self._matrix[i]
572
for j from 0<= j < self._ncols:
573
udata = &ss[(i*self._ncols+j)*word_size]
574
row_self[j] = ((udata[0]) +
575
(udata[1] << 8) +
576
(udata[2] << 16) +
577
(udata[3] << 24))
578
579
else:
580
for i from 0 <= i < self._nrows:
581
row_self = self._matrix[i]
582
for j from 0<= j < self._ncols:
583
udata = &ss[(i*self._ncols+j)*word_size]
584
row_self[j] = ((udata[word_size-1]) +
585
(udata[word_size-2] << 8) +
586
(udata[word_size-3] << 16) +
587
(udata[word_size-4] << 24))
588
589
sig_off()
590
591
else:
592
raise RuntimeError, "unknown matrix version"
593
594
595
cdef long _hash(self) except -1:
596
"""
597
TESTS::
598
599
sage: a = random_matrix(GF(11), 5, 5)
600
sage: a.set_immutable()
601
sage: hash(a) #random
602
216
603
sage: b = a.change_ring(ZZ)
604
sage: b.set_immutable()
605
sage: hash(b) == hash(a)
606
True
607
"""
608
x = self.fetch('hash')
609
if not x is None: return x
610
611
if not self._is_immutable:
612
raise TypeError, "mutable matrices are unhashable"
613
614
cdef Py_ssize_t i
615
cdef long h = 0, n = 0
616
617
sig_on()
618
for i from 0 <= i < self._nrows:
619
for j from 0<= j < self._ncols:
620
h ^= n * self._matrix[i][j]
621
n += 1
622
sig_off()
623
624
self.cache('hash', h)
625
return h
626
627
628
def __neg__(self):
629
"""
630
EXAMPLES::
631
632
sage: m = matrix(GF(19), 3, 3, range(9)); m
633
[0 1 2]
634
[3 4 5]
635
[6 7 8]
636
sage: -m
637
[ 0 18 17]
638
[16 15 14]
639
[13 12 11]
640
"""
641
cdef Py_ssize_t i,j
642
cdef Matrix_modn_dense M
643
cdef mod_int p = self.p
644
645
M = Matrix_modn_dense.__new__(Matrix_modn_dense, self._parent,None,None,None)
646
M.p = p
647
648
sig_on()
649
cdef mod_int *row_self, *row_ans
650
for i from 0 <= i < self._nrows:
651
row_self = self._matrix[i]
652
row_ans = M._matrix[i]
653
for j from 0<= j < self._ncols:
654
if row_self[j]:
655
row_ans[j] = p - row_self[j]
656
else:
657
row_ans[j] = 0
658
sig_off()
659
return M
660
661
662
cpdef ModuleElement _lmul_(self, RingElement right):
663
"""
664
EXAMPLES::
665
666
sage: a = random_matrix(Integers(60), 400, 500)
667
sage: 3*a + 9*a == 12*a
668
True
669
"""
670
return self._rmul_(right)
671
672
cpdef ModuleElement _rmul_(self, RingElement left):
673
"""
674
EXAMPLES::
675
676
sage: a = matrix(GF(101), 3, 3, range(9)); a
677
[0 1 2]
678
[3 4 5]
679
[6 7 8]
680
sage: a * 5
681
[ 0 5 10]
682
[15 20 25]
683
[30 35 40]
684
sage: a * 50
685
[ 0 50 100]
686
[ 49 99 48]
687
[ 98 47 97]
688
"""
689
cdef Py_ssize_t i,j
690
cdef Matrix_modn_dense M
691
cdef mod_int p = self.p
692
cdef mod_int a = left
693
694
M = Matrix_modn_dense.__new__(Matrix_modn_dense, self._parent,None,None,None)
695
M.p = p
696
697
sig_on()
698
cdef mod_int *row_self, *row_ans
699
for i from 0 <= i < self._nrows:
700
row_self = self._matrix[i]
701
row_ans = M._matrix[i]
702
for j from 0<= j < self._ncols:
703
row_ans[j] = (a*row_self[j]) % p
704
sig_off()
705
return M
706
707
def __copy__(self):
708
cdef Matrix_modn_dense A
709
A = Matrix_modn_dense.__new__(Matrix_modn_dense, self._parent,
710
0, 0, 0)
711
memcpy(A._entries, self._entries, sizeof(mod_int)*self._nrows*self._ncols)
712
A.p = self.p
713
A.gather = self.gather
714
if self._subdivisions is not None:
715
A.subdivide(*self.subdivisions())
716
return A
717
718
719
cpdef ModuleElement _add_(self, ModuleElement right):
720
"""
721
Add two dense matrices over Z/nZ
722
723
EXAMPLES::
724
725
sage: a = MatrixSpace(GF(19),3)(range(9))
726
sage: a+a
727
[ 0 2 4]
728
[ 6 8 10]
729
[12 14 16]
730
sage: b = MatrixSpace(GF(19),3)(range(9))
731
sage: b.swap_rows(1,2)
732
sage: a+b
733
[ 0 2 4]
734
[ 9 11 13]
735
[ 9 11 13]
736
sage: b+a
737
[ 0 2 4]
738
[ 9 11 13]
739
[ 9 11 13]
740
"""
741
742
cdef Py_ssize_t i,j
743
cdef mod_int k, p
744
cdef Matrix_modn_dense M
745
746
M = Matrix_modn_dense.__new__(Matrix_modn_dense, self._parent,None,None,None)
747
Matrix.__init__(M, self._parent)
748
p = self.p
749
750
sig_on()
751
cdef mod_int *row_self, *row_right, *row_ans
752
for i from 0 <= i < self._nrows:
753
row_self = self._matrix[i]
754
row_right = (<Matrix_modn_dense> right)._matrix[i]
755
row_ans = M._matrix[i]
756
for j from 0<= j < self._ncols:
757
k = row_self[j] + row_right[j]
758
row_ans[j] = k - (k >= p) * p
759
sig_off()
760
return M
761
762
763
cpdef ModuleElement _sub_(self, ModuleElement right):
764
"""
765
Subtract two dense matrices over Z/nZ
766
767
EXAMPLES::
768
769
sage: a = matrix(GF(11), 3, 3, range(9)); a
770
[0 1 2]
771
[3 4 5]
772
[6 7 8]
773
sage: a - 4
774
[7 1 2]
775
[3 0 5]
776
[6 7 4]
777
sage: a - matrix(GF(11), 3, 3, range(1, 19, 2))
778
[10 9 8]
779
[ 7 6 5]
780
[ 4 3 2]
781
"""
782
783
cdef Py_ssize_t i,j
784
cdef mod_int k, p
785
cdef Matrix_modn_dense M
786
787
M = Matrix_modn_dense.__new__(Matrix_modn_dense, self._parent,None,None,None)
788
Matrix.__init__(M, self._parent)
789
p = self.p
790
791
sig_on()
792
cdef mod_int *row_self, *row_right, *row_ans
793
for i from 0 <= i < self._nrows:
794
row_self = self._matrix[i]
795
row_right = (<Matrix_modn_dense> right)._matrix[i]
796
row_ans = M._matrix[i]
797
for j from 0<= j < self._ncols:
798
k = p + row_self[j] - row_right[j]
799
row_ans[j] = k - (k >= p) * p
800
sig_off()
801
return M
802
803
804
cdef int _cmp_c_impl(self, Element right) except -2:
805
"""
806
Compare two dense matrices over Z/nZ
807
808
EXAMPLES::
809
810
sage: a = matrix(GF(17), 4, range(3, 83, 5)); a
811
[ 3 8 13 1]
812
[ 6 11 16 4]
813
[ 9 14 2 7]
814
[12 0 5 10]
815
sage: a == a
816
True
817
sage: b = a - 3; b
818
[ 0 8 13 1]
819
[ 6 8 16 4]
820
[ 9 14 16 7]
821
[12 0 5 7]
822
sage: b < a
823
True
824
sage: b > a
825
False
826
sage: b == a
827
False
828
sage: b + 3 == a
829
True
830
"""
831
832
cdef Py_ssize_t i, j
833
cdef int cmp
834
835
sig_on()
836
cdef mod_int *row_self, *row_right
837
for i from 0 <= i < self._nrows:
838
row_self = self._matrix[i]
839
row_right = (<Matrix_modn_dense> right)._matrix[i]
840
for j from 0 <= j < self._ncols:
841
if row_self[j] < row_right[j]:
842
sig_off()
843
return -1
844
elif row_self[j] > row_right[j]:
845
sig_off()
846
return 1
847
#cmp = memcmp(row_self, row_right, sizeof(mod_int)*self._ncols)
848
#if cmp:
849
# return cmp
850
sig_off()
851
return 0
852
853
854
cdef Matrix _matrix_times_matrix_(self, Matrix right):
855
if get_verbose() >= 2:
856
verbose('mod-p multiply of %s x %s matrix by %s x %s matrix modulo %s'%(
857
self._nrows, self._ncols, right._nrows, right._ncols, self.p))
858
if self._will_use_strassen(right):
859
return self._multiply_strassen(right)
860
else:
861
return self._multiply_classical(right)
862
863
def _multiply_classical(left, right):
864
return left._multiply_strassen(right, left._ncols + left._nrows)
865
866
cdef Vector _vector_times_matrix_(self, Vector v):
867
cdef Vector_modn_dense w, ans
868
cdef Py_ssize_t i, j
869
cdef mod_int k
870
cdef mod_int x
871
872
M = self._row_ambient_module()
873
w = v
874
ans = M.zero_vector()
875
876
for i from 0 <= i < self._ncols:
877
x = 0
878
k = 0
879
for j from 0 <= j < self._nrows:
880
x += w._entries[j] * self._matrix[j][i]
881
k += 1
882
if k >= self.gather:
883
x %= self.p
884
k = 0
885
ans._entries[i] = x % self.p
886
return ans
887
888
889
########################################################################
890
# LEVEL 3 functionality (Optional)
891
# x * cdef _sub_
892
# * __deepcopy__
893
# * __invert__
894
# * Matrix windows -- only if you need strassen for that base
895
# * Other functions (list them here):
896
# - all row/column operations, but optimized
897
# x - echelon form in place
898
# - Hessenberg forms of matrices
899
########################################################################
900
901
902
def charpoly(self, var='x', algorithm='generic'):
903
"""
904
Returns the characteristic polynomial of self.
905
906
INPUT:
907
908
- ``var`` - a variable name
909
- ``algorithm`` - 'generic' (default)
910
911
EXAMPLES::
912
913
sage: A = Mat(GF(7),3,3)(range(3)*3)
914
sage: A.charpoly()
915
x^3 + 4*x^2
916
917
sage: A = Mat(Integers(6),3,3)(range(9))
918
sage: A.charpoly()
919
x^3
920
"""
921
if algorithm == 'generic':
922
g = matrix_dense.Matrix_dense.charpoly(self, var)
923
else:
924
raise ValueError, "no algorithm '%s'"%algorithm
925
self.cache('charpoly_%s_%s'%(algorithm, var), g)
926
return g
927
928
def minpoly(self, var='x', algorithm='generic', proof=None):
929
"""
930
Returns the minimal polynomial of self.
931
932
INPUT:
933
934
- ``var`` - a variable name
935
- ``algorithm`` - 'generic' (default)
936
937
- ``proof`` -- (default: True); whether to provably return
938
the true minimal polynomial; if False, we only guarantee
939
to return a divisor of the minimal polynomial. There are
940
also certainly cases where the computed results is
941
frequently not exactly equal to the minimal polynomial
942
(but is instead merely a divisor of it).
943
944
WARNING: If proof=True, minpoly is insanely slow compared to
945
proof=False.
946
947
EXAMPLES::
948
949
sage: R.<x>=GF(3)[]
950
sage: A = matrix(GF(3),2,[0,0,1,2])
951
sage: A.minpoly()
952
x^2 + x
953
954
Minpoly with proof=False is *dramatically* ("millions" of
955
times!) faster than minpoly with proof=True. This matters
956
since proof=True is the default, unless you first type
957
''proof.linear_algebra(False)''.::
958
959
sage: A.minpoly(proof=False) in [x, x+1, x^2+x]
960
True
961
"""
962
963
proof = get_proof_flag(proof, "linear_algebra")
964
965
if algorithm == 'generic':
966
raise NotImplementedError, "minimal polynomials are not implemented for Z/nZ"
967
else:
968
raise ValueError, "no algorithm '%s'"%algorithm
969
self.cache('minpoly_%s_%s'%(algorithm, var), g)
970
return g
971
972
def echelonize(self, algorithm="gauss", **kwds):
973
"""
974
Puts self in row echelon form.
975
976
INPUT:
977
978
- ``self`` - a mutable matrix
979
- ``algorithm``- ``'gauss'`` - uses a custom slower `O(n^3)`
980
Gauss elimination implemented in Sage.
981
- ``**kwds`` - these are all ignored
982
983
984
OUTPUT:
985
986
- self is put in reduced row echelon form.
987
- the rank of self is computed and cached
988
- the pivot columns of self are computed and cached.
989
- the fact that self is now in echelon form is recorded and
990
cached so future calls to echelonize return immediately.
991
992
EXAMPLES::
993
994
sage: a = matrix(GF(97),3,4,range(12))
995
sage: a.echelonize(); a
996
[ 1 0 96 95]
997
[ 0 1 2 3]
998
[ 0 0 0 0]
999
sage: a.pivots()
1000
(0, 1)
1001
"""
1002
x = self.fetch('in_echelon_form')
1003
if not x is None: return # already known to be in echelon form
1004
if not self.base_ring().is_field():
1005
#self._echelon_mod_n ()
1006
raise NotImplementedError, "Echelon form not implemented over '%s'."%self.base_ring()
1007
1008
self.check_mutability()
1009
self.clear_cache()
1010
1011
if algorithm == 'gauss':
1012
self._echelon_in_place_classical()
1013
else:
1014
raise ValueError, "algorithm '%s' not known"%algorithm
1015
1016
def _pivots(self):
1017
if not self.fetch('in_echelon_form'):
1018
raise RuntimeError, "self must be in reduced row echelon form first."
1019
pivots = []
1020
cdef Py_ssize_t i, j, nc
1021
nc = self._ncols
1022
cdef mod_int* row
1023
i = 0
1024
while i < self._nrows:
1025
row = self._matrix[i]
1026
for j from i <= j < nc:
1027
if row[j] != 0:
1028
pivots.append(j)
1029
i += 1
1030
break
1031
if j == nc:
1032
break
1033
return pivots
1034
1035
def _echelon_in_place_classical(self):
1036
self.check_mutability()
1037
self.clear_cache()
1038
1039
cdef Py_ssize_t start_row, c, r, nr, nc, i
1040
cdef mod_int p, a, s, t, b
1041
cdef mod_int **m
1042
1043
start_row = 0
1044
p = self.p
1045
m = self._matrix
1046
nr = self._nrows
1047
nc = self._ncols
1048
pivots = []
1049
fifth = self._ncols / 10 + 1
1050
do_verb = (get_verbose() >= 2)
1051
for c from 0 <= c < nc:
1052
if do_verb and (c % fifth == 0 and c>0):
1053
tm = verbose('on column %s of %s'%(c, self._ncols),
1054
level = 2,
1055
caller_name = 'matrix_modn_dense echelon')
1056
#end if
1057
sig_check()
1058
for r from start_row <= r < nr:
1059
a = m[r][c]
1060
if a:
1061
pivots.append(c)
1062
a_inverse = ai.c_inverse_mod_int(a, p)
1063
self._rescale_row_c(r, a_inverse, c)
1064
self.swap_rows_c(r, start_row)
1065
for i from 0 <= i < nr:
1066
if i != start_row:
1067
b = m[i][c]
1068
if b != 0:
1069
self._add_multiple_of_row_c(i, start_row, p-b, c)
1070
start_row = start_row + 1
1071
break
1072
self.cache('pivots', tuple(pivots))
1073
self.cache('in_echelon_form',True)
1074
1075
cdef xgcd_eliminate (self, mod_int * row1, mod_int* row2, Py_ssize_t start_col):
1076
"""
1077
Reduces row1 and row2 by a unimodular transformation using the xgcd
1078
relation between their first coefficients a and b.
1079
1080
INPUT:
1081
1082
1083
- self: a mutable matrix
1084
1085
-````- row1, row2: the two rows to be transformed
1086
(within self)
1087
1088
-```` - start_col: the column of the pivots in row1
1089
and row2. It is assumed that all entries before start_col in row1
1090
and row2 are zero.
1091
1092
1093
OUTPUT:
1094
1095
1096
- g: the gcd of the first elements of row1 and
1097
row2 at column start_col
1098
1099
- put row1 = s \* row1 + t \* row2 row2 = w \*
1100
row1 + t \* row2 where g = sa + tb
1101
"""
1102
cdef mod_int p = self.p
1103
cdef mod_int * row1_p, * row2_p
1104
cdef mod_int tmp
1105
cdef int g, s, t, v, w
1106
cdef Py_ssize_t nc, i
1107
cdef mod_int a = row1[start_col]
1108
cdef mod_int b = row2[start_col]
1109
g = ArithIntObj.c_xgcd_int (a,b,<int*>&s,<int*>&t)
1110
v = a/g
1111
w = -<int>b/g
1112
nc = self.ncols()
1113
1114
# print("In wgcd_eliminate")
1115
for i from start_col <= i < nc:
1116
# print(self)
1117
tmp = ( s * <int>row1[i] + t * <int>row2[i]) % p
1118
# print (tmp,s, <int>row1[i],t,<int>row2[i])
1119
# print (row2[i],w, <int>row1[i],v,<int>row2[i])
1120
row2[i] = (w* <int>row1[i] + v*<int>row2[i]) % p
1121
# print (row2[i],w, <int>row1[i],v,<int>row2[i])
1122
row1[i] = tmp
1123
#print(self)
1124
# print("sortie")
1125
return g
1126
1127
1128
## This is code by William Stein and/or Clement Pernet from SD7. Unfortunately I (W.S.)
1129
## think it is still buggy, since it is so painful to implement with
1130
## unsigned ints. Code to do basically the same thing is in
1131
## matrix_integer_dense, by Burcin Erocal.
1132
## def _echelon_mod_n (self):
1133
## """
1134
## Put self in Hermite normal form modulo n
1135
## (echelonize the matrix over a ring $Z_n$)
1136
1137
## INPUT:
1138
## self: a mutable matrix over $Z_n$
1139
## OUTPUT:
1140
## Transform in place the working matrix into its Hermite
1141
## normal form over Z, using the modulo n algorithm of
1142
## [Hermite Normal form computation using modulo determinant arithmetic,
1143
## Domich Kannan & Trotter, 1987]
1144
## """
1145
## self.check_mutability()
1146
## self.clear_cache()
1147
1148
## cdef Py_ssize_t start_row, nr, nc,
1149
## cdef long c, r, i
1150
## cdef mod_int p, a, a_inverse, b, g
1151
## cdef mod_int **m
1152
## cdef Py_ssize_t start_row = 0
1153
## p = self.p
1154
## m = self._matrix
1155
## nr = self._nrows
1156
## nc = self._ncols
1157
## pivots = []
1158
## cdef Py_ssize_t fifth = self._ncols / 10 + 1
1159
## do_verb = (get_verbose() >= 2)
1160
## for c from 0 <= c < nc:
1161
## if do_verb and (c % fifth == 0 and c>0):
1162
## tm = verbose('on column %s of %s'%(c, self._ncols),
1163
## level = 2,
1164
## caller_name = 'matrix_modn_dense echelon mod n')
1165
1166
## sig_check()
1167
## for r from start_row <= r < nr:
1168
## a = m[r][c]
1169
## if a:
1170
## self.swap_rows_c(r, start_row)
1171
## for i from start_row +1 <= i < nr:
1172
## b = m[i][c]
1173
1174
## if b != 0:
1175
## self.xgcd_eliminate (self._matrix[start_row], self._matrix[i], c)
1176
## verbose('eliminating rows %s and %s', (start_row,i))
1177
## for i from 0 <= i <start_row:
1178
## p = -m[i][c]//m[start_row][c]
1179
## self._add_multiple_of_row_c(i, start_row, p, c)
1180
1181
## pivots.append(m[start_row][c])
1182
## start_row = start_row + 1
1183
## break
1184
## self.cache('pivots',pivots)
1185
## self.cache('in_echelon_form',True)
1186
1187
1188
cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
1189
self._rescale_row_c(row, multiple, start_col)
1190
1191
cdef _rescale_row_c(self, Py_ssize_t row, mod_int multiple, Py_ssize_t start_col):
1192
cdef mod_int r, p
1193
cdef mod_int* v
1194
cdef Py_ssize_t i
1195
p = self.p
1196
v = self._matrix[row]
1197
for i from start_col <= i < self._ncols:
1198
v[i] = (v[i]*multiple) % p
1199
1200
cdef rescale_col_c(self, Py_ssize_t col, multiple, Py_ssize_t start_row):
1201
self._rescale_col_c(col, multiple, start_row)
1202
1203
cdef _rescale_col_c(self, Py_ssize_t col, mod_int multiple, Py_ssize_t start_row):
1204
"""
1205
EXAMPLES::
1206
1207
sage: n=3; b = MatrixSpace(Integers(37),n,n,sparse=False)([1]*n*n)
1208
sage: b
1209
[1 1 1]
1210
[1 1 1]
1211
[1 1 1]
1212
sage: b.rescale_col(1,5)
1213
sage: b
1214
[1 5 1]
1215
[1 5 1]
1216
[1 5 1]
1217
1218
Rescaling need not include the entire row.
1219
1220
::
1221
1222
sage: b.rescale_col(0,2,1); b
1223
[1 5 1]
1224
[2 5 1]
1225
[2 5 1]
1226
1227
Bounds are checked.
1228
1229
::
1230
1231
sage: b.rescale_col(3,2)
1232
Traceback (most recent call last):
1233
...
1234
IndexError: matrix column index out of range
1235
1236
Rescaling by a negative number::
1237
1238
sage: b.rescale_col(2,-3); b
1239
[ 1 5 34]
1240
[ 2 5 34]
1241
[ 2 5 34]
1242
"""
1243
cdef mod_int r, p
1244
cdef mod_int* v
1245
cdef Py_ssize_t i
1246
p = self.p
1247
for i from start_row <= i < self._nrows:
1248
self._matrix[i][col] = (self._matrix[i][col]*multiple) % p
1249
1250
cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple,
1251
Py_ssize_t start_col):
1252
self._add_multiple_of_row_c(row_to, row_from, multiple, start_col)
1253
1254
cdef _add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, mod_int multiple,
1255
Py_ssize_t start_col):
1256
cdef mod_int p
1257
cdef mod_int *v_from, *v_to
1258
1259
p = self.p
1260
v_from = self._matrix[row_from]
1261
v_to = self._matrix[row_to]
1262
1263
cdef Py_ssize_t i, nc
1264
nc = self._ncols
1265
for i from start_col <= i < nc:
1266
v_to[i] = (multiple * v_from[i] + v_to[i]) % p
1267
1268
cdef add_multiple_of_column_c(self, Py_ssize_t col_to, Py_ssize_t col_from, s,
1269
Py_ssize_t start_row):
1270
self._add_multiple_of_column_c(col_to, col_from, s, start_row)
1271
1272
cdef _add_multiple_of_column_c(self, Py_ssize_t col_to, Py_ssize_t col_from, mod_int multiple,
1273
Py_ssize_t start_row):
1274
cdef mod_int p
1275
cdef mod_int **m
1276
1277
m = self._matrix
1278
p = self.p
1279
1280
cdef Py_ssize_t i, nr
1281
nr = self._nrows
1282
for i from start_row <= i < self._nrows:
1283
m[i][col_to] = (m[i][col_to] + multiple * m[i][col_from]) %p
1284
1285
cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
1286
"""
1287
EXAMPLES::
1288
1289
sage: A = matrix(Integers(8), 2,[1,2,3,4])
1290
sage: A.swap_rows(0,1)
1291
sage: A
1292
[3 4]
1293
[1 2]
1294
"""
1295
cdef mod_int* temp
1296
temp = self._matrix[row1]
1297
self._matrix[row1] = self._matrix[row2]
1298
self._matrix[row2] = temp
1299
1300
cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
1301
cdef Py_ssize_t i, nr
1302
cdef mod_int t
1303
cdef mod_int **m
1304
m = self._matrix
1305
nr = self._nrows
1306
for i from 0 <= i < self._nrows:
1307
t = m[i][col1]
1308
m[i][col1] = m[i][col2]
1309
m[i][col2] = t
1310
1311
def hessenbergize(self):
1312
"""
1313
Transforms self in place to its Hessenberg form.
1314
"""
1315
self.check_mutability()
1316
x = self.fetch('in_hessenberg_form')
1317
if not x is None and x: return # already known to be in Hessenberg form
1318
1319
if self._nrows != self._ncols:
1320
raise ArithmeticError, "Matrix must be square to compute Hessenberg form."
1321
1322
cdef Py_ssize_t n
1323
n = self._nrows
1324
1325
cdef mod_int **h
1326
h = self._matrix
1327
1328
cdef mod_int p, r, t, t_inv, u
1329
cdef Py_ssize_t i, j, m
1330
p = self.p
1331
1332
sig_on()
1333
for m from 1 <= m < n-1:
1334
# Search for a nonzero entry in column m-1
1335
i = -1
1336
for r from m+1 <= r < n:
1337
if h[r][m-1]:
1338
i = r
1339
break
1340
1341
if i != -1:
1342
# Found a nonzero entry in column m-1 that is strictly
1343
# below row m. Now set i to be the first nonzero position >=
1344
# m in column m-1.
1345
if h[m][m-1]:
1346
i = m
1347
t = h[i][m-1]
1348
t_inv = ai.c_inverse_mod_int(t,p)
1349
if i > m:
1350
self.swap_rows_c(i,m)
1351
self.swap_columns_c(i,m)
1352
1353
# Now the nonzero entry in position (m,m-1) is t.
1354
# Use t to clear the entries in column m-1 below m.
1355
for j from m+1 <= j < n:
1356
if h[j][m-1]:
1357
u = (h[j][m-1] * t_inv) % p
1358
self._add_multiple_of_row_c(j, m, p - u, 0) # h[j] -= u*h[m]
1359
# To maintain charpoly, do the corresponding
1360
# column operation, which doesn't mess up the
1361
# matrix, since it only changes column m, and
1362
# we're only worried about column m-1 right
1363
# now. Add u*column_j to column_m.
1364
self._add_multiple_of_column_c(m, j, u, 0)
1365
# end for
1366
# end if
1367
# end for
1368
sig_off()
1369
self.cache('in_hessenberg_form',True)
1370
1371
def _charpoly_hessenberg(self, var):
1372
"""
1373
Transforms self in place to its Hessenberg form then computes and
1374
returns the coefficients of the characteristic polynomial of this
1375
matrix.
1376
1377
INPUT:
1378
1379
1380
- ``var`` - name of the indeterminate of the
1381
charpoly.
1382
1383
1384
The characteristic polynomial is represented as a vector of ints,
1385
where the constant term of the characteristic polynomial is the 0th
1386
coefficient of the vector.
1387
"""
1388
if self._nrows != self._ncols:
1389
raise ArithmeticError, "charpoly not defined for non-square matrix."
1390
1391
cdef Py_ssize_t i, m, n,
1392
n = self._nrows
1393
1394
cdef mod_int p, t
1395
p = self.p
1396
1397
# Replace self by its Hessenberg form, and set H to this form
1398
# for notation below.
1399
cdef Matrix_modn_dense H
1400
H = self.__copy__()
1401
H.hessenbergize()
1402
1403
1404
# We represent the intermediate polynomials that come up in
1405
# the calculations as rows of an (n+1)x(n+1) matrix, since
1406
# we've implemented basic arithmetic with such a matrix.
1407
# Please see the generic implementation of charpoly in
1408
# matrix.py to see more clearly how the following algorithm
1409
# actually works. (The implementation is clearer (but slower)
1410
# if one uses polynomials to represent polynomials instead of
1411
# using the rows of a matrix.) Also see Cohen's first GTM,
1412
# Algorithm 2.2.9.
1413
1414
cdef Matrix_modn_dense c
1415
c = self.new_matrix(nrows=n+1,ncols=n+1) # the 0 matrix
1416
c._matrix[0][0] = 1
1417
for m from 1 <= m <= n:
1418
# Set the m-th row of c to (x - H[m-1,m-1])*c[m-1] = x*c[m-1] - H[m-1,m-1]*c[m-1]
1419
# We do this by hand by setting the m-th row to c[m-1]
1420
# shifted to the right by one. We then add
1421
# -H[m-1,m-1]*c[m-1] to the resulting m-th row.
1422
for i from 1 <= i <= n:
1423
c._matrix[m][i] = c._matrix[m-1][i-1]
1424
# the p-.. below is to keep scalar normalized between 0 and p.
1425
c._add_multiple_of_row_c(m, m-1, p - H._matrix[m-1][m-1], 0)
1426
t = 1
1427
for i from 1 <= i < m:
1428
t = (t*H._matrix[m-i][m-i-1]) % p
1429
# Set the m-th row of c to c[m] - t*H[m-i-1,m-1]*c[m-i-1]
1430
c._add_multiple_of_row_c(m, m-i-1, p - (t*H._matrix[m-i-1][m-1])%p, 0)
1431
1432
# The answer is now the n-th row of c.
1433
v = []
1434
for i from 0 <= i <= n:
1435
v.append(int(c._matrix[n][i]))
1436
R = self._base_ring[var] # polynomial ring over the base ring
1437
return R(v)
1438
1439
def rank(self):
1440
"""
1441
Return the rank of this matrix.
1442
1443
EXAMPLES::
1444
1445
sage: m = matrix(GF(7),5,range(25))
1446
sage: m.rank()
1447
2
1448
1449
Rank is not implemented over the integers modulo a composite yet.::
1450
1451
sage: m = matrix(Integers(4), 2, [2,2,2,2])
1452
sage: m.rank()
1453
Traceback (most recent call last):
1454
...
1455
NotImplementedError: Echelon form not implemented over 'Ring of integers modulo 4'.
1456
"""
1457
return matrix_dense.Matrix_dense.rank(self)
1458
1459
def determinant(self):
1460
"""
1461
Return the determinant of this matrix.
1462
1463
EXAMPLES::
1464
1465
sage: m = matrix(GF(101),5,range(25))
1466
sage: m.det()
1467
0
1468
1469
::
1470
1471
sage: m = matrix(Integers(4), 2, [2,2,2,2])
1472
sage: m.det()
1473
0
1474
1475
TESTS::
1476
1477
sage: m = random_matrix(GF(3), 3, 4)
1478
sage: m.determinant()
1479
Traceback (most recent call last):
1480
...
1481
ValueError: self must be a square matrix
1482
"""
1483
if self._nrows != self._ncols:
1484
raise ValueError, "self must be a square matrix"
1485
if self._nrows == 0:
1486
return self._coerce_element(1)
1487
1488
return matrix_dense.Matrix_dense.determinant(self)
1489
1490
def randomize(self, density=1, nonzero=False):
1491
"""
1492
Randomize ``density`` proportion of the entries of this matrix,
1493
leaving the rest unchanged.
1494
1495
INPUT:
1496
1497
- ``density`` - Integer; proportion (roughly) to be considered for
1498
changes
1499
- ``nonzero`` - Bool (default: ``False``); whether the new entries
1500
are forced to be non-zero
1501
1502
OUTPUT:
1503
1504
- None, the matrix is modified in-space
1505
1506
EXAMPLES::
1507
1508
sage: A = matrix(GF(5), 5, 5, 0)
1509
sage: A.randomize(0.5); A
1510
[0 0 0 2 0]
1511
[0 3 0 0 2]
1512
[4 0 0 0 0]
1513
[4 0 0 0 0]
1514
[0 1 0 0 0]
1515
sage: A.randomize(); A
1516
[3 3 2 1 2]
1517
[4 3 3 2 2]
1518
[0 3 3 3 3]
1519
[3 3 2 2 4]
1520
[2 2 2 1 4]
1521
"""
1522
density = float(density)
1523
if density <= 0:
1524
return
1525
if density > 1:
1526
density = float(1)
1527
1528
self.check_mutability()
1529
self.clear_cache()
1530
1531
cdef randstate rstate = current_randstate()
1532
cdef int nc
1533
cdef long pm1
1534
1535
if not nonzero:
1536
# Original code, before adding the ``nonzero`` option.
1537
if density == 1:
1538
for i from 0 <= i < self._nrows*self._ncols:
1539
self._entries[i] = rstate.c_random() % self.p
1540
else:
1541
nc = self._ncols
1542
num_per_row = int(density * nc)
1543
sig_on()
1544
for i from 0 <= i < self._nrows:
1545
for j from 0 <= j < num_per_row:
1546
k = rstate.c_random() % nc
1547
self._matrix[i][k] = rstate.c_random() % self.p
1548
sig_off()
1549
else:
1550
# New code, to implement the ``nonzero`` option.
1551
pm1 = self.p - 1
1552
if density == 1:
1553
for i from 0 <= i < self._nrows*self._ncols:
1554
self._entries[i] = (rstate.c_random() % pm1) + 1
1555
else:
1556
nc = self._ncols
1557
num_per_row = int(density * nc)
1558
sig_on()
1559
for i from 0 <= i < self._nrows:
1560
for j from 0 <= j < num_per_row:
1561
k = rstate.c_random() % nc
1562
self._matrix[i][k] = (rstate.c_random() % pm1) + 1
1563
sig_off()
1564
1565
cdef int _strassen_default_cutoff(self, matrix0.Matrix right) except -2:
1566
# TODO: lots of testing
1567
return 100
1568
1569
cpdef matrix_window(self, Py_ssize_t row=0, Py_ssize_t col=0,
1570
Py_ssize_t nrows=-1, Py_ssize_t ncols=-1,
1571
bint check=1):
1572
"""
1573
Return the requested matrix window.
1574
1575
EXAMPLES::
1576
1577
sage: a = matrix(GF(7),3,range(9)); a
1578
[0 1 2]
1579
[3 4 5]
1580
[6 0 1]
1581
sage: type(a)
1582
<type 'sage.matrix.matrix_modn_dense_float.Matrix_modn_dense_float'>
1583
1584
We test the optional check flag.
1585
1586
::
1587
1588
sage: matrix(GF(7),[1]).matrix_window(0,1,1,1)
1589
Traceback (most recent call last):
1590
...
1591
IndexError: matrix window index out of range
1592
sage: matrix(GF(7),[1]).matrix_window(0,1,1,1, check=False)
1593
Matrix window of size 1 x 1 at (0,1):
1594
[1]
1595
"""
1596
if nrows == -1:
1597
nrows = self._nrows - row
1598
ncols = self._ncols - col
1599
if check and (row < 0 or col < 0 or row + nrows > self._nrows or \
1600
col + ncols > self._ncols):
1601
raise IndexError, "matrix window index out of range"
1602
return matrix_window_modn_dense.MatrixWindow_modn_dense(self, row, col, nrows, ncols)
1603
1604
def _magma_init_(self, magma):
1605
"""
1606
Returns a string representation of self in Magma form.
1607
1608
INPUT:
1609
1610
1611
- ``magma`` - a Magma session
1612
1613
1614
OUTPUT: string
1615
1616
EXAMPLES::
1617
1618
sage: a = matrix(GF(389),2,2,[1..4])
1619
sage: magma(a) # optional - magma
1620
[ 1 2]
1621
[ 3 4]
1622
sage: a._magma_init_(magma) # optional - magma
1623
'Matrix(GF(389),2,2,StringToIntegerSequence("1 2 3 4"))'
1624
1625
A consistency check::
1626
1627
sage: a = random_matrix(GF(13),50); b = random_matrix(GF(13),50)
1628
sage: magma(a*b) == magma(a)*magma(b) # optional - magma
1629
True
1630
"""
1631
s = self.base_ring()._magma_init_(magma)
1632
return 'Matrix(%s,%s,%s,StringToIntegerSequence("%s"))'%(
1633
s, self._nrows, self._ncols, self._export_as_string())
1634
1635
cpdef _export_as_string(self):
1636
"""
1637
Return space separated string of the entries in this matrix.
1638
1639
EXAMPLES::
1640
1641
sage: w = matrix(GF(997),2,3,[1,2,5,-3,8,2]); w
1642
[ 1 2 5]
1643
[994 8 2]
1644
sage: w._export_as_string()
1645
'1 2 5 994 8 2'
1646
"""
1647
cdef int ndigits = len(str(self.p))
1648
1649
cdef Py_ssize_t i, n
1650
cdef char *s, *t
1651
1652
if self._nrows == 0 or self._ncols == 0:
1653
data = ''
1654
else:
1655
n = self._nrows*self._ncols*(ndigits + 1) + 2 # spaces between each number plus trailing null
1656
s = <char*> sage_malloc(n * sizeof(char))
1657
t = s
1658
sig_on()
1659
for i in range(self._nrows * self._ncols):
1660
sprintf(t, "%d ", self._entries[i])
1661
t += strlen(t)
1662
sig_off()
1663
data = str(s)[:-1]
1664
sage_free(s)
1665
return data
1666
1667
def _list(self):
1668
"""
1669
Return list of elements of self. This method is called by self.list().
1670
1671
EXAMPLES::
1672
1673
sage: w = matrix(GF(19), 2, 3, [1..6])
1674
sage: w.list()
1675
[1, 2, 3, 4, 5, 6]
1676
sage: w._list()
1677
[1, 2, 3, 4, 5, 6]
1678
sage: w.list()[0].parent()
1679
Finite Field of size 19
1680
1681
TESTS::
1682
1683
sage: w = random_matrix(GF(3),100)
1684
sage: w.parent()(w.list()) == w
1685
True
1686
"""
1687
cdef Py_ssize_t i, j
1688
F = self.base_ring()
1689
entries = []
1690
for i from 0 <= i < self._nrows:
1691
for j from 0<= j < self._ncols:
1692
entries.append(F(self._matrix[i][j]))
1693
return entries
1694
1695
def lift(self):
1696
"""
1697
Return the lift of this matrix to the integers.
1698
1699
EXAMPLES::
1700
1701
sage: a = matrix(GF(7),2,3,[1..6])
1702
sage: a.lift()
1703
[1 2 3]
1704
[4 5 6]
1705
sage: a.lift().parent()
1706
Full MatrixSpace of 2 by 3 dense matrices over Integer Ring
1707
1708
Subdivisions are preserved when lifting::
1709
1710
sage: a.subdivide([], [1,1]); a
1711
[1||2 3]
1712
[4||5 6]
1713
sage: a.lift()
1714
[1||2 3]
1715
[4||5 6]
1716
"""
1717
cdef Py_ssize_t i, j
1718
1719
cdef Matrix_integer_dense L
1720
L = Matrix_integer_dense.__new__(Matrix_integer_dense,
1721
self.parent().change_ring(ZZ),
1722
0, 0, 0)
1723
cdef mpz_t* L_row
1724
cdef mod_int* A_row
1725
for i from 0 <= i < self._nrows:
1726
L_row = L._matrix[i]
1727
A_row = self._matrix[i]
1728
for j from 0 <= j < self._ncols:
1729
mpz_init_set_si(L_row[j], A_row[j])
1730
L._initialized = 1
1731
L.subdivide(self.subdivisions())
1732
return L
1733
1734
1735
def _matrices_from_rows(self, Py_ssize_t nrows, Py_ssize_t ncols):
1736
"""
1737
Make a list of matrix from the rows of this matrix. This is a
1738
fairly technical function which is used internally, e.g., by
1739
the cyclotomic field linear algebra code.
1740
1741
INPUT:
1742
1743
- ``nrows, ncols`` - integers
1744
1745
OUTPUT:
1746
1747
- ``list`` - a list of matrices
1748
"""
1749
if nrows * ncols != self._ncols:
1750
raise ValueError, "nrows * ncols must equal self's number of columns"
1751
1752
from matrix_space import MatrixSpace
1753
F = self.base_ring()
1754
MS = MatrixSpace(F, nrows, ncols)
1755
1756
cdef Matrix_modn_dense M
1757
cdef Py_ssize_t i
1758
cdef Py_ssize_t n = nrows * ncols
1759
ans = []
1760
for i from 0 <= i < self._nrows:
1761
# Quickly construct a new mod-p matrix
1762
M = Matrix_modn_dense.__new__(Matrix_modn_dense, MS, 0,0,0)
1763
M.p = self.p
1764
M.gather = self.gather
1765
# Set the entries
1766
memcpy(M._entries, self._entries+i*n, sizeof(mod_int)*n)
1767
ans.append(M)
1768
return ans
1769
1770
_matrix_from_rows_of_matrices = staticmethod(__matrix_from_rows_of_matrices)
1771
1772