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