passagemath-ntl 10.8.1a1__cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. passagemath_ntl/__init__.py +3 -0
  2. passagemath_ntl-10.8.1a1.dist-info/METADATA +121 -0
  3. passagemath_ntl-10.8.1a1.dist-info/RECORD +162 -0
  4. passagemath_ntl-10.8.1a1.dist-info/WHEEL +6 -0
  5. passagemath_ntl-10.8.1a1.dist-info/top_level.txt +3 -0
  6. passagemath_ntl.libs/libgf2x-a4cdec90.so.3.0.0 +0 -0
  7. passagemath_ntl.libs/libgmp-6e109695.so.10.5.0 +0 -0
  8. passagemath_ntl.libs/libmpfi-bcd96d8d.so.0.0.0 +0 -0
  9. passagemath_ntl.libs/libmpfr-104d25bb.so.6.2.2 +0 -0
  10. passagemath_ntl.libs/libntl-bacfd860.so.45.0.0 +0 -0
  11. sage/all__sagemath_ntl.py +7 -0
  12. sage/libs/all__sagemath_ntl.py +3 -0
  13. sage/libs/mpfi/__init__.pxd +287 -0
  14. sage/libs/mpfi/types.pxd +10 -0
  15. sage/libs/ntl/GF2.pxd +18 -0
  16. sage/libs/ntl/GF2E.pxd +28 -0
  17. sage/libs/ntl/GF2EX.pxd +12 -0
  18. sage/libs/ntl/GF2X.pxd +81 -0
  19. sage/libs/ntl/ZZ.pxd +93 -0
  20. sage/libs/ntl/ZZX.pxd +85 -0
  21. sage/libs/ntl/ZZ_p.pxd +28 -0
  22. sage/libs/ntl/ZZ_pE.pxd +37 -0
  23. sage/libs/ntl/ZZ_pEX.pxd +106 -0
  24. sage/libs/ntl/ZZ_pX.pxd +122 -0
  25. sage/libs/ntl/__init__.py +4 -0
  26. sage/libs/ntl/all.py +72 -0
  27. sage/libs/ntl/conversion.pxd +106 -0
  28. sage/libs/ntl/convert.cpython-314t-x86_64-linux-gnu.so +0 -0
  29. sage/libs/ntl/convert.pxd +7 -0
  30. sage/libs/ntl/convert.pyx +38 -0
  31. sage/libs/ntl/decl.pxi +18 -0
  32. sage/libs/ntl/error.cpython-314t-x86_64-linux-gnu.so +0 -0
  33. sage/libs/ntl/error.pyx +63 -0
  34. sage/libs/ntl/lzz_p.pxd +20 -0
  35. sage/libs/ntl/lzz_pX.pxd +59 -0
  36. sage/libs/ntl/mat_GF2.pxd +30 -0
  37. sage/libs/ntl/mat_GF2E.pxd +30 -0
  38. sage/libs/ntl/mat_ZZ.pxd +59 -0
  39. sage/libs/ntl/misc.pxi +33 -0
  40. sage/libs/ntl/ntl_GF2.cpython-314t-x86_64-linux-gnu.so +0 -0
  41. sage/libs/ntl/ntl_GF2.pxd +5 -0
  42. sage/libs/ntl/ntl_GF2.pyx +281 -0
  43. sage/libs/ntl/ntl_GF2E.cpython-314t-x86_64-linux-gnu.so +0 -0
  44. sage/libs/ntl/ntl_GF2E.pxd +8 -0
  45. sage/libs/ntl/ntl_GF2E.pyx +488 -0
  46. sage/libs/ntl/ntl_GF2EContext.cpython-314t-x86_64-linux-gnu.so +0 -0
  47. sage/libs/ntl/ntl_GF2EContext.pxd +9 -0
  48. sage/libs/ntl/ntl_GF2EContext.pyx +134 -0
  49. sage/libs/ntl/ntl_GF2EX.cpython-314t-x86_64-linux-gnu.so +0 -0
  50. sage/libs/ntl/ntl_GF2EX.pxd +10 -0
  51. sage/libs/ntl/ntl_GF2EX.pyx +251 -0
  52. sage/libs/ntl/ntl_GF2X.cpython-314t-x86_64-linux-gnu.so +0 -0
  53. sage/libs/ntl/ntl_GF2X.pxd +5 -0
  54. sage/libs/ntl/ntl_GF2X.pyx +771 -0
  55. sage/libs/ntl/ntl_GF2X_linkage.pxi +404 -0
  56. sage/libs/ntl/ntl_ZZ.cpython-314t-x86_64-linux-gnu.so +0 -0
  57. sage/libs/ntl/ntl_ZZ.pxd +7 -0
  58. sage/libs/ntl/ntl_ZZ.pyx +541 -0
  59. sage/libs/ntl/ntl_ZZX.cpython-314t-x86_64-linux-gnu.so +0 -0
  60. sage/libs/ntl/ntl_ZZX.pxd +7 -0
  61. sage/libs/ntl/ntl_ZZX.pyx +1206 -0
  62. sage/libs/ntl/ntl_ZZ_p.cpython-314t-x86_64-linux-gnu.so +0 -0
  63. sage/libs/ntl/ntl_ZZ_p.pxd +10 -0
  64. sage/libs/ntl/ntl_ZZ_p.pyx +508 -0
  65. sage/libs/ntl/ntl_ZZ_pContext.cpython-314t-x86_64-linux-gnu.so +0 -0
  66. sage/libs/ntl/ntl_ZZ_pContext.pxd +22 -0
  67. sage/libs/ntl/ntl_ZZ_pContext.pyx +201 -0
  68. sage/libs/ntl/ntl_ZZ_pE.cpython-314t-x86_64-linux-gnu.so +0 -0
  69. sage/libs/ntl/ntl_ZZ_pE.pxd +11 -0
  70. sage/libs/ntl/ntl_ZZ_pE.pyx +349 -0
  71. sage/libs/ntl/ntl_ZZ_pEContext.cpython-314t-x86_64-linux-gnu.so +0 -0
  72. sage/libs/ntl/ntl_ZZ_pEContext.pxd +23 -0
  73. sage/libs/ntl/ntl_ZZ_pEContext.pyx +226 -0
  74. sage/libs/ntl/ntl_ZZ_pEX.cpython-314t-x86_64-linux-gnu.so +0 -0
  75. sage/libs/ntl/ntl_ZZ_pEX.pxd +10 -0
  76. sage/libs/ntl/ntl_ZZ_pEX.pyx +1255 -0
  77. sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi +420 -0
  78. sage/libs/ntl/ntl_ZZ_pX.cpython-314t-x86_64-linux-gnu.so +0 -0
  79. sage/libs/ntl/ntl_ZZ_pX.pxd +17 -0
  80. sage/libs/ntl/ntl_ZZ_pX.pyx +1532 -0
  81. sage/libs/ntl/ntl_lzz_p.cpython-314t-x86_64-linux-gnu.so +0 -0
  82. sage/libs/ntl/ntl_lzz_p.pxd +8 -0
  83. sage/libs/ntl/ntl_lzz_p.pyx +440 -0
  84. sage/libs/ntl/ntl_lzz_pContext.cpython-314t-x86_64-linux-gnu.so +0 -0
  85. sage/libs/ntl/ntl_lzz_pContext.pxd +7 -0
  86. sage/libs/ntl/ntl_lzz_pContext.pyx +137 -0
  87. sage/libs/ntl/ntl_lzz_pX.cpython-314t-x86_64-linux-gnu.so +0 -0
  88. sage/libs/ntl/ntl_lzz_pX.pxd +10 -0
  89. sage/libs/ntl/ntl_lzz_pX.pyx +902 -0
  90. sage/libs/ntl/ntl_mat_GF2.cpython-314t-x86_64-linux-gnu.so +0 -0
  91. sage/libs/ntl/ntl_mat_GF2.pxd +8 -0
  92. sage/libs/ntl/ntl_mat_GF2.pyx +612 -0
  93. sage/libs/ntl/ntl_mat_GF2E.cpython-314t-x86_64-linux-gnu.so +0 -0
  94. sage/libs/ntl/ntl_mat_GF2E.pxd +10 -0
  95. sage/libs/ntl/ntl_mat_GF2E.pyx +752 -0
  96. sage/libs/ntl/ntl_mat_ZZ.cpython-314t-x86_64-linux-gnu.so +0 -0
  97. sage/libs/ntl/ntl_mat_ZZ.pxd +6 -0
  98. sage/libs/ntl/ntl_mat_ZZ.pyx +1522 -0
  99. sage/libs/ntl/ntl_tools.pxd +3 -0
  100. sage/libs/ntl/ntlwrap.h +53 -0
  101. sage/libs/ntl/ntlwrap_impl.h +743 -0
  102. sage/libs/ntl/types.pxd +157 -0
  103. sage/libs/ntl/vec_GF2.pxd +26 -0
  104. sage/libs/ntl/vec_GF2E.pxd +2 -0
  105. sage/matrix/all__sagemath_ntl.py +1 -0
  106. sage/matrix/matrix_modn_dense_double.pxd +10 -0
  107. sage/matrix/matrix_modn_dense_float.pxd +9 -0
  108. sage/matrix/matrix_modn_dense_template.pxi +3475 -0
  109. sage/matrix/matrix_modn_dense_template_header.pxi +15 -0
  110. sage/matrix/matrix_modn_sparse.pxd +8 -0
  111. sage/misc/all__sagemath_ntl.py +1 -0
  112. sage/rings/all__sagemath_ntl.py +7 -0
  113. sage/rings/bernmm.cpython-314t-x86_64-linux-gnu.so +0 -0
  114. sage/rings/bernmm.pyx +161 -0
  115. sage/rings/bernoulli_mod_p.cpython-314t-x86_64-linux-gnu.so +0 -0
  116. sage/rings/bernoulli_mod_p.pyx +312 -0
  117. sage/rings/finite_rings/all__sagemath_ntl.py +1 -0
  118. sage/rings/finite_rings/finite_field_ntl_gf2e.py +309 -0
  119. sage/rings/finite_rings/residue_field_ntl_gf2e.cpython-314t-x86_64-linux-gnu.so +0 -0
  120. sage/rings/finite_rings/residue_field_ntl_gf2e.pyx +140 -0
  121. sage/rings/padics/all__sagemath_ntl.py +5 -0
  122. sage/rings/padics/padic_ZZ_pX_CA_element.cpython-314t-x86_64-linux-gnu.so +0 -0
  123. sage/rings/padics/padic_ZZ_pX_CA_element.pxd +25 -0
  124. sage/rings/padics/padic_ZZ_pX_CA_element.pyx +2366 -0
  125. sage/rings/padics/padic_ZZ_pX_CR_element.cpython-314t-x86_64-linux-gnu.so +0 -0
  126. sage/rings/padics/padic_ZZ_pX_CR_element.pxd +33 -0
  127. sage/rings/padics/padic_ZZ_pX_CR_element.pyx +3282 -0
  128. sage/rings/padics/padic_ZZ_pX_FM_element.cpython-314t-x86_64-linux-gnu.so +0 -0
  129. sage/rings/padics/padic_ZZ_pX_FM_element.pxd +12 -0
  130. sage/rings/padics/padic_ZZ_pX_FM_element.pyx +1737 -0
  131. sage/rings/padics/padic_ZZ_pX_element.cpython-314t-x86_64-linux-gnu.so +0 -0
  132. sage/rings/padics/padic_ZZ_pX_element.pxd +6 -0
  133. sage/rings/padics/padic_ZZ_pX_element.pyx +919 -0
  134. sage/rings/padics/padic_ext_element.cpython-314t-x86_64-linux-gnu.so +0 -0
  135. sage/rings/padics/padic_ext_element.pxd +38 -0
  136. sage/rings/padics/padic_ext_element.pyx +513 -0
  137. sage/rings/padics/pow_computer_ext.cpython-314t-x86_64-linux-gnu.so +0 -0
  138. sage/rings/padics/pow_computer_ext.pxd +107 -0
  139. sage/rings/padics/pow_computer_ext.pyx +2402 -0
  140. sage/rings/polynomial/all__sagemath_ntl.py +1 -0
  141. sage/rings/polynomial/evaluation_ntl.cpython-314t-x86_64-linux-gnu.so +0 -0
  142. sage/rings/polynomial/evaluation_ntl.pxd +7 -0
  143. sage/rings/polynomial/evaluation_ntl.pyx +70 -0
  144. sage/rings/polynomial/polynomial_gf2x.cpython-314t-x86_64-linux-gnu.so +0 -0
  145. sage/rings/polynomial/polynomial_gf2x.pxd +10 -0
  146. sage/rings/polynomial/polynomial_gf2x.pyx +364 -0
  147. sage/rings/polynomial/polynomial_integer_dense_ntl.cpython-314t-x86_64-linux-gnu.so +0 -0
  148. sage/rings/polynomial/polynomial_integer_dense_ntl.pxd +8 -0
  149. sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +1128 -0
  150. sage/rings/polynomial/polynomial_modn_dense_ntl.cpython-314t-x86_64-linux-gnu.so +0 -0
  151. sage/rings/polynomial/polynomial_modn_dense_ntl.pxd +36 -0
  152. sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +2050 -0
  153. sage/rings/polynomial/polynomial_template.pxi +839 -0
  154. sage/rings/polynomial/polynomial_template_header.pxi +11 -0
  155. sage/rings/polynomial/polynomial_zz_pex.cpython-314t-x86_64-linux-gnu.so +0 -0
  156. sage/rings/polynomial/polynomial_zz_pex.pxd +12 -0
  157. sage/rings/polynomial/polynomial_zz_pex.pyx +778 -0
  158. sage/rings/real_mpfi.pxd +50 -0
  159. sage/schemes/all__sagemath_ntl.py +1 -0
  160. sage/schemes/hyperelliptic_curves/all__sagemath_ntl.py +1 -0
  161. sage/schemes/hyperelliptic_curves/hypellfrob.cpython-314t-x86_64-linux-gnu.so +0 -0
  162. sage/schemes/hyperelliptic_curves/hypellfrob.pyx +253 -0
@@ -0,0 +1,3475 @@
1
+ # sage_setup: distribution = sagemath-ntl
2
+ r"""
3
+ Dense matrices over `\ZZ/n\ZZ` for `n` small using the LinBox library (FFLAS/FFPACK)
4
+
5
+ FFLAS/FFPACK are libraries to provide BLAS/LAPACK-style routines for
6
+ working with finite fields. Additionally, these routines reduce to
7
+ BLAS/LAPACK routines using floating point arithmetic.
8
+
9
+ EXAMPLES::
10
+
11
+ sage: A = matrix(GF(127), 7, 7, range(49))
12
+ sage: A*A
13
+ [ 2 23 44 65 86 107 1]
14
+ [ 15 85 28 98 41 111 54]
15
+ [ 28 20 12 4 123 115 107]
16
+ [ 41 82 123 37 78 119 33]
17
+ [ 54 17 107 70 33 123 86]
18
+ [ 67 79 91 103 115 0 12]
19
+ [ 80 14 75 9 70 4 65]
20
+ sage: A.rank()
21
+ 2
22
+
23
+ sage: A = matrix(GF(127), 4, 4, [106, 98, 24, 84, 108, 7, 94, 71, 96, 100, 15, 42, 80, 56, 72, 35])
24
+ sage: A.rank()
25
+ 4
26
+ sage: v = vector(GF(127), 4, (100, 93, 47, 110))
27
+ sage: x = A.solve_right(v)
28
+ sage: A*x == v
29
+ True
30
+
31
+ AUTHORS:
32
+
33
+ - William Stein (2004-2006): some functions in this file were copied
34
+ from ``matrix_modn_dense.pyx`` which was mainly written by William
35
+ Stein
36
+ - Clement Pernet (2010): LinBox related functions in this file were
37
+ taken from linbox-sage.C by Clement Pernet
38
+ - Burcin Erocal (2010-2011): most of the functions present in this file
39
+ - Martin Albrecht (2011): some polishing, bug fixes, documentation
40
+ - Rob Beezer (2011): documentation
41
+
42
+ TESTS:
43
+
44
+ We test corner cases for multiplication::
45
+
46
+ sage: v0 = vector(GF(3),[])
47
+ sage: v1 = vector(GF(3),[1])
48
+ sage: m00 = matrix(GF(3),0,0,[])
49
+ sage: m01 = matrix(GF(3),0,1,[])
50
+ sage: m10 = matrix(GF(3),1,0,[])
51
+ sage: m11 = matrix(GF(3),1,1,[1])
52
+ sage: good = [ (v0,m00), (v0,m01), (v1,m10), (v1,m11), (m00,v0), (m10,v0), (m01,v1), (m11,v1), (m00,m00), (m01,m10), (m10,m01), (m11,m11) ]
53
+ sage: for v, m in good:
54
+ ....: print('{} x {} = {}'.format(v, m, v * m))
55
+ () x [] = ()
56
+ () x [] = (0)
57
+ (1) x [] = ()
58
+ (1) x [1] = (1)
59
+ [] x () = ()
60
+ [] x () = (0)
61
+ [] x (1) = ()
62
+ [1] x (1) = (1)
63
+ [] x [] = []
64
+ [] x [] = []
65
+ [] x [] = [0]
66
+ [1] x [1] = [1]
67
+
68
+ sage: bad = [ (v1,m00), (v1,m01), (v0,m10), (v0,m11), (m00,v1), (m10,v1), (m01,v0), (m11,v0), (m01,m01), (m10,m10), (m11,m01), (m10,m11) ]
69
+ sage: for v, m in bad:
70
+ ....: try:
71
+ ....: v*m
72
+ ....: print('Uncaught dimension mismatch!')
73
+ ....: except (IndexError, TypeError, ArithmeticError):
74
+ ....: pass
75
+ """
76
+
77
+ # ****************************************************************************
78
+ # Copyright (C) 2004,2005,2006 William Stein <wstein@gmail.com>
79
+ # Copyright (C) 2011 Burcin Erocal <burcin@erocal.org>
80
+ # Copyright (C) 2011 Martin Albrecht <martinralbrecht@googlemail.com>
81
+ # Copyright (C) 2011 Rob Beezer
82
+ #
83
+ # This program is free software: you can redistribute it and/or modify
84
+ # it under the terms of the GNU General Public License as published by
85
+ # the Free Software Foundation, either version 2 of the License, or
86
+ # (at your option) any later version.
87
+ # https://www.gnu.org/licenses/
88
+ # ****************************************************************************
89
+
90
+ from libc.stdint cimport uint64_t
91
+ from cpython.bytes cimport *
92
+
93
+ from cysignals.memory cimport check_malloc, check_allocarray, check_calloc, sig_malloc, sig_free
94
+ from cysignals.signals cimport sig_check, sig_on, sig_off
95
+
96
+ from sage.libs.gmp.mpz cimport *
97
+ from sage.libs.linbox.fflas cimport FFLAS_TRANSPOSE, FflasNoTrans, FflasTrans, \
98
+ FfpackTileRecursive, FflasLeft, FflasRight, vector, list as std_list, \
99
+ RankProfileFromLU, PLUQtoEchelonPermutation, MathPerm2LAPACKPerm
100
+
101
+ from libcpp cimport bool
102
+ from sage.parallel.parallelism import Parallelism
103
+
104
+ cimport sage.rings.fast_arith
105
+ cdef sage.rings.fast_arith.arith_int ArithIntObj
106
+ ArithIntObj = sage.rings.fast_arith.arith_int()
107
+
108
+ # for copying/pickling
109
+ from libc.string cimport memcpy
110
+ from libc.stdio cimport snprintf
111
+
112
+ from sage.modules.vector_modn_dense cimport Vector_modn_dense
113
+
114
+ from sage.arith.misc import is_prime
115
+ from sage.structure.element cimport (Element, Vector, Matrix,
116
+ ModuleElement, RingElement)
117
+ from sage.matrix.matrix_dense cimport Matrix_dense
118
+ from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense
119
+ from sage.rings.finite_rings.integer_mod cimport IntegerMod_int, IntegerMod_abstract
120
+ from sage.misc.timing import cputime
121
+ from sage.misc.verbose import verbose, get_verbose
122
+ from sage.rings.integer cimport Integer
123
+ from sage.rings.integer_ring import ZZ
124
+ from sage.structure.proof.proof import get_flag as get_proof_flag
125
+ from sage.structure.richcmp cimport rich_to_bool
126
+ from sage.misc.randstate cimport randstate, current_randstate
127
+ import sage.matrix.matrix_space as matrix_space
128
+ from sage.matrix.args cimport SparseEntry, MatrixArgs_init
129
+
130
+
131
+ from sage.cpython.string cimport char_to_str
132
+
133
+ cdef long num = 1
134
+ cdef bint little_endian = (<char*>(&num))[0]
135
+
136
+ cdef inline celement_invert(celement a, celement n):
137
+ """
138
+ Invert the finite field element `a` modulo `n`.
139
+ """
140
+ # This is copied from linbox source linbox/field/modular-float.h
141
+ # The extended Euclidean algorithm
142
+ cdef int x_int, y_int, q, tx, ty, temp
143
+ x_int = <int>n
144
+ y_int = <int>a
145
+ tx = 0
146
+ ty = 1
147
+
148
+ while y_int != 0:
149
+ # always: gcd (n,residue) = gcd (x_int,y_int)
150
+ # sx*n + tx*residue = x_int
151
+ # sy*n + ty*residue = y_int
152
+ q = x_int / y_int # integer quotient
153
+ temp = y_int
154
+ y_int = x_int - q * y_int
155
+ x_int = temp
156
+ temp = ty
157
+ ty = tx - q * ty
158
+ tx = temp
159
+
160
+ if tx < 0:
161
+ tx += <int>n
162
+
163
+ # now x_int = gcd (n,residue)
164
+ return <celement>tx
165
+
166
+ cdef inline bint linbox_is_zero(celement modulus, celement* entries, Py_ssize_t nrows, Py_ssize_t ncols) except -1:
167
+ """
168
+ Return 1 if all entries of this matrix are zero.
169
+ """
170
+ cdef Py_ssize_t i, j
171
+ for i in range(nrows):
172
+ for j in range(ncols):
173
+ if (entries+i*ncols+j)[0] != 0:
174
+ return 0
175
+ return 1
176
+
177
+ cdef inline linbox_echelonize(celement modulus, celement* entries, Py_ssize_t nrows, Py_ssize_t ncols):
178
+ """
179
+ In-place transform this matrix into its reduced row echelon form, and return
180
+ the rank `r` of this matrix as well as two lists of length `r`, sorted
181
+ increasingly. The first list gives the column rank profile of this matrix
182
+ (which is also that of its reduced row echelon form) while the second list
183
+ gives the row rank profile of the input matrix (which may differ from that
184
+ of its reduced row echelon form).
185
+ """
186
+ if linbox_is_zero(modulus, entries, nrows, ncols):
187
+ return 0, [], []
188
+
189
+ cdef Py_ssize_t i, j
190
+ cdef ModField *F = new ModField(<long>modulus)
191
+ cdef size_t* P = <size_t*>check_allocarray(nrows, sizeof(size_t))
192
+ cdef size_t* Q = <size_t*>check_allocarray(ncols, sizeof(size_t))
193
+ cdef size_t* Qperm = <size_t*>check_allocarray(ncols, sizeof(size_t))
194
+
195
+ cdef size_t* rrp = <size_t*>check_allocarray(nrows, sizeof(size_t))
196
+ cdef size_t* crp = <size_t*>check_allocarray(ncols, sizeof(size_t))
197
+
198
+ cdef Py_ssize_t r
199
+ cdef size_t nbthreads
200
+ nbthreads = Parallelism().get('linbox')
201
+ cdef bool transform = False
202
+ if nrows * ncols > 1000:
203
+ sig_on()
204
+ if nbthreads > 1 :
205
+ r = pReducedRowEchelonForm(F[0], nrows, ncols, <ModField.Element*>entries,
206
+ ncols, P, Q, transform, nbthreads, FfpackTileRecursive)
207
+ else :
208
+ r = ReducedRowEchelonForm(F[0], nrows, ncols, <ModField.Element*>entries, ncols, P, Q,
209
+ transform, FfpackTileRecursive)
210
+ if nrows * ncols > 1000:
211
+ sig_off()
212
+
213
+ # extract row and column rank profiles
214
+ RankProfileFromLU(P, nrows, r, rrp, FfpackTileRecursive)
215
+ RankProfileFromLU(Q, ncols, r, crp, FfpackTileRecursive)
216
+
217
+ cdef list pivots = [int(crp[i]) for i in range(r)]
218
+ cdef list pivot_rows = [int(rrp[i]) for i in range(r)]
219
+
220
+ # row permutation into echelon
221
+ PLUQtoEchelonPermutation(ncols, r, Q, Qperm)
222
+ applyP(F[0], FflasLeft, FflasNoTrans, ncols, 0, r, <ModField.Element*>entries, ncols, Qperm)
223
+
224
+ # fill top left with identity
225
+ for i in range(nrows):
226
+ for j in range(r):
227
+ (entries + i*ncols + j)[0] = 0
228
+ if i<r:
229
+ (entries + i*ncols + i)[0] = 1
230
+
231
+ # column permutation to put pivots in correct columns
232
+ piv = 0
233
+ ipiv = r
234
+ idx = 0
235
+ while idx < r and ipiv < ncols:
236
+ if crp[idx] > piv:
237
+ crp[ipiv] = piv
238
+ ipiv += 1
239
+ piv += 1
240
+ else:
241
+ idx += 1
242
+ piv += 1
243
+ while ipiv < ncols:
244
+ crp[ipiv] = piv
245
+ ipiv += 1
246
+ piv += 1
247
+
248
+ MathPerm2LAPACKPerm(Q, crp, ncols)
249
+
250
+ applyP(F[0], FflasRight, FflasNoTrans, nrows, 0, ncols, <ModField.Element*>entries, ncols, Q)
251
+
252
+ sig_free(P)
253
+ sig_free(Q)
254
+ sig_free(Qperm)
255
+ sig_free(rrp)
256
+ sig_free(crp)
257
+ del F
258
+ return r, pivots, pivot_rows
259
+
260
+ cdef inline linbox_echelonize_efd(celement modulus, celement* entries, Py_ssize_t nrows, Py_ssize_t ncols):
261
+ """
262
+ In-place transform this matrix into its reduced row echelon form, and return
263
+ the rank `r` of this matrix as well as a list of length `r`, sorted
264
+ increasingly. This list gives the column rank profile of this matrix
265
+ (which is also that of its reduced row echelon form).
266
+ """
267
+ # See trac #13878: This is to avoid sending invalid data to linbox,
268
+ # which would yield a segfault in Sage's debug version. TODO: Fix
269
+ # that bug upstream.
270
+ if nrows == 0 or ncols == 0:
271
+ return 0, []
272
+
273
+ cdef ModField *F = new ModField(<long>modulus)
274
+ cdef DenseMatrix *A = new DenseMatrix(F[0], nrows, ncols)
275
+
276
+ cdef Py_ssize_t i, j
277
+ for i in range(nrows):
278
+ for j in range(ncols):
279
+ A.setEntry(i, j, entries[i*ncols+j])
280
+
281
+ cdef Py_ssize_t r = reducedRowEchelonize(A[0])
282
+ for i in range(nrows):
283
+ for j in range(ncols):
284
+ entries[i*ncols+j] = <celement>A.getEntry(i, j)
285
+
286
+ cdef Py_ssize_t ii = 0
287
+ cdef list pivots = []
288
+ for i in range(r):
289
+ for j in range(ii, ncols):
290
+ if entries[i*ncols+j] == 1:
291
+ pivots.append(j)
292
+ ii = j+1
293
+ break
294
+
295
+ del F
296
+ return r, pivots
297
+
298
+ cdef inline celement *linbox_copy(celement modulus, celement *entries, Py_ssize_t nrows, Py_ssize_t ncols) except? NULL:
299
+ """
300
+ Create a copy of the entries array.
301
+ """
302
+ cdef celement *entries_copy = <celement*>check_allocarray(nrows * ncols, sizeof(celement))
303
+ memcpy(entries_copy, entries, sizeof(celement)*nrows*ncols)
304
+ return entries_copy
305
+
306
+ cdef inline int linbox_rank(celement modulus, celement* entries, Py_ssize_t nrows, Py_ssize_t ncols) except -1:
307
+ """
308
+ Return the rank of this matrix.
309
+ """
310
+ cdef ModField *F = new ModField(<long>modulus)
311
+
312
+ cdef celement *cpy = linbox_copy(modulus, entries, nrows, ncols)
313
+
314
+ cdef Py_ssize_t r
315
+ cdef size_t nbthreads
316
+ nbthreads = Parallelism().get('linbox')
317
+ if nrows * ncols > 1000:
318
+ sig_on()
319
+ if nbthreads > 1:
320
+ r = pRank(F[0], nrows, ncols, <ModField.Element*>cpy, ncols, nbthreads)
321
+ else:
322
+ r = Rank(F[0], nrows, ncols, <ModField.Element*>cpy, ncols)
323
+ if nrows * ncols > 1000:
324
+ sig_off()
325
+ sig_free(cpy)
326
+ del F
327
+ return r
328
+
329
+ cdef inline celement linbox_det(celement modulus, celement* entries, Py_ssize_t n) noexcept:
330
+ """
331
+ Return the determinant of this matrix.
332
+ """
333
+ cdef ModField *F = new ModField(<long>modulus)
334
+ cdef celement *cpy = linbox_copy(modulus, entries, n, n)
335
+
336
+ cdef celement d = 0
337
+ cdef size_t nbthreads
338
+ nbthreads = Parallelism().get('linbox')
339
+
340
+ if n*n > 1000:
341
+ sig_on()
342
+ if nbthreads > 1 :
343
+ pDet(F[0], d, n, <ModField.Element*>cpy, n, nbthreads)
344
+ else :
345
+ Det(F[0], d, n, <ModField.Element*>cpy, n)
346
+ if n*n > 1000:
347
+ sig_off()
348
+ sig_free(cpy)
349
+ del F
350
+ return d
351
+
352
+ cdef inline celement linbox_matrix_matrix_multiply(celement modulus, celement* ans, celement* A, celement* B, Py_ssize_t m, Py_ssize_t n, Py_ssize_t k) noexcept:
353
+ """
354
+ C = A*B
355
+ """
356
+ cdef ModField *F = new ModField(<long>modulus)
357
+ cdef ModField.Element one = 0, zero = 0
358
+ F[0].init(one, <int>1)
359
+ F[0].init(zero, <int>0)
360
+
361
+ cdef size_t nbthreads
362
+ nbthreads = Parallelism().get('linbox')
363
+
364
+ if m*n*k > 100000:
365
+ sig_on()
366
+ if nbthreads > 1 :
367
+ pfgemm(F[0], FflasNoTrans, FflasNoTrans, m, n, k, one,
368
+ <ModField.Element*>A, k, <ModField.Element*>B, n, zero,
369
+ <ModField.Element*>ans, n, nbthreads)
370
+ else:
371
+ fgemm(F[0], FflasNoTrans, FflasNoTrans, m, n, k, one,
372
+ <ModField.Element*>A, k, <ModField.Element*>B, n, zero,
373
+ <ModField.Element*>ans, n)
374
+
375
+ if m*n*k > 100000:
376
+ sig_off()
377
+
378
+ del F
379
+
380
+ cdef inline int linbox_matrix_vector_multiply(celement modulus, celement* C, celement* A, celement* b, Py_ssize_t m, Py_ssize_t n, FFLAS_TRANSPOSE trans) noexcept:
381
+ """
382
+ C = A*v
383
+ """
384
+ cdef ModField *F = new ModField(<long>modulus)
385
+ cdef ModField.Element one = 0, zero = 0
386
+ F.init(one, <int>1)
387
+ F.init(zero, <int>0)
388
+
389
+ if m*n > 100000:
390
+ sig_on()
391
+
392
+ fgemv(F[0], trans, m, n, one, <ModField.Element*>A, n, <ModField.Element*>b, 1,
393
+ zero, <ModField.Element*>C, 1)
394
+
395
+ if m*n > 100000:
396
+ sig_off()
397
+
398
+ del F
399
+
400
+ cdef inline linbox_minpoly(celement modulus, Py_ssize_t nrows, celement* entries):
401
+ """
402
+ Compute the minimal polynomial.
403
+ """
404
+ cdef Py_ssize_t i
405
+ cdef ModField *F = new ModField(<long>modulus)
406
+ cdef vector[ModField.Element] *minP = new vector[ModField.Element]()
407
+
408
+ if nrows*nrows > 1000:
409
+ sig_on()
410
+ MinPoly(F[0], minP[0], nrows, <ModField.Element*>entries, nrows)
411
+ if nrows*nrows > 1000:
412
+ sig_off()
413
+
414
+ l = [<celement>minP.at(i) for i in range(minP.size())]
415
+
416
+ del F
417
+ return l
418
+
419
+ cdef inline linbox_charpoly(celement modulus, Py_ssize_t nrows, celement* entries):
420
+ """
421
+ Compute the characteristic polynomial.
422
+ """
423
+ cdef Py_ssize_t i
424
+ cdef ModField *F = new ModField(<long>modulus)
425
+ cdef ModDensePolyRing * R = new ModDensePolyRing(F[0])
426
+ cdef ModDensePoly P
427
+
428
+ cdef celement *cpy = linbox_copy(modulus, entries, nrows, nrows)
429
+
430
+ if nrows * nrows > 1000:
431
+ sig_on()
432
+ CharPoly(R[0], P, nrows, <ModField.Element*>cpy, nrows)
433
+ if nrows * nrows > 1000:
434
+ sig_off()
435
+
436
+ sig_free(cpy)
437
+
438
+ l = []
439
+ for i in range(P.size()):
440
+ l.append(<celement>P[i])
441
+
442
+ del F
443
+ del R
444
+ return l
445
+
446
+
447
+ cpdef __matrix_from_rows_of_matrices(X):
448
+ """
449
+ Return a matrix whose row ``i`` is constructed from the entries of
450
+ matrix ``X[i]``.
451
+
452
+ INPUT:
453
+
454
+ - ``X`` -- a nonempty list of matrices of the same size mod a
455
+ single modulus `n`
456
+
457
+ EXAMPLES::
458
+
459
+ sage: X = [random_matrix(GF(17), 4, 4) for _ in range(10)]
460
+ sage: Y = X[0]._matrix_from_rows_of_matrices(X) # indirect doctest
461
+ sage: all(list(Y[i]) == X[i].list() for i in range(10))
462
+ True
463
+
464
+ OUTPUT: a single matrix mod ``p`` whose ``i``-th row is ``X[i].list()``.
465
+
466
+ .. NOTE::
467
+
468
+ Do not call this function directly but use the static method
469
+ ``Matrix_modn_dense_float/double._matrix_from_rows_of_matrices``
470
+ """
471
+ # The code below is just a fast version of the following:
472
+ # from constructor import matrix
473
+ # K = X[0].base_ring()
474
+ # v = sum([y.list() for y in X],[])
475
+ # return matrix(K, len(X), X[0].nrows()*X[0].ncols(), v)
476
+
477
+ cdef Matrix_modn_dense_template T
478
+ cdef Py_ssize_t i, n, m
479
+ n = len(X)
480
+
481
+ T = X[0]
482
+ m = T._nrows * T._ncols
483
+ cdef Matrix_modn_dense_template A = T.new_matrix(nrows=n, ncols=m)
484
+
485
+ for i from 0 <= i < n:
486
+ T = X[i]
487
+ memcpy(A._entries + i*m, T._entries, sizeof(celement)*m)
488
+ return A
489
+
490
+
491
+ cdef class Matrix_modn_dense_template(Matrix_dense):
492
+ def __cinit__(self, *args, bint zeroed_alloc=True, **kwds):
493
+ cdef long p = self._base_ring.characteristic()
494
+ self.p = p
495
+ if p >= MAX_MODULUS:
496
+ raise OverflowError("p (=%s) must be < %s." % (p, MAX_MODULUS))
497
+
498
+ if zeroed_alloc:
499
+ self._entries = <celement *> check_calloc(self._nrows * self._ncols, sizeof(celement))
500
+ else:
501
+ self._entries = <celement *> check_allocarray(self._nrows * self._ncols, sizeof(celement))
502
+
503
+ # TODO: it is a bit of a waste to allocate _matrix when ncols=0. Though some
504
+ # of the code expects self._matrix[i] to be valid, even though it points to
505
+ # an empty vector.
506
+ self._matrix = <celement **> check_allocarray(self._nrows, sizeof(celement*))
507
+ if self._nrows == 0:
508
+ return
509
+
510
+ cdef Py_ssize_t i
511
+ self._matrix[0] = self._entries
512
+ for i in range(self._nrows - 1):
513
+ self._matrix[i + 1] = self._matrix[i] + self._ncols
514
+
515
+ def __dealloc__(self):
516
+ """
517
+ TESTS::
518
+
519
+ sage: import gc
520
+ sage: for i in range(10): # needs sage.libs.linbox sage.rings.finite_rings
521
+ ....: A = random_matrix(GF(7),1000,1000)
522
+ ....: B = random_matrix(Integers(10),1000,1000)
523
+ ....: C = random_matrix(GF(16007),1000,1000)
524
+ ....: D = random_matrix(Integers(1000),1000,1000)
525
+ ....: del A
526
+ ....: del B
527
+ ....: del C
528
+ ....: del D
529
+ ....: _ = gc.collect()
530
+ """
531
+ sig_free(self._entries)
532
+ sig_free(self._matrix)
533
+
534
+ def __init__(self, parent, entries=None, copy=None, bint coerce=True):
535
+ r"""
536
+ Create a new matrix.
537
+
538
+ INPUT:
539
+
540
+ - ``parent`` -- a matrix space
541
+
542
+ - ``entries`` -- see :func:`matrix`
543
+
544
+ - ``copy`` -- ignored (for backwards compatibility)
545
+
546
+ - ``coerce`` -- perform modular reduction first?
547
+
548
+ EXAMPLES::
549
+
550
+ sage: A = random_matrix(GF(3),1000,1000)
551
+ sage: type(A)
552
+ <class 'sage.matrix.matrix_modn_dense_float.Matrix_modn_dense_float'>
553
+ sage: A = random_matrix(Integers(10),1000,1000)
554
+ sage: type(A)
555
+ <class 'sage.matrix.matrix_modn_dense_float.Matrix_modn_dense_float'>
556
+ sage: A = random_matrix(Integers(2^16),1000,1000)
557
+ sage: type(A)
558
+ <class 'sage.matrix.matrix_modn_dense_double.Matrix_modn_dense_double'>
559
+
560
+ TESTS::
561
+
562
+ sage: Matrix(GF(7), 2, 2, [-1, int(-2), GF(7)(-3), 1/4])
563
+ [6 5]
564
+ [4 2]
565
+
566
+ sage: Matrix(GF(6434383), 2, 2, [-1, int(-2), GF(7)(-3), 1/4]) # needs sage.rings.finite_rings
567
+ [6434382 6434381]
568
+ [ 4 1608596]
569
+
570
+ sage: Matrix(Integers(4618990), 2, 2, [-1, int(-2), GF(7)(-3), 1/7]) # needs sage.rings.finite_rings
571
+ [4618989 4618988]
572
+ [ 4 2639423]
573
+
574
+ sage: Matrix(IntegerModRing(200), [[int(2**128+1), int(2**256+1), int(2**1024+1)]]) # needs sage.rings.finite_rings
575
+ [ 57 137 17]
576
+ """
577
+ ma = MatrixArgs_init(parent, entries)
578
+ cdef long i, j
579
+ it = ma.iter(convert=False, sparse=True)
580
+ R = ma.base
581
+ p = R.characteristic()
582
+
583
+ for t in it:
584
+ se = <SparseEntry>t
585
+ x = se.entry
586
+ v = self._matrix[se.i]
587
+ if type(x) is IntegerMod_int and (<IntegerMod_int>x)._parent is R:
588
+ v[se.j] = <celement>(<IntegerMod_int>x).ivalue
589
+ elif type(x) is Integer:
590
+ if coerce:
591
+ v[se.j] = mpz_fdiv_ui((<Integer>x).value, p)
592
+ else:
593
+ v[se.j] = mpz_get_ui((<Integer>x).value)
594
+ elif coerce:
595
+ v[se.j] = R(x)
596
+ else:
597
+ v[se.j] = <celement>x
598
+
599
+ cdef long _hash_(self) except -1:
600
+ """
601
+ EXAMPLES::
602
+
603
+ sage: B = random_matrix(GF(127),3,3)
604
+ sage: B.set_immutable()
605
+ sage: _ = {B:0} # indirect doctest
606
+
607
+ sage: M = random_matrix(GF(7), 10, 10)
608
+ sage: M.set_immutable()
609
+ sage: _ = hash(M)
610
+ sage: MZ = M.change_ring(ZZ)
611
+ sage: MZ.set_immutable()
612
+ sage: hash(MZ) == hash(M)
613
+ True
614
+ sage: MS = M.sparse_matrix()
615
+ sage: MS.set_immutable()
616
+ sage: hash(MS) == hash(M)
617
+ True
618
+
619
+ TESTS::
620
+
621
+ sage: A = matrix(GF(2),2,0)
622
+ sage: hash(A)
623
+ Traceback (most recent call last):
624
+ ...
625
+ TypeError: mutable matrices are unhashable
626
+ sage: A.set_immutable()
627
+ sage: hash(A)
628
+ 0
629
+ """
630
+ cdef long C[5]
631
+ self.get_hash_constants(C)
632
+
633
+ cdef long h = 0, k, l
634
+ cdef Py_ssize_t i, j
635
+ cdef celement* row
636
+ sig_on()
637
+ for i in range(self._nrows):
638
+ k = C[0] if i == 0 else C[1] + C[2] * i
639
+ row = self._matrix[i]
640
+ for j in range(self._ncols):
641
+ l = C[3] * (i - j) * (i ^ j)
642
+ h += (k ^ l) * <long>(row[j])
643
+ h *= C[4]
644
+ sig_off()
645
+
646
+ if h == -1:
647
+ return -2
648
+ return h
649
+
650
+ def _pickle(self):
651
+ """
652
+ Utility function for pickling.
653
+
654
+ If the prime is small enough to fit in a byte, then it is
655
+ stored as a contiguous string of bytes (to save
656
+ space). Otherwise, memcpy is used to copy the raw data in the
657
+ platforms native format. Endianness is dealt with when
658
+ unpickling.
659
+
660
+ EXAMPLES::
661
+
662
+ sage: m = matrix(Integers(128), 3, 3, [ord(c) for c in "Hi there!"]); m
663
+ [ 72 105 32]
664
+ [116 104 101]
665
+ [114 101 33]
666
+ sage: m._pickle()
667
+ ((1, ..., ...'Hi there!'), 10)
668
+
669
+ .. todo::
670
+
671
+ The upcoming buffer protocol would be useful to not have
672
+ to do any copying.
673
+ """
674
+ cdef Py_ssize_t i, j
675
+ cdef unsigned char* us
676
+ cdef mod_int *um
677
+ cdef unsigned char* row_us
678
+ cdef mod_int *row_um
679
+ cdef long word_size
680
+ cdef celement *row_self
681
+
682
+ if self.p <= 0xFF:
683
+ word_size = sizeof(unsigned char)
684
+ else:
685
+ word_size = sizeof(mod_int)
686
+
687
+ cdef void *buf = check_allocarray(self._nrows * self._ncols, word_size)
688
+
689
+ sig_on()
690
+ try:
691
+ if word_size == sizeof(unsigned char):
692
+ us = <unsigned char*>buf
693
+ for i in range(self._nrows):
694
+ row_self = self._matrix[i]
695
+ row_us = us + i*self._ncols
696
+ for j in range(self._ncols):
697
+ row_us[j] = <mod_int>row_self[j]
698
+ else:
699
+ um = <mod_int*>buf
700
+ for i in range(self._nrows):
701
+ row_self = self._matrix[i]
702
+ row_um = um + i*self._ncols
703
+ for j in range(self._ncols):
704
+ row_um[j] = <mod_int>row_self[j]
705
+
706
+ s = PyBytes_FromStringAndSize(<char*>buf, word_size * self._nrows * self._ncols)
707
+ finally:
708
+ sig_free(buf)
709
+ sig_off()
710
+ return (word_size, little_endian, s), 10
711
+
712
+ def _unpickle(self, data, int version):
713
+ """
714
+ TESTS:
715
+
716
+ Test for char-sized modulus::
717
+
718
+ sage: A = random_matrix(GF(7), 5, 9)
719
+ sage: data, version = A._pickle()
720
+ sage: B = A.parent()(0)
721
+ sage: B._unpickle(data, version)
722
+ sage: B == A
723
+ True
724
+
725
+ And for larger modulus::
726
+
727
+ sage: # needs sage.rings.finite_rings
728
+ sage: A = random_matrix(GF(1009), 51, 5)
729
+ sage: data, version = A._pickle()
730
+ sage: B = A.parent()(0)
731
+ sage: B._unpickle(data, version)
732
+ sage: B == A
733
+ True
734
+
735
+ Now test all the bit-packing options::
736
+
737
+ sage: A = matrix(Integers(1000), 2, 2)
738
+ sage: A._unpickle((1, True, b'\x01\x02\xFF\x00'), 10)
739
+ sage: A
740
+ [ 1 2]
741
+ [255 0]
742
+
743
+ sage: A = matrix(Integers(1000), 1, 2)
744
+ sage: A._unpickle((4, True, b'\x02\x01\x00\x00\x01\x00\x00\x00'), 10)
745
+ sage: A
746
+ [258 1]
747
+ sage: A._unpickle((4, False, b'\x00\x00\x02\x01\x00\x00\x01\x03'), 10)
748
+ sage: A
749
+ [513 259]
750
+ sage: A._unpickle((8, True, b'\x03\x01\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00'), 10)
751
+ sage: A
752
+ [259 5]
753
+ sage: A._unpickle((8, False, b'\x00\x00\x00\x00\x00\x00\x02\x08\x00\x00\x00\x00\x00\x00\x01\x04'), 10)
754
+ sage: A
755
+ [520 260]
756
+
757
+ Now make sure it works in context::
758
+
759
+ sage: A = random_matrix(Integers(33), 31, 31)
760
+ sage: loads(dumps(A)) == A
761
+ True
762
+ sage: A = random_matrix(Integers(3333), 31, 31)
763
+ sage: loads(dumps(A)) == A
764
+ True
765
+ """
766
+ if version < 10:
767
+ return Matrix_dense._unpickle(self, data, version)
768
+
769
+ cdef Py_ssize_t i, j
770
+ cdef unsigned char* us
771
+ cdef long word_size
772
+ cdef celement *row_self
773
+ cdef bint little_endian_data
774
+ cdef char* buf
775
+ cdef Py_ssize_t buflen
776
+ cdef Py_ssize_t expectedlen
777
+ cdef mod_int v
778
+
779
+ if version == 10:
780
+ word_size, little_endian_data, s = data
781
+ expectedlen = word_size * self._nrows * self._ncols
782
+
783
+ PyBytes_AsStringAndSize(s, &buf, &buflen)
784
+ if buflen != expectedlen:
785
+ raise ValueError("incorrect size in matrix pickle (expected %d, got %d)" % (expectedlen, buflen))
786
+
787
+ sig_on()
788
+ try:
789
+ if word_size == 1:
790
+ us = <unsigned char*>buf
791
+ for i from 0 <= i < self._nrows:
792
+ row_self = self._matrix[i]
793
+ for j from 0 <= j < self._ncols:
794
+ row_self[j] = <celement>(us[0])
795
+ us += word_size
796
+
797
+ elif word_size >= 4 and little_endian_data:
798
+ us = <unsigned char*>buf
799
+ for i from 0 <= i < self._nrows:
800
+ row_self = self._matrix[i]
801
+ for j from 0 <= j < self._ncols:
802
+ v = <mod_int>(us[0])
803
+ v += <mod_int>(us[1]) << 8
804
+ v += <mod_int>(us[2]) << 16
805
+ v += <mod_int>(us[3]) << 24
806
+ row_self[j] = <celement>v
807
+ us += word_size
808
+
809
+ elif word_size >= 4 and not little_endian_data:
810
+ us = <unsigned char*>buf
811
+ for i from 0 <= i < self._nrows:
812
+ row_self = self._matrix[i]
813
+ for j from 0 <= j < self._ncols:
814
+ v = <mod_int>(us[word_size-1])
815
+ v += <mod_int>(us[word_size-2]) << 8
816
+ v += <mod_int>(us[word_size-3]) << 16
817
+ v += <mod_int>(us[word_size-4]) << 24
818
+ row_self[j] = <celement>v
819
+ us += word_size
820
+
821
+ else:
822
+ raise ValueError("unknown matrix pickle format")
823
+ finally:
824
+ sig_off()
825
+ else:
826
+ raise ValueError("unknown matrix pickle version")
827
+
828
+ def __neg__(self):
829
+ """
830
+ EXAMPLES::
831
+
832
+ sage: A = matrix(GF(19), 3, 3, range(9)); A
833
+ [0 1 2]
834
+ [3 4 5]
835
+ [6 7 8]
836
+
837
+ sage: -A
838
+ [ 0 18 17]
839
+ [16 15 14]
840
+ [13 12 11]
841
+ """
842
+ cdef Py_ssize_t i, j
843
+ cdef Matrix_modn_dense_template M
844
+ cdef celement p = self.p
845
+
846
+ M = self.__class__.__new__(self.__class__, self._parent,
847
+ None, None, None, zeroed_alloc=False)
848
+
849
+ sig_on()
850
+ for i in range(self._nrows*self._ncols):
851
+ if self._entries[i]:
852
+ M._entries[i] = p - self._entries[i]
853
+ else:
854
+ M._entries[i] = 0
855
+ sig_off()
856
+ return M
857
+
858
+ cpdef _lmul_(self, Element left):
859
+ """
860
+ EXAMPLES::
861
+
862
+ sage: A = matrix(GF(101), 3, 3, range(9)); A
863
+ [0 1 2]
864
+ [3 4 5]
865
+ [6 7 8]
866
+ sage: A * 5
867
+ [ 0 5 10]
868
+ [15 20 25]
869
+ [30 35 40]
870
+ sage: A * 50
871
+ [ 0 50 100]
872
+ [ 49 99 48]
873
+ [ 98 47 97]
874
+
875
+ ::
876
+
877
+ sage: A = random_matrix(Integers(60), 400, 500)
878
+ sage: 3*A + 9*A == 12*A
879
+ True
880
+ """
881
+ cdef Py_ssize_t i, j
882
+ cdef Matrix_modn_dense_template M
883
+ cdef celement p = self.p
884
+ cdef celement a = left
885
+
886
+ M = self.__class__.__new__(self.__class__, self._parent,
887
+ None, None, None, zeroed_alloc=False)
888
+
889
+ sig_on()
890
+ for i in range(self._nrows*self._ncols):
891
+ M._entries[i] = (a*self._entries[i]) % p
892
+ sig_off()
893
+ return M
894
+
895
+ def __copy__(self):
896
+ """
897
+ EXAMPLES::
898
+
899
+ sage: A = random_matrix(GF(127), 100, 100)
900
+ sage: copy(A) == A
901
+ True
902
+ sage: copy(A) is A
903
+ False
904
+ """
905
+ cdef Matrix_modn_dense_template A
906
+ A = self.__class__.__new__(self.__class__, self._parent,
907
+ None, None, None, zeroed_alloc=False)
908
+ memcpy(A._entries, self._entries, sizeof(celement)*self._nrows*self._ncols)
909
+ if self._subdivisions is not None:
910
+ A.subdivide(*self.subdivisions())
911
+ return A
912
+
913
+ cpdef _add_(self, right):
914
+ r"""
915
+ Add two dense matrices over `\Z/n\Z`.
916
+
917
+ INPUT:
918
+
919
+ - ``right`` -- a matrix
920
+
921
+ EXAMPLES::
922
+
923
+ sage: A = MatrixSpace(GF(19),3)(range(9))
924
+ sage: A+A
925
+ [ 0 2 4]
926
+ [ 6 8 10]
927
+ [12 14 16]
928
+
929
+ sage: B = MatrixSpace(GF(19),3)(range(9))
930
+ sage: B.swap_rows(1,2)
931
+ sage: A+B
932
+ [ 0 2 4]
933
+ [ 9 11 13]
934
+ [ 9 11 13]
935
+
936
+ sage: B+A
937
+ [ 0 2 4]
938
+ [ 9 11 13]
939
+ [ 9 11 13]
940
+ """
941
+ cdef Py_ssize_t i
942
+ cdef celement k, p
943
+ cdef Matrix_modn_dense_template M
944
+
945
+ M = self.__class__.__new__(self.__class__, self._parent,
946
+ None, None, None, zeroed_alloc=False)
947
+ p = self.p
948
+ cdef celement* other_ent = (<Matrix_modn_dense_template>right)._entries
949
+
950
+ sig_on()
951
+ for i in range(self._nrows*self._ncols):
952
+ k = self._entries[i] + other_ent[i]
953
+ M._entries[i] = k - (k >= p) * p
954
+ sig_off()
955
+ return M
956
+
957
+ cpdef _sub_(self, right):
958
+ r"""
959
+ Subtract two dense matrices over `\Z/n\Z`.
960
+
961
+ EXAMPLES::
962
+
963
+ sage: A = matrix(GF(11), 3, 3, range(9)); A
964
+ [0 1 2]
965
+ [3 4 5]
966
+ [6 7 8]
967
+
968
+ sage: A - 4
969
+ [7 1 2]
970
+ [3 0 5]
971
+ [6 7 4]
972
+
973
+ sage: A - matrix(GF(11), 3, 3, range(1, 19, 2))
974
+ [10 9 8]
975
+ [ 7 6 5]
976
+ [ 4 3 2]
977
+ """
978
+ cdef Py_ssize_t i
979
+ cdef celement k, p
980
+ cdef Matrix_modn_dense_template M
981
+
982
+ M = self.__class__.__new__(self.__class__, self._parent, None, None, None, zeroed_alloc=False)
983
+ p = self.p
984
+ cdef celement* other_ent = (<Matrix_modn_dense_template>right)._entries
985
+
986
+ sig_on()
987
+ for i in range(self._nrows*self._ncols):
988
+ k = p + self._entries[i] - other_ent[i]
989
+ M._entries[i] = k - (k >= p) * p
990
+ sig_off()
991
+ return M
992
+
993
+ cpdef _richcmp_(self, right, int op):
994
+ r"""
995
+ Compare two dense matrices over `\Z/n\Z`.
996
+
997
+ EXAMPLES::
998
+
999
+ sage: A = matrix(GF(17), 4, range(3, 83, 5)); A
1000
+ [ 3 8 13 1]
1001
+ [ 6 11 16 4]
1002
+ [ 9 14 2 7]
1003
+ [12 0 5 10]
1004
+ sage: A == A
1005
+ True
1006
+ sage: B = A - 3; B
1007
+ [ 0 8 13 1]
1008
+ [ 6 8 16 4]
1009
+ [ 9 14 16 7]
1010
+ [12 0 5 7]
1011
+ sage: B < A
1012
+ True
1013
+ sage: B > A
1014
+ False
1015
+ sage: B == A
1016
+ False
1017
+ sage: B + 3 == A
1018
+ True
1019
+
1020
+ ::
1021
+
1022
+ sage: A = matrix(ZZ, 10, 10, range(1000, 1100))
1023
+ sage: A.change_ring(GF(17)) == A.change_ring(GF(17))
1024
+ True
1025
+ sage: A.change_ring(GF(17)) == A.change_ring(GF(19)) # needs sage.rings.finite_rings
1026
+ False
1027
+ sage: A.change_ring(GF(17)) == A.change_ring(Integers(2000)) # needs sage.rings.finite_rings
1028
+ False
1029
+ sage: A.change_ring(GF(17)) == A.change_ring(Integers(2000)) # needs sage.rings.finite_rings
1030
+ False
1031
+ """
1032
+ cdef Py_ssize_t i
1033
+ cdef celement* other_ent = (<Matrix_modn_dense_template>right)._entries
1034
+ sig_on()
1035
+ for i in range(self._nrows * self._ncols):
1036
+ if self._entries[i] < other_ent[i]:
1037
+ sig_off()
1038
+ return rich_to_bool(op, -1)
1039
+ elif self._entries[i] > other_ent[i]:
1040
+ sig_off()
1041
+ return rich_to_bool(op, 1)
1042
+ sig_off()
1043
+ return rich_to_bool(op, 0)
1044
+
1045
+ cdef _matrix_times_matrix_(self, Matrix right):
1046
+ """
1047
+ Return ``self*right``.
1048
+
1049
+ INPUT:
1050
+
1051
+ - ``right``- a matrix
1052
+
1053
+ EXAMPLES::
1054
+
1055
+ sage: A = random_matrix(GF(7),2,2)
1056
+ sage: B = random_matrix(GF(7),2,2)
1057
+ sage: C = A*B
1058
+ sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2))
1059
+ True
1060
+
1061
+ sage: MS = parent(A)
1062
+ sage: MS(3) * A == 3*A
1063
+ True
1064
+
1065
+ ::
1066
+
1067
+ sage: A = random_matrix(GF(17), 201, 117)
1068
+ sage: B = random_matrix(GF(17), 117, 195)
1069
+ sage: C = random_matrix(GF(17), 201, 117)
1070
+ sage: D = random_matrix(GF(17), 117, 195)
1071
+
1072
+ sage: E = (A+C)*(B+D)
1073
+
1074
+ sage: F = A*B + A*D + C*B + C*D
1075
+
1076
+ sage: E == F
1077
+ True
1078
+
1079
+ sage: A = random_matrix(GF(17), 200, 200)
1080
+ sage: MS = parent(A)
1081
+ sage: (MS(0) * A) == 0
1082
+ True
1083
+
1084
+ sage: (MS(1) * A) == A
1085
+ True
1086
+
1087
+ ::
1088
+
1089
+ sage: A = random_matrix(Integers(8),2,2)
1090
+ sage: B = random_matrix(Integers(8),2,2)
1091
+ sage: C = A*B
1092
+ sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2))
1093
+ True
1094
+
1095
+ sage: MS = parent(A)
1096
+ sage: MS(3) * A == 3*A
1097
+ True
1098
+
1099
+ ::
1100
+
1101
+ sage: A = random_matrix(Integers(16), 201, 117)
1102
+ sage: B = random_matrix(Integers(16), 117, 195)
1103
+ sage: C = random_matrix(Integers(16), 201, 117)
1104
+ sage: D = random_matrix(Integers(16), 117, 195)
1105
+
1106
+ sage: E = (A+C)*(B+D)
1107
+
1108
+ sage: F = A*B + A*D + C*B + C*D
1109
+
1110
+ sage: E == F
1111
+ True
1112
+
1113
+ sage: A = random_matrix(Integers(16), 200, 200)
1114
+ sage: MS = parent(A)
1115
+ sage: (MS(0) * A) == 0
1116
+ True
1117
+
1118
+ sage: (MS(1) * A) == A
1119
+ True
1120
+
1121
+ ::
1122
+
1123
+ sage: A = random_matrix(GF(16007),2,2) # needs sage.rings.finite_rings
1124
+ sage: B = random_matrix(GF(16007),2,2) # needs sage.rings.finite_rings
1125
+ sage: C = A*B
1126
+ sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2))
1127
+ True
1128
+
1129
+ sage: MS = parent(A)
1130
+ sage: MS(3) * A == 3*A
1131
+ True
1132
+
1133
+ ::
1134
+
1135
+ sage: # needs sage.rings.finite_rings
1136
+ sage: A = random_matrix(GF(15991), 201, 117)
1137
+ sage: B = random_matrix(GF(15991), 117, 195)
1138
+ sage: C = random_matrix(GF(15991), 201, 117)
1139
+ sage: D = random_matrix(GF(15991), 117, 195)
1140
+
1141
+ sage: E = (A+C)*(B+D)
1142
+
1143
+ sage: F = A*B + A*D + C*B + C*D
1144
+
1145
+ sage: E == F
1146
+ True
1147
+
1148
+ ::
1149
+
1150
+ sage: A = random_matrix(GF(16007), 200, 200) # needs sage.rings.finite_rings
1151
+ sage: MS = parent(A)
1152
+ sage: (MS(0) * A) == 0
1153
+ True
1154
+
1155
+ sage: (MS(1) * A) == A
1156
+ True
1157
+
1158
+ ::
1159
+
1160
+ sage: A = random_matrix(Integers(1008),2,2)
1161
+ sage: B = random_matrix(Integers(1008),2,2)
1162
+ sage: C = A*B
1163
+ sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2))
1164
+ True
1165
+
1166
+ sage: MS = parent(A)
1167
+ sage: MS(3) * A == 3*A
1168
+ True
1169
+
1170
+ ::
1171
+
1172
+ sage: A = random_matrix(Integers(1600), 201, 117)
1173
+ sage: B = random_matrix(Integers(1600), 117, 195)
1174
+ sage: C = random_matrix(Integers(1600), 201, 117)
1175
+ sage: D = random_matrix(Integers(1600), 117, 195)
1176
+
1177
+ sage: E = (A+C)*(B+D)
1178
+
1179
+ sage: F = A*B + A*D + C*B + C*D
1180
+
1181
+ sage: E == F
1182
+ True
1183
+ """
1184
+ if get_verbose() >= 2:
1185
+ verbose('mod-p multiply of %s x %s matrix by %s x %s matrix modulo %s' % (
1186
+ self._nrows, self._ncols, right._nrows, right._ncols, self.p))
1187
+
1188
+ if self._ncols != right._nrows:
1189
+ raise ArithmeticError("right's number of rows must match self's number of columns")
1190
+
1191
+ cdef int e
1192
+ cdef Matrix_modn_dense_template ans, B
1193
+
1194
+ ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
1195
+
1196
+ B = right
1197
+
1198
+ linbox_matrix_matrix_multiply(self.p, ans._entries, self._entries,
1199
+ B._entries, self._nrows, B._ncols, B._nrows)
1200
+
1201
+ return ans
1202
+
1203
+ cdef _vector_times_matrix_(self, Vector v):
1204
+ """
1205
+ Return ``v*self``.
1206
+
1207
+ INPUT:
1208
+
1209
+ - ``v`` -- a vector
1210
+
1211
+ EXAMPLES::
1212
+
1213
+ sage: A = random_matrix(GF(17), 10, 20)
1214
+ sage: v = random_vector(GF(17), 10)
1215
+ sage: matrix(v*A) == matrix(v)*A
1216
+ True
1217
+
1218
+ sage: A = random_matrix(Integers(126), 10, 20)
1219
+ sage: v = random_vector(Integers(126), 10)
1220
+ sage: matrix(v*A) == matrix(v)*A
1221
+ True
1222
+
1223
+ sage: A = random_matrix(GF(4796509), 10, 20) # needs sage.rings.finite_rings
1224
+ sage: v = random_vector(GF(4796509), 10) # needs sage.rings.finite_rings
1225
+ sage: matrix(v*A) == matrix(v)*A
1226
+ True
1227
+
1228
+ sage: A = random_matrix(Integers(16337), 10, 20)
1229
+ sage: v = random_vector(Integers(16337), 10) # needs sage.rings.finite_rings
1230
+ sage: matrix(v*A) == matrix(v)*A
1231
+ True
1232
+ """
1233
+ if not isinstance(v, Vector_modn_dense):
1234
+ return (self.new_matrix(1, self._nrows, entries=v.list()) * self)[0]
1235
+
1236
+ M = self.row_ambient_module()
1237
+ cdef Vector_modn_dense c = M.zero_vector()
1238
+
1239
+ if self._ncols == 0 or self._nrows == 0:
1240
+ return c
1241
+
1242
+ cdef Py_ssize_t i
1243
+ cdef Vector_modn_dense b = v
1244
+
1245
+ cdef celement *_b = <celement*>check_allocarray(self._nrows, sizeof(celement))
1246
+ cdef celement *_c = <celement*>check_allocarray(self._ncols, sizeof(celement))
1247
+
1248
+ for i in range(self._nrows):
1249
+ _b[i] = <celement>b._entries[i]
1250
+
1251
+ linbox_matrix_vector_multiply(self.p, _c, self._entries, _b, self._nrows, self._ncols, FflasTrans)
1252
+
1253
+ for i in range(self._ncols):
1254
+ c._entries[i] = <mod_int>_c[i]
1255
+ sig_free(_b)
1256
+ sig_free(_c)
1257
+ return c
1258
+
1259
+ cdef _matrix_times_vector_(self, Vector v):
1260
+ """
1261
+ Return ``self*v``.
1262
+
1263
+ EXAMPLES::
1264
+
1265
+ sage: A = random_matrix(GF(17), 10, 20)
1266
+ sage: v = random_vector(GF(17), 20)
1267
+ sage: matrix(A*v).transpose() == A*matrix(v).transpose()
1268
+ True
1269
+
1270
+ sage: A = random_matrix(Integers(126), 10, 20)
1271
+ sage: v = random_vector(Integers(126), 20)
1272
+ sage: matrix(A*v).transpose() == A*matrix(v).transpose()
1273
+ True
1274
+
1275
+ sage: A = random_matrix(GF(4796509), 10, 20) # needs sage.rings.finite_rings
1276
+ sage: v = random_vector(GF(4796509), 20) # needs sage.rings.finite_rings
1277
+ sage: matrix(A*v).transpose() == A*matrix(v).transpose()
1278
+ True
1279
+
1280
+ sage: A = random_matrix(Integers(16337), 10, 20)
1281
+ sage: v = random_vector(Integers(16337), 20) # needs sage.rings.finite_rings
1282
+ sage: matrix(A*v).transpose() == A*matrix(v).transpose()
1283
+ True
1284
+ """
1285
+ if not isinstance(v, Vector_modn_dense):
1286
+ r = (self * self.new_matrix(nrows=len(v), ncols=1, entries=v.list()))
1287
+ from sage.modules.free_module_element import vector
1288
+ return vector(r.list())
1289
+
1290
+ M = self.column_ambient_module()
1291
+ cdef Vector_modn_dense c = M.zero_vector()
1292
+
1293
+ if self._ncols == 0 or self._nrows == 0:
1294
+ return c
1295
+
1296
+ cdef Py_ssize_t i
1297
+ cdef Vector_modn_dense b = v
1298
+
1299
+ cdef celement *_b = <celement*>check_allocarray(self._ncols, sizeof(celement))
1300
+ cdef celement *_c = <celement*>check_allocarray(self._nrows, sizeof(celement))
1301
+
1302
+ for i in range(self._ncols):
1303
+ _b[i] = <celement>b._entries[i]
1304
+
1305
+ linbox_matrix_vector_multiply(self.p, _c, self._entries, _b, self._nrows, self._ncols, FflasNoTrans)
1306
+
1307
+ for i in range(self._nrows):
1308
+ c._entries[i] = <mod_int>_c[i]
1309
+ sig_free(_b)
1310
+ sig_free(_c)
1311
+ return c
1312
+
1313
+ ########################################################################
1314
+ # LEVEL 3 functionality (Optional)
1315
+ # x * cdef _sub_
1316
+ # * __deepcopy__
1317
+ # * __invert__
1318
+ # * Matrix windows -- only if you need strassen for that base
1319
+ # * Other functions (list them here):
1320
+ # - all row/column operations, but optimized
1321
+ # x - echelon form in place
1322
+ # - Hessenberg forms of matrices
1323
+ ########################################################################
1324
+
1325
+ def charpoly(self, var='x', algorithm='linbox'):
1326
+ """
1327
+ Return the characteristic polynomial of ``self``.
1328
+
1329
+ INPUT:
1330
+
1331
+ - ``var`` -- a variable name
1332
+
1333
+ - ``algorithm`` -- 'generic', 'linbox' or 'all' (default: linbox)
1334
+
1335
+ EXAMPLES::
1336
+
1337
+ sage: A = random_matrix(GF(19), 10, 10)
1338
+ sage: B = copy(A)
1339
+ sage: char_p = A.characteristic_polynomial()
1340
+ sage: char_p(A) == 0
1341
+ True
1342
+ sage: B == A # A is not modified
1343
+ True
1344
+
1345
+ sage: min_p = A.minimal_polynomial(proof=True)
1346
+ sage: min_p.divides(char_p)
1347
+ True
1348
+
1349
+ ::
1350
+
1351
+ sage: A = random_matrix(GF(2916337), 7, 7) # needs sage.rings.finite_rings
1352
+ sage: B = copy(A)
1353
+ sage: char_p = A.characteristic_polynomial()
1354
+ sage: char_p(A) == 0
1355
+ True
1356
+ sage: B == A # A is not modified
1357
+ True
1358
+
1359
+ sage: min_p = A.minimal_polynomial(proof=True)
1360
+ sage: min_p.divides(char_p)
1361
+ True
1362
+
1363
+ sage: A = Mat(Integers(6),3,3)(range(9))
1364
+ sage: A.charpoly()
1365
+ x^3
1366
+
1367
+ TESTS::
1368
+
1369
+ sage: for i in range(10):
1370
+ ....: A = random_matrix(GF(17), 50, 50, density=0.1)
1371
+ ....: _ = A.characteristic_polynomial(algorithm='all')
1372
+
1373
+ sage: A = random_matrix(GF(19), 0, 0)
1374
+ sage: A.minimal_polynomial()
1375
+ 1
1376
+
1377
+ sage: A = random_matrix(GF(19), 0, 1)
1378
+ sage: A.minimal_polynomial()
1379
+ Traceback (most recent call last):
1380
+ ...
1381
+ ValueError: matrix must be square
1382
+
1383
+ sage: A = random_matrix(GF(19), 1, 0)
1384
+ sage: A.minimal_polynomial()
1385
+ Traceback (most recent call last):
1386
+ ...
1387
+ ValueError: matrix must be square
1388
+
1389
+ sage: A = matrix(GF(19), 10, 10)
1390
+ sage: A.minimal_polynomial() # needs sage.libs.pari
1391
+ x
1392
+
1393
+ sage: A = random_matrix(GF(4198973), 0, 0) # needs sage.rings.finite_rings
1394
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1395
+ 1
1396
+
1397
+ sage: A = random_matrix(GF(4198973), 0, 1) # needs sage.rings.finite_rings
1398
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1399
+ Traceback (most recent call last):
1400
+ ...
1401
+ ValueError: matrix must be square
1402
+
1403
+ sage: A = random_matrix(GF(4198973), 1, 0) # needs sage.rings.finite_rings
1404
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1405
+ Traceback (most recent call last):
1406
+ ...
1407
+ ValueError: matrix must be square
1408
+
1409
+ sage: A = matrix(GF(4198973), 10, 10) # needs sage.rings.finite_rings
1410
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1411
+ x
1412
+
1413
+ sage: A = Mat(GF(7),3,3)([0, 1, 2] * 3)
1414
+ sage: A.charpoly()
1415
+ x^3 + 4*x^2
1416
+
1417
+ ALGORITHM: Uses LinBox if ``self.base_ring()`` is a field,
1418
+ otherwise use Hessenberg form algorithm.
1419
+
1420
+ TESTS:
1421
+
1422
+ The cached polynomial should be independent of the ``var``
1423
+ argument (:issue:`12292`). We check (indirectly) that the
1424
+ second call uses the cached value by noting that its result is
1425
+ not cached. The polynomial here is not unique, so we only
1426
+ check the polynomial's variable.
1427
+
1428
+ sage: M = MatrixSpace(Integers(37), 2)
1429
+ sage: A = M(range(0, 2^2))
1430
+ sage: type(A)
1431
+ <class 'sage.matrix.matrix_modn_dense_float.Matrix_modn_dense_float'>
1432
+ sage: A.charpoly('x').variables()
1433
+ (x,)
1434
+ sage: A.charpoly('y').variables()
1435
+ (y,)
1436
+ sage: A._cache['charpoly_linbox'].variables()
1437
+ (x,)
1438
+ """
1439
+ cache_key = 'charpoly_%s' % algorithm
1440
+ g = self.fetch(cache_key)
1441
+ if g is not None:
1442
+ return g.change_variable_name(var)
1443
+
1444
+ if algorithm == 'linbox' and (self.p == 2 or not self.base_ring().is_field()):
1445
+ algorithm = 'generic' # LinBox only supports Z/pZ (p prime)
1446
+
1447
+ if algorithm == 'linbox':
1448
+ g = self._charpoly_linbox(var)
1449
+ elif algorithm == 'generic':
1450
+ g = Matrix_dense.charpoly(self, var)
1451
+ elif algorithm == 'all':
1452
+ g = self._charpoly_linbox(var)
1453
+ h = Matrix_dense.charpoly(self, var)
1454
+ if g != h:
1455
+ raise ArithmeticError("Characteristic polynomials do not match.")
1456
+ else:
1457
+ raise ValueError("no algorithm '%s'" % algorithm)
1458
+
1459
+ self.cache(cache_key, g)
1460
+ return g
1461
+
1462
+ def minpoly(self, var='x', algorithm='linbox', proof=None):
1463
+ """
1464
+ Return the minimal polynomial of ``self``.
1465
+
1466
+ INPUT:
1467
+
1468
+ - ``var`` -- a variable name
1469
+
1470
+ - ``algorithm`` -- ``generic`` or ``linbox`` (default:
1471
+ ``linbox``)
1472
+
1473
+ - ``proof`` -- (default: ``True``) whether to provably return
1474
+ the true minimal polynomial; if ``False``, we only guarantee
1475
+ to return a divisor of the minimal polynomial. There are
1476
+ also certainly cases where the computed results is
1477
+ frequently not exactly equal to the minimal polynomial (but
1478
+ is instead merely a divisor of it).
1479
+
1480
+ .. warning::
1481
+
1482
+ If ``proof=True``, minpoly is insanely slow compared to
1483
+ ``proof=False``. This matters since proof=True is the
1484
+ default, unless you first type
1485
+ ``proof.linear_algebra(False)``.
1486
+
1487
+ EXAMPLES::
1488
+
1489
+ sage: A = random_matrix(GF(17), 10, 10)
1490
+ sage: B = copy(A)
1491
+ sage: min_p = A.minimal_polynomial(proof=True)
1492
+ sage: min_p(A) == 0
1493
+ True
1494
+ sage: B == A
1495
+ True
1496
+
1497
+ sage: char_p = A.characteristic_polynomial()
1498
+ sage: min_p.divides(char_p)
1499
+ True
1500
+
1501
+ ::
1502
+
1503
+ sage: A = random_matrix(GF(1214471), 10, 10) # needs sage.rings.finite_rings
1504
+ sage: B = copy(A)
1505
+ sage: min_p = A.minimal_polynomial(proof=True)
1506
+ sage: min_p(A) == 0
1507
+ True
1508
+ sage: B == A
1509
+ True
1510
+
1511
+ sage: char_p = A.characteristic_polynomial()
1512
+ sage: min_p.divides(char_p)
1513
+ True
1514
+
1515
+ TESTS::
1516
+
1517
+ sage: A = random_matrix(GF(17), 0, 0)
1518
+ sage: A.minimal_polynomial()
1519
+ 1
1520
+
1521
+ sage: A = random_matrix(GF(17), 0, 1)
1522
+ sage: A.minimal_polynomial()
1523
+ Traceback (most recent call last):
1524
+ ...
1525
+ ValueError: matrix must be square
1526
+
1527
+ sage: A = random_matrix(GF(17), 1, 0)
1528
+ sage: A.minimal_polynomial()
1529
+ Traceback (most recent call last):
1530
+ ...
1531
+ ValueError: matrix must be square
1532
+
1533
+ sage: A = matrix(GF(17), 10, 10)
1534
+ sage: A.minimal_polynomial() # needs sage.libs.pari
1535
+ x
1536
+
1537
+ ::
1538
+
1539
+ sage: A = random_matrix(GF(2535919), 0, 0) # needs sage.rings.finite_rings
1540
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1541
+ 1
1542
+
1543
+ sage: A = random_matrix(GF(2535919), 0, 1) # needs sage.rings.finite_rings
1544
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1545
+ Traceback (most recent call last):
1546
+ ...
1547
+ ValueError: matrix must be square
1548
+
1549
+ sage: A = random_matrix(GF(2535919), 1, 0) # needs sage.rings.finite_rings
1550
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1551
+ Traceback (most recent call last):
1552
+ ...
1553
+ ValueError: matrix must be square
1554
+
1555
+ sage: A = matrix(GF(2535919), 10, 10) # needs sage.rings.finite_rings
1556
+ sage: A.minimal_polynomial() # needs sage.rings.finite_rings
1557
+ x
1558
+
1559
+ EXAMPLES::
1560
+
1561
+ sage: R.<x>=GF(3)[]
1562
+ sage: A = matrix(GF(3),2,[0,0,1,2])
1563
+ sage: A.minpoly()
1564
+ x^2 + x
1565
+
1566
+ sage: A.minpoly(proof=False) in [x, x+1, x^2+x]
1567
+ True
1568
+ """
1569
+ proof = get_proof_flag(proof, "linear_algebra")
1570
+
1571
+ if algorithm == 'linbox' and (self.p == 2 or not self.base_ring().is_field()):
1572
+ algorithm='generic' # LinBox only supports fields
1573
+
1574
+ if algorithm == 'linbox':
1575
+ if self._nrows != self._ncols:
1576
+ raise ValueError("matrix must be square")
1577
+
1578
+ if self._nrows <= 1:
1579
+ return Matrix_dense.minpoly(self, var)
1580
+
1581
+ R = self._base_ring[var]
1582
+ v = linbox_minpoly(self.p, self._nrows, self._entries)
1583
+ g = R(v)
1584
+
1585
+ if proof:
1586
+ while g(self): # insanely toy slow (!)
1587
+ g = g.lcm(R(linbox_minpoly(self.p, self._nrows, self._entries)))
1588
+
1589
+ elif algorithm == 'generic':
1590
+ raise NotImplementedError("Minimal polynomials are not implemented for Z/nZ.")
1591
+
1592
+ else:
1593
+ raise ValueError("no algorithm '%s'" % algorithm)
1594
+
1595
+ self.cache('minpoly_%s_%s' % (algorithm, var), g)
1596
+ return g
1597
+
1598
+ def _charpoly_linbox(self, var='x'):
1599
+ """
1600
+ Compute the characteristic polynomial using LinBox. No checks
1601
+ are performed.
1602
+
1603
+ This function is called internally by ``charpoly``.
1604
+
1605
+ INPUT:
1606
+
1607
+ - ``var`` -- a variable name
1608
+
1609
+ EXAMPLES::
1610
+
1611
+ sage: # needs sage.libs.linbox
1612
+ sage: A = random_matrix(GF(19), 10, 10)
1613
+ sage: B = copy(A)
1614
+ sage: char_p = A._charpoly_linbox()
1615
+ sage: char_p(A) == 0
1616
+ True
1617
+ sage: B == A # A is not modified
1618
+ True
1619
+ sage: min_p = A.minimal_polynomial(proof=True)
1620
+ sage: min_p.divides(char_p)
1621
+ True
1622
+ """
1623
+ verbose('_charpoly_linbox...')
1624
+
1625
+ if self._nrows != self._ncols:
1626
+ raise ValueError("matrix must be square")
1627
+ R = self._base_ring[var]
1628
+ # call linbox for charpoly
1629
+ v = linbox_charpoly(self.p, self._nrows, self._entries)
1630
+ r = R(v)
1631
+ return r
1632
+
1633
+ def echelonize(self, algorithm='linbox_noefd', **kwds):
1634
+ """
1635
+ Put ``self`` in reduced row echelon form.
1636
+
1637
+ INPUT:
1638
+
1639
+ - ``self`` -- a mutable matrix
1640
+
1641
+ - ``algorithm``
1642
+
1643
+ - ``linbox`` -- uses the LinBox library (wrapping fflas-ffpack)
1644
+
1645
+ - ``linbox_noefd`` -- uses the FFPACK directly, less memory and faster (default)
1646
+
1647
+ - ``gauss`` -- uses a custom slower `O(n^3)` Gauss
1648
+ elimination implemented in Sage
1649
+
1650
+ - ``all`` -- compute using all algorithms and verify that
1651
+ the results are the same
1652
+
1653
+ - ``**kwds`` -- these are all ignored
1654
+
1655
+ OUTPUT: if ``self`` is known to be echelonized (the information is
1656
+ cached), nothing is done; else, ``self`` is put in reduced row echelon
1657
+ form and
1658
+
1659
+ - the fact that ``self`` is now in echelon form is cached so future
1660
+ calls to echelonize return immediately
1661
+
1662
+ - the rank of ``self`` is computed and cached
1663
+
1664
+ - the pivot columns (a.k.a. column rank profile) of ``self`` are
1665
+ computed and cached
1666
+
1667
+ - only if using algorithm ``linbox_noefd``: the pivot rows of ``self``
1668
+ before echelonization (a.k.a. its row rank profile) are computed and
1669
+ cached
1670
+
1671
+ EXAMPLES::
1672
+
1673
+ sage: A = random_matrix(GF(7), 10, 20)
1674
+ sage: E = A.echelon_form()
1675
+ sage: A.row_space() == E.row_space()
1676
+ True
1677
+ sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r)
1678
+ True
1679
+
1680
+ ::
1681
+
1682
+ sage: A = random_matrix(GF(13), 10, 10)
1683
+ sage: while A.rank() != 10:
1684
+ ....: A = random_matrix(GF(13), 10, 10)
1685
+ sage: MS = parent(A)
1686
+ sage: B = A.augment(MS(1))
1687
+ sage: B.echelonize()
1688
+ sage: A.rank()
1689
+ 10
1690
+ sage: C = B.submatrix(0,10,10,10)
1691
+ sage: ~A == C
1692
+ True
1693
+
1694
+ sage: B = A.augment(MS(1))
1695
+ sage: B.echelonize(algorithm='linbox')
1696
+ sage: A.rank()
1697
+ 10
1698
+ sage: C = B.submatrix(0,10,10,10)
1699
+ sage: ~A == C
1700
+ True
1701
+
1702
+ ::
1703
+
1704
+ sage: A = random_matrix(Integers(10), 10, 20)
1705
+ sage: A.echelon_form()
1706
+ Traceback (most recent call last):
1707
+ ...
1708
+ NotImplementedError: Echelon form not implemented over 'Ring of integers modulo 10'.
1709
+
1710
+ ::
1711
+
1712
+ sage: # needs sage.rings.finite_rings
1713
+ sage: A = random_matrix(GF(16007), 10, 20)
1714
+ sage: E = A.echelon_form()
1715
+ sage: A.row_space() == E.row_space()
1716
+ True
1717
+ sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r)
1718
+ True
1719
+
1720
+ ::
1721
+
1722
+ sage: A = random_matrix(Integers(10000), 10, 20)
1723
+ sage: A.echelon_form()
1724
+ Traceback (most recent call last):
1725
+ ...
1726
+ NotImplementedError: Echelon form not implemented over 'Ring of integers modulo 10000'.
1727
+
1728
+ Parallel computation::
1729
+
1730
+ sage: # needs sage.rings.finite_rings
1731
+ sage: A = random_matrix(GF(65521),100,200)
1732
+ sage: Parallelism().set('linbox', nproc=2)
1733
+ sage: E = A.echelon_form()
1734
+ sage: Parallelism().set('linbox', nproc=1) # switch off parallelization
1735
+ sage: F = A.echelon_form()
1736
+ sage: E==F
1737
+ True
1738
+
1739
+ TESTS::
1740
+
1741
+ sage: A = random_matrix(GF(7), 0, 10)
1742
+ sage: A.echelon_form()
1743
+ []
1744
+ sage: A = random_matrix(GF(7), 10, 0)
1745
+ sage: A.echelon_form()
1746
+ []
1747
+ sage: A = random_matrix(GF(7), 0, 0)
1748
+ sage: A.echelon_form()
1749
+ []
1750
+ sage: A = matrix(GF(7), 10, 10)
1751
+ sage: A.echelon_form()
1752
+ [0 0 0 0 0 0 0 0 0 0]
1753
+ [0 0 0 0 0 0 0 0 0 0]
1754
+ [0 0 0 0 0 0 0 0 0 0]
1755
+ [0 0 0 0 0 0 0 0 0 0]
1756
+ [0 0 0 0 0 0 0 0 0 0]
1757
+ [0 0 0 0 0 0 0 0 0 0]
1758
+ [0 0 0 0 0 0 0 0 0 0]
1759
+ [0 0 0 0 0 0 0 0 0 0]
1760
+ [0 0 0 0 0 0 0 0 0 0]
1761
+ [0 0 0 0 0 0 0 0 0 0]
1762
+
1763
+ sage: # needs sage.rings.finite_rings
1764
+ sage: A = random_matrix(GF(16007), 0, 10)
1765
+ sage: A.echelon_form()
1766
+ []
1767
+ sage: A = random_matrix(GF(16007), 10, 0)
1768
+ sage: A.echelon_form()
1769
+ []
1770
+ sage: A = random_matrix(GF(16007), 0, 0)
1771
+ sage: A.echelon_form()
1772
+ []
1773
+ sage: A = matrix(GF(16007), 10, 10)
1774
+ sage: A.echelon_form()
1775
+ [0 0 0 0 0 0 0 0 0 0]
1776
+ [0 0 0 0 0 0 0 0 0 0]
1777
+ [0 0 0 0 0 0 0 0 0 0]
1778
+ [0 0 0 0 0 0 0 0 0 0]
1779
+ [0 0 0 0 0 0 0 0 0 0]
1780
+ [0 0 0 0 0 0 0 0 0 0]
1781
+ [0 0 0 0 0 0 0 0 0 0]
1782
+ [0 0 0 0 0 0 0 0 0 0]
1783
+ [0 0 0 0 0 0 0 0 0 0]
1784
+ [0 0 0 0 0 0 0 0 0 0]
1785
+
1786
+ sage: A = matrix(GF(97),3,4,range(12))
1787
+ sage: A.echelonize(); A
1788
+ [ 1 0 96 95]
1789
+ [ 0 1 2 3]
1790
+ [ 0 0 0 0]
1791
+ sage: A.pivots()
1792
+ (0, 1)
1793
+ sage: A.pivot_rows()
1794
+ (0, 1)
1795
+
1796
+ sage: for p in (3,17,97,127,1048573):
1797
+ ....: for i in range(10):
1798
+ ....: A = random_matrix(GF(p), 100, 100)
1799
+ ....: A.echelonize(algorithm='all')
1800
+ """
1801
+ x = self.fetch('in_echelon_form')
1802
+ if x is not None:
1803
+ return # already known to be in echelon form
1804
+
1805
+ if not self.base_ring().is_field():
1806
+ raise NotImplementedError("Echelon form not implemented over '%s'." % self.base_ring())
1807
+
1808
+ if algorithm == 'linbox':
1809
+ self._echelonize_linbox(efd=True)
1810
+ elif algorithm == 'linbox_noefd':
1811
+ self._echelonize_linbox(efd=False)
1812
+ elif algorithm == 'gauss':
1813
+ self._echelon_in_place_classical()
1814
+
1815
+ elif algorithm == 'all':
1816
+ A = self.__copy__()
1817
+ B = self.__copy__()
1818
+ self._echelonize_linbox(efd=True)
1819
+ A._echelon_in_place_classical()
1820
+ B._echelonize_linbox(efd=False)
1821
+ if A != self or A != B:
1822
+ raise ArithmeticError("Bug in echelon form.")
1823
+ else:
1824
+ raise ValueError("Algorithm '%s' not known" % algorithm)
1825
+
1826
+ def _echelonize_linbox(self, efd=True):
1827
+ """
1828
+ Puts ``self`` in row echelon form using LinBox.
1829
+
1830
+ This function is called by echelonize if
1831
+ ``algorithm='linbox'``.
1832
+
1833
+ INPUT:
1834
+
1835
+ - ``efd`` -- if ``True`` LinBox's ``EchelonFormDomain``
1836
+ implementation is used, which is faster than the direct
1837
+ ``LinBox::FFPACK`` implementation, since the latter also
1838
+ computes the transformation matrix (which we
1839
+ ignore). However, ``efd=True`` uses more memory than FFLAS
1840
+ directly (default: ``True``)
1841
+
1842
+ OUTPUT: if ``efd`` is ``False``, return the pivot rows (a.k.a. row rank
1843
+ profile) of the matrix ``self`` before echelonization
1844
+
1845
+ EXAMPLES::
1846
+
1847
+ sage: # needs sage.libs.linbox
1848
+ sage: A = random_matrix(GF(7), 10, 20)
1849
+ sage: B = copy(A)
1850
+ sage: A._echelonize_linbox()
1851
+ sage: A.row_space() == B.row_space()
1852
+ True
1853
+ sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r)
1854
+ True
1855
+ """
1856
+ self.check_mutability()
1857
+ self.clear_cache()
1858
+
1859
+ t = verbose('Calling echelonize mod %d.' % self.p)
1860
+ if efd:
1861
+ r, pivots = linbox_echelonize_efd(self.p, self._entries,
1862
+ self._nrows, self._ncols)
1863
+ else:
1864
+ r, pivots, rrp = linbox_echelonize(self.p, self._entries,
1865
+ self._nrows, self._ncols)
1866
+ verbose('done with echelonize', t)
1867
+ self.cache('in_echelon_form', True)
1868
+ self.cache('rank', r)
1869
+ self.cache('pivots', tuple(pivots))
1870
+ self.cache('pivot_rows', tuple(range(r)))
1871
+
1872
+ if not efd:
1873
+ return tuple(rrp)
1874
+
1875
+ def _echelon_in_place_classical(self):
1876
+ """
1877
+ Puts ``self`` in row echelon form using LinBox.
1878
+
1879
+ This function is called by echelonize if
1880
+ ``algorithm='gauss'``.
1881
+
1882
+ EXAMPLES::
1883
+
1884
+ sage: A = random_matrix(GF(7), 10, 20)
1885
+ sage: B = copy(A)
1886
+ sage: A._echelon_in_place_classical()
1887
+ sage: A.row_space() == B.row_space()
1888
+ True
1889
+ sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r)
1890
+ True
1891
+ """
1892
+ self.check_mutability()
1893
+ self.clear_cache()
1894
+
1895
+ cdef Py_ssize_t start_row, c, r, nr, nc, i
1896
+ cdef celement p, a, s, t, b
1897
+ cdef celement **m
1898
+
1899
+ start_row = 0
1900
+ p = self.p
1901
+ m = self._matrix
1902
+ nr = self._nrows
1903
+ nc = self._ncols
1904
+ pivots = []
1905
+ fifth = self._ncols / 10 + 1
1906
+ do_verb = (get_verbose() >= 2)
1907
+ for c from 0 <= c < nc:
1908
+ if do_verb and (c % fifth == 0 and c > 0):
1909
+ tm = verbose('on column %s of %s' % (c, self._ncols),
1910
+ level = 2,
1911
+ caller_name = 'matrix_modn_dense echelon')
1912
+ # end if
1913
+ sig_check()
1914
+ for r from start_row <= r < nr:
1915
+ a = m[r][c]
1916
+ if a:
1917
+ pivots.append(c)
1918
+ a_inverse = celement_invert(a, p)
1919
+ self.rescale_row_c(r, a_inverse, c)
1920
+ self.swap_rows_c(r, start_row)
1921
+ for i from 0 <= i < nr:
1922
+ if i != start_row:
1923
+ b = m[i][c]
1924
+ if b != 0:
1925
+ self.add_multiple_of_row_c(i, start_row, p-b, c)
1926
+ start_row = start_row + 1
1927
+ break
1928
+ self.cache('pivots', tuple(pivots))
1929
+ self.cache('pivot_rows', tuple(range(r)))
1930
+ self.cache('in_echelon_form', True)
1931
+
1932
+ def pivots(self):
1933
+ """
1934
+ Return the column pivot positions for this matrix, which form the
1935
+ leftmost subset of the columns that span the column space and are
1936
+ linearly independent. This coincides with the position of the first
1937
+ nonzero entry in each row of the reduced row echelon form of ``self``,
1938
+ and is also known as the column rank profile of ``self``. The returned
1939
+ tuple is ordered increasingly.
1940
+
1941
+ If this has already been computed and cached, this returns the cached
1942
+ value. Otherwise, this computes an echelon form (using algorithm
1943
+ ``linbox_noefd``), and deduces the pivot positions to be cached and
1944
+ returned. In the latter case, this also caches other attributes at the
1945
+ same time: the reduced row echelon form of ``self`` and the rank of
1946
+ ``self``, as well as its row pivot positions (also known as row rank
1947
+ profile).
1948
+
1949
+ OUTPUT: a tuple of `r` integers where `r` is the rank of ``self``
1950
+
1951
+ .. SEEALSO::
1952
+
1953
+ The method :meth:`Matrix_modn_dense_template.pivot_rows` computes
1954
+ the row pivot positions, also known as row rank profile.
1955
+
1956
+ EXAMPLES::
1957
+
1958
+ sage: A = matrix(GF(7), 2, 2, range(4))
1959
+ sage: A.pivots()
1960
+ (0, 1)
1961
+
1962
+ sage: A = matrix(GF(3), [[1,1,1,0],[0,0,0,1],[1,0,0,0]])
1963
+ sage: A
1964
+ [1 1 1 0]
1965
+ [0 0 0 1]
1966
+ [1 0 0 0]
1967
+ sage: A.pivots() == (0, 1, 3)
1968
+ True
1969
+
1970
+ sage: A = matrix(GF(65537), 5, 3,
1971
+ ....: [ 223, 669, 21130,
1972
+ ....: 13996, 41988, 21387,
1973
+ ....: 39034, 51565, 40500,
1974
+ ....: 14660, 43980, 3899,
1975
+ ....: 12016, 36048, 9308])
1976
+ sage: A[:,1] == 3 * A[:,0]
1977
+ True
1978
+ sage: A.pivots() == (0, 2)
1979
+ True
1980
+ sage: A = matrix(GF(7), 3, 5, [2, 2, 4, 1, 4,
1981
+ ....: 4, 4, 1, 4, 6,
1982
+ ....: 5, 2, 5, 6, 6])
1983
+ sage: A[:,0] == 2 * A[:,1] + 3 * A[:,2]
1984
+ True
1985
+ sage: A.pivots() == (0, 1, 3)
1986
+ True
1987
+ """
1988
+ if not self.base_ring().is_field():
1989
+ raise NotImplementedError("Echelon form not implemented over '%s'." % self.base_ring())
1990
+
1991
+ v = self.fetch('pivots')
1992
+ if v is not None:
1993
+ return tuple(v)
1994
+
1995
+ E = self.__copy__()
1996
+ rrp = E._echelonize_linbox(efd=False)
1997
+ E.set_immutable()
1998
+ v = E.pivots()
1999
+ self.cache('echelon_form', E)
2000
+ self.cache('rank', E._cache['rank'])
2001
+ self.cache('pivots', tuple(v))
2002
+ self.cache('pivot_rows', rrp)
2003
+ return tuple(v)
2004
+
2005
+ def pivot_rows(self):
2006
+ """
2007
+ Return the row pivot positions for this matrix, which form the topmost
2008
+ subset of the rows that span the row space and are linearly
2009
+ independent. This coincides with the position of the first nonzero
2010
+ entry in each column of the reduced column echelon form of ``self``,
2011
+ and is also known as the row rank profile of ``self``. The returned
2012
+ tuple is ordered increasingly.
2013
+
2014
+ If this has already been computed and cached, this returns the cached
2015
+ value. Otherwise, this computes an echelon form (using algorithm
2016
+ ``linbox_noefd``), and deduces the pivot row positions to be cached and
2017
+ returned. In the latter case, this also caches other attributes at the
2018
+ same time: the reduced row echelon form of ``self`` and the rank of
2019
+ ``self``, as well as its pivot indices (also known as column rank
2020
+ profile).
2021
+
2022
+ OUTPUT: a tuple of `r` integers where `r` is the rank of ``self``
2023
+
2024
+ .. SEEALSO::
2025
+
2026
+ The method :meth:`Matrix_modn_dense_template.pivots` computes
2027
+ the column pivot positions, also known as column rank profile.
2028
+
2029
+ EXAMPLES::
2030
+
2031
+ sage: A = matrix(GF(3), [[1,0,1,0],[1,0,0,0],[1,0,0,0],[0,1,0,0]])
2032
+ sage: A
2033
+ [1 0 1 0]
2034
+ [1 0 0 0]
2035
+ [1 0 0 0]
2036
+ [0 1 0 0]
2037
+ sage: A.pivot_rows() == (0, 1, 3)
2038
+ True
2039
+
2040
+ sage: A = matrix(GF(65537), 3, 5,
2041
+ ....: [ 223, 13996, 39034, 14660, 12016,
2042
+ ....: 669, 41988, 51565, 43980, 36048,
2043
+ ....: 21130, 21387, 40500, 3899, 9308])
2044
+ sage: A[1,:] == 3 * A[0,:]
2045
+ True
2046
+ sage: A.pivot_rows() == (0, 2)
2047
+ True
2048
+ sage: A = matrix(GF(7), 5, 3, [2, 4, 5,
2049
+ ....: 2, 4, 2,
2050
+ ....: 4, 1, 5,
2051
+ ....: 1, 4, 6,
2052
+ ....: 4, 6, 6])
2053
+ sage: A[0,:] == 2 * A[1,:] + 3 * A[2,:]
2054
+ True
2055
+ sage: A.pivot_rows() == (0, 1, 3)
2056
+ True
2057
+ """
2058
+ if not self.base_ring().is_field():
2059
+ raise NotImplementedError("Echelon form not implemented over '%s'." % self.base_ring())
2060
+
2061
+ v = self.fetch('pivot_rows')
2062
+ if v is not None:
2063
+ return tuple(v)
2064
+
2065
+ E = self.__copy__()
2066
+ v = E._echelonize_linbox(efd=False)
2067
+ E.set_immutable()
2068
+ self.cache('echelon_form', E)
2069
+ self.cache('rank', E._cache['rank'])
2070
+ self.cache('pivots', E._cache['pivots'])
2071
+ self.cache('pivot_rows', v)
2072
+ return v
2073
+
2074
+ def right_kernel_matrix(self, algorithm='linbox', basis='echelon'):
2075
+ r"""
2076
+ Return a matrix whose rows form a basis for the right kernel
2077
+ of ``self``.
2078
+
2079
+ If the base ring is the ring of integers modulo a composite,
2080
+ the keyword arguments are ignored and the computation is
2081
+ delegated to :meth:`Matrix_dense.right_kernel_matrix`.
2082
+
2083
+ INPUT:
2084
+
2085
+ - ``algorithm`` -- (default: ``'linbox'``) a parameter that is
2086
+ passed on to ``self.echelon_form``, if computation of an echelon
2087
+ form is required; see that routine for allowable values
2088
+
2089
+ - ``basis`` -- (default: ``'echelon'``) a keyword that describes the
2090
+ format of the basis returned, allowable values are:
2091
+
2092
+ - ``'echelon'``: the basis matrix is in echelon form
2093
+ - ``'pivot'``: the basis matrix is such that the submatrix obtained
2094
+ by taking the columns that in ``self`` contain no pivots, is the
2095
+ identity matrix
2096
+ - ``'computed'``: no work is done to transform the basis; in
2097
+ the current implementation the result is the negative of
2098
+ that returned by ``'pivot'``
2099
+
2100
+ OUTPUT:
2101
+
2102
+ A matrix ``X`` whose rows are a basis for the right kernel of
2103
+ ``self``. This means that ``self * X.transpose()`` is a zero matrix.
2104
+
2105
+ The result is not cached, but the routine benefits when ``self`` is
2106
+ known to be in echelon form already.
2107
+
2108
+ EXAMPLES::
2109
+
2110
+ sage: M = matrix(GF(5),6,6,range(36))
2111
+ sage: M.right_kernel_matrix(basis='computed')
2112
+ [4 2 4 0 0 0]
2113
+ [3 3 0 4 0 0]
2114
+ [2 4 0 0 4 0]
2115
+ [1 0 0 0 0 4]
2116
+ sage: M.right_kernel_matrix(basis='pivot')
2117
+ [1 3 1 0 0 0]
2118
+ [2 2 0 1 0 0]
2119
+ [3 1 0 0 1 0]
2120
+ [4 0 0 0 0 1]
2121
+ sage: M.right_kernel_matrix()
2122
+ [1 0 0 0 0 4]
2123
+ [0 1 0 0 1 3]
2124
+ [0 0 1 0 2 2]
2125
+ [0 0 0 1 3 1]
2126
+ sage: M * M.right_kernel_matrix().transpose()
2127
+ [0 0 0 0]
2128
+ [0 0 0 0]
2129
+ [0 0 0 0]
2130
+ [0 0 0 0]
2131
+ [0 0 0 0]
2132
+ [0 0 0 0]
2133
+ """
2134
+ if self.fetch('in_echelon_form') is None:
2135
+ try:
2136
+ self = self.echelon_form(algorithm=algorithm)
2137
+ except NotImplementedError: # composite modulus
2138
+ return Matrix_dense.right_kernel_matrix(self)
2139
+
2140
+ cdef Py_ssize_t r = self.rank()
2141
+ cdef Py_ssize_t nrows = self._nrows
2142
+ cdef Py_ssize_t ncols = self._ncols
2143
+ cdef Py_ssize_t i, j, k
2144
+
2145
+ cdef Py_ssize_t* nonpivots = <Py_ssize_t*>sig_malloc(sizeof(Py_ssize_t)*(ncols-r))
2146
+ cdef Py_ssize_t* pivots = <Py_ssize_t*>sig_malloc(sizeof(Py_ssize_t)*(r))
2147
+ cdef tuple pivot_tuple = self.pivots()
2148
+
2149
+ for i in range(r):
2150
+ pivots[i] = pivot_tuple[i]
2151
+ j = 0
2152
+ k = 0
2153
+ for i in range(ncols):
2154
+ if j < r and i == pivots[j]:
2155
+ j += 1
2156
+ else:
2157
+ nonpivots[k] = i
2158
+ k += 1
2159
+
2160
+ cdef Matrix_modn_dense_template M = self.new_matrix(nrows=ncols-r, ncols=ncols)
2161
+ cdef celement pm1 = self.p - 1
2162
+
2163
+ k = 0
2164
+ for i in range(ncols-r):
2165
+ for j in range(ncols-r):
2166
+ M._entries[nonpivots[i]+j*ncols] = 0
2167
+ M._entries[nonpivots[i]+k*ncols] = pm1
2168
+ k += 1
2169
+ for j in range(r):
2170
+ M._entries[i*ncols+pivots[j]] = self._entries[nonpivots[i]+j*ncols]
2171
+
2172
+ sig_free(pivots)
2173
+ sig_free(nonpivots)
2174
+ if basis == 'computed':
2175
+ return M
2176
+ elif basis == 'pivot':
2177
+ return -M
2178
+ elif basis != 'echelon':
2179
+ raise ValueError("matrix kernel basis format not recognized")
2180
+ M.echelonize(algorithm=algorithm)
2181
+ return M
2182
+
2183
+ def hessenbergize(self):
2184
+ """
2185
+ Transform ``self`` in place to its Hessenberg form.
2186
+
2187
+ EXAMPLES::
2188
+
2189
+ sage: A = random_matrix(GF(17), 10, 10, density=0.1)
2190
+ sage: B = copy(A)
2191
+ sage: A.hessenbergize()
2192
+ sage: all(A[i,j] == 0 for j in range(10) for i in range(j+2, 10))
2193
+ True
2194
+ sage: A.charpoly() == B.charpoly()
2195
+ True
2196
+ """
2197
+ self.check_mutability()
2198
+ x = self.fetch('in_hessenberg_form')
2199
+ if x is not None and x:
2200
+ return # already known to be in Hessenberg form
2201
+ if self._nrows != self._ncols:
2202
+ raise ArithmeticError("Matrix must be square to compute Hessenberg form.")
2203
+
2204
+ cdef Py_ssize_t n
2205
+ n = self._nrows
2206
+
2207
+ cdef celement **h
2208
+ h = self._matrix
2209
+
2210
+ cdef celement p, t, t_inv, u
2211
+ cdef Py_ssize_t i, j, m
2212
+ p = self.p
2213
+
2214
+ sig_on()
2215
+ for m from 1 <= m < n-1:
2216
+ # Search for a nonzero entry in column m-1
2217
+ i = -1
2218
+ for r from m+1 <= r < n:
2219
+ if h[r][m-1]:
2220
+ i = r
2221
+ break
2222
+
2223
+ if i != -1:
2224
+ # Found a nonzero entry in column m-1 that is strictly
2225
+ # below row m. Now set i to be the first nonzero position >=
2226
+ # m in column m-1.
2227
+ if h[m][m-1]:
2228
+ i = m
2229
+ t = h[i][m-1]
2230
+ t_inv = celement_invert(t, p)
2231
+ if i > m:
2232
+ self.swap_rows_c(i, m)
2233
+ self.swap_columns_c(i, m)
2234
+
2235
+ # Now the nonzero entry in position (m,m-1) is t.
2236
+ # Use t to clear the entries in column m-1 below m.
2237
+ for j from m+1 <= j < n:
2238
+ if h[j][m-1]:
2239
+ u = (h[j][m-1] * t_inv) % p
2240
+ self.add_multiple_of_row_c(j, m, p - u, 0) # h[j] -= u*h[m]
2241
+ # To maintain charpoly, do the corresponding
2242
+ # column operation, which doesn't mess up the
2243
+ # matrix, since it only changes column m, and
2244
+ # we're only worried about column m-1 right
2245
+ # now. Add u*column_j to column_m.
2246
+ self.add_multiple_of_column_c(m, j, u, 0)
2247
+ # end for
2248
+ # end if
2249
+ # end for
2250
+ sig_off()
2251
+ self.cache('in_hessenberg_form', True)
2252
+
2253
+ def _charpoly_hessenberg(self, var):
2254
+ """
2255
+ Transform ``self`` in place to its Hessenberg form then computes
2256
+ and returns the coefficients of the characteristic polynomial
2257
+ of this matrix.
2258
+
2259
+ INPUT:
2260
+
2261
+ - ``var`` -- name of the indeterminate of the charpoly
2262
+
2263
+ OUTPUT:
2264
+
2265
+ The characteristic polynomial is represented as a vector of
2266
+ ints, where the constant term of the characteristic
2267
+ polynomial is the `0`-th coefficient of the vector.
2268
+
2269
+ EXAMPLES::
2270
+
2271
+ sage: A = random_matrix(GF(17), 10, 10, density=0.1)
2272
+ sage: B = copy(A)
2273
+ sage: P.<x> = GF(17)[]
2274
+ sage: A._charpoly_hessenberg('x') == B.charpoly()
2275
+ True
2276
+ """
2277
+ if self._nrows != self._ncols:
2278
+ raise ArithmeticError("charpoly not defined for non-square matrix.")
2279
+
2280
+ cdef Py_ssize_t i, m, n,
2281
+ n = self._nrows
2282
+
2283
+ cdef celement p, t
2284
+ p = self.p
2285
+
2286
+ # Replace self by its Hessenberg form, and set H to this form
2287
+ # for notation below.
2288
+ cdef Matrix_modn_dense_template H
2289
+ H = self.__copy__()
2290
+ H.hessenbergize()
2291
+
2292
+ # We represent the intermediate polynomials that come up in
2293
+ # the calculations as rows of an (n+1)x(n+1) matrix, since
2294
+ # we've implemented basic arithmetic with such a matrix.
2295
+ # Please see the generic implementation of charpoly in
2296
+ # matrix.py to see more clearly how the following algorithm
2297
+ # actually works. (The implementation is clearer (but slower)
2298
+ # if one uses polynomials to represent polynomials instead of
2299
+ # using the rows of a matrix.) Also see Cohen's first GTM,
2300
+ # Algorithm 2.2.9.
2301
+
2302
+ cdef Matrix_modn_dense_template c
2303
+ c = self.new_matrix(nrows=n+1, ncols=n+1) # the 0 matrix
2304
+ c._matrix[0][0] = 1
2305
+ for m from 1 <= m <= n:
2306
+ # 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]
2307
+ # We do this by hand by setting the m-th row to c[m-1]
2308
+ # shifted to the right by one. We then add
2309
+ # -H[m-1,m-1]*c[m-1] to the resulting m-th row.
2310
+ for i from 1 <= i <= n:
2311
+ c._matrix[m][i] = c._matrix[m-1][i-1]
2312
+ # the p-.. below is to keep scalar normalized between 0 and p.
2313
+ c.add_multiple_of_row_c(m, m-1, p - H._matrix[m-1][m-1], 0)
2314
+ t = 1
2315
+ for i from 1 <= i < m:
2316
+ t = (t*H._matrix[m-i][m-i-1]) % p
2317
+ # Set the m-th row of c to c[m] - t*H[m-i-1,m-1]*c[m-i-1]
2318
+ c.add_multiple_of_row_c(m, m-i-1, p - (t*H._matrix[m-i-1][m-1]) % p, 0)
2319
+
2320
+ # The answer is now the n-th row of c.
2321
+ v = []
2322
+ for i from 0 <= i <= n:
2323
+ v.append(int(c._matrix[n][i]))
2324
+ R = self._base_ring[var] # polynomial ring over the base ring
2325
+ return R(v)
2326
+
2327
+ def rank(self):
2328
+ """
2329
+ Return the rank of this matrix.
2330
+
2331
+ EXAMPLES::
2332
+
2333
+ sage: A = random_matrix(GF(3), 100, 100)
2334
+ sage: B = copy(A)
2335
+ sage: _ = A.rank()
2336
+ sage: B == A
2337
+ True
2338
+
2339
+ sage: A = random_matrix(GF(3), 100, 100, density=0.01)
2340
+ sage: A.transpose().rank() == A.rank()
2341
+ True
2342
+
2343
+ sage: A = matrix(GF(3), 100, 100)
2344
+ sage: A.rank()
2345
+ 0
2346
+
2347
+ Rank is not implemented over the integers modulo a composite
2348
+ yet.::
2349
+
2350
+ sage: M = matrix(Integers(4), 2, [2,2,2,2])
2351
+ sage: M.rank()
2352
+ Traceback (most recent call last):
2353
+ ...
2354
+ NotImplementedError: Echelon form not implemented over 'Ring of integers modulo 4'.
2355
+
2356
+ ::
2357
+
2358
+ sage: # needs sage.rings.finite_rings
2359
+ sage: while True:
2360
+ ....: A = random_matrix(GF(16007), 100, 100)
2361
+ ....: if A.rank() == 100:
2362
+ ....: break
2363
+ sage: B = copy(A)
2364
+ sage: A.rank()
2365
+ 100
2366
+ sage: B == A
2367
+ True
2368
+ sage: MS = A.parent()
2369
+ sage: MS(1) == ~A*A
2370
+ True
2371
+
2372
+ TESTS::
2373
+
2374
+ sage: A = random_matrix(GF(7), 0, 0)
2375
+ sage: A.rank()
2376
+ 0
2377
+ sage: A = random_matrix(GF(7), 1, 0)
2378
+ sage: A.rank()
2379
+ 0
2380
+ sage: A = random_matrix(GF(7), 0, 1)
2381
+ sage: A.rank()
2382
+ 0
2383
+
2384
+ sage: # needs sage.rings.finite_rings
2385
+ sage: A = random_matrix(GF(16007), 0, 0)
2386
+ sage: A.rank()
2387
+ 0
2388
+ sage: A = random_matrix(GF(16007), 1, 0)
2389
+ sage: A.rank()
2390
+ 0
2391
+ sage: A = random_matrix(GF(16007), 0, 1)
2392
+ sage: A.rank()
2393
+ 0
2394
+ """
2395
+ cdef Matrix_modn_dense_template A
2396
+ if self.p > 2 and is_prime(self.p):
2397
+ x = self.fetch('rank')
2398
+ if x is not None:
2399
+ return x
2400
+ r = Integer(linbox_rank(self.p, self._entries, self._nrows, self._ncols))
2401
+ self.cache('rank', r)
2402
+ return r
2403
+ else:
2404
+ # linbox is very buggy for p=2, but this code should never
2405
+ # be called since p=2 is handled via M4RI
2406
+ return Matrix_dense.rank(self)
2407
+
2408
+ def determinant(self):
2409
+ """
2410
+ Return the determinant of this matrix.
2411
+
2412
+ EXAMPLES::
2413
+
2414
+ sage: s = set()
2415
+ sage: while s != set(GF(7)):
2416
+ ....: A = random_matrix(GF(7), 10, 10)
2417
+ ....: s.add(A.determinant())
2418
+
2419
+ ::
2420
+
2421
+ sage: A = random_matrix(GF(7), 100, 100)
2422
+ sage: A.determinant() == A.transpose().determinant()
2423
+ True
2424
+
2425
+ sage: B = random_matrix(GF(7), 100, 100)
2426
+ sage: (A*B).determinant() == A.determinant() * B.determinant()
2427
+ True
2428
+
2429
+ ::
2430
+
2431
+ sage: # needs sage.rings.finite_rings
2432
+ sage: A = random_matrix(GF(16007), 10, 10)
2433
+ sage: A.determinant().parent() is GF(16007)
2434
+ True
2435
+
2436
+ ::
2437
+
2438
+ sage: # needs sage.rings.finite_rings
2439
+ sage: A = random_matrix(GF(16007), 100, 100)
2440
+ sage: A.determinant().parent() is GF(16007)
2441
+ True
2442
+ sage: A.determinant() == A.transpose().determinant()
2443
+ True
2444
+ sage: B = random_matrix(GF(16007), 100, 100)
2445
+ sage: (A*B).determinant() == A.determinant() * B.determinant()
2446
+ True
2447
+
2448
+ Parallel computation::
2449
+
2450
+ sage: # needs sage.rings.finite_rings
2451
+ sage: A = random_matrix(GF(65521),200)
2452
+ sage: B = copy(A)
2453
+ sage: Parallelism().set('linbox', nproc=2)
2454
+ sage: d = A.determinant()
2455
+ sage: Parallelism().set('linbox', nproc=1) # switch off parallelization
2456
+ sage: e = B.determinant()
2457
+ sage: d==e
2458
+ True
2459
+
2460
+ TESTS::
2461
+
2462
+ sage: A = random_matrix(GF(7), 0, 0); A.det()
2463
+ 1
2464
+
2465
+ sage: A = random_matrix(GF(7), 0, 1); A.det()
2466
+ Traceback (most recent call last):
2467
+ ...
2468
+ ValueError: self must be a square matrix
2469
+
2470
+ sage: A = random_matrix(GF(7), 1, 0); A.det()
2471
+ Traceback (most recent call last):
2472
+ ...
2473
+ ValueError: self must be a square matrix
2474
+
2475
+ sage: A = matrix(GF(7), 5, 5); A.det() # needs sage.libs.pari
2476
+ 0
2477
+
2478
+ sage: # needs sage.rings.finite_rings
2479
+ sage: A = random_matrix(GF(16007), 0, 0); A.det()
2480
+ 1
2481
+ sage: A = random_matrix(GF(16007), 0, 1); A.det()
2482
+ Traceback (most recent call last):
2483
+ ...
2484
+ ValueError: self must be a square matrix
2485
+ sage: A = random_matrix(GF(16007), 1, 0); A.det()
2486
+ Traceback (most recent call last):
2487
+ ...
2488
+ ValueError: self must be a square matrix
2489
+ sage: A = matrix(GF(16007), 5, 5); A.det()
2490
+ 0
2491
+ """
2492
+ if self._nrows != self._ncols:
2493
+ raise ValueError("self must be a square matrix")
2494
+ if self._nrows == 0:
2495
+ return self._coerce_element(1)
2496
+
2497
+ if self.p > 2 and is_prime(self.p):
2498
+ x = self.fetch('det')
2499
+ if x is not None:
2500
+ return x
2501
+ d = linbox_det(self.p, self._entries, self._nrows)
2502
+ d2 = self._coerce_element(d)
2503
+ self.cache('det', d2)
2504
+ return d2
2505
+ else:
2506
+ return Matrix_dense.determinant(self)
2507
+
2508
+ cdef xgcd_eliminate(self, celement * row1, celement* row2, Py_ssize_t start_col):
2509
+ r"""
2510
+ Reduces ``row1`` and ``row2`` by a unimodular transformation
2511
+ using the xgcd relation between their first coefficients ``a`` and
2512
+ ``b``.
2513
+
2514
+ INPUT:
2515
+
2516
+ - ``row1``, ``row2`` -- the two rows to be transformed (within
2517
+ ``self``)
2518
+
2519
+ - ``start_col`` -- the column of the pivots in ``row1`` and
2520
+ ``row2``. It is assumed that all entries before ``start_col``
2521
+ in ``row1`` and ``row2`` are zero.
2522
+
2523
+ OUTPUT:
2524
+
2525
+ - g: the gcd of the first elements of row1 and
2526
+ row2 at column start_col
2527
+
2528
+ - put row1 = s \* row1 + t \* row2 row2 = w \*
2529
+ row1 + t \* row2 where g = sa + tb
2530
+ """
2531
+ cdef int p = <int>self.p
2532
+ cdef celement *row1_p
2533
+ cdef celement *row2_p
2534
+ cdef celement tmp
2535
+ cdef int g, s, t, v, w
2536
+ cdef Py_ssize_t nc, i
2537
+ cdef int a = <int>row1[start_col]
2538
+ cdef int b = <int>row2[start_col]
2539
+ g = ArithIntObj.c_xgcd_int(a, b, <int*>&s, <int*>&t)
2540
+ v = a/g
2541
+ w = -<int>b/g
2542
+ nc = self.ncols()
2543
+
2544
+ for i from start_col <= i < nc:
2545
+ tmp = (s * <int>row1[i] + t * <int>row2[i]) % p
2546
+ row2[i] = (w* <int>row1[i] + v*<int>row2[i]) % p
2547
+ row1[i] = tmp
2548
+ return g
2549
+
2550
+ cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
2551
+ """
2552
+ Rescale ``self[row]`` by ``multiple`` but only start at column
2553
+ index ``start_col``.
2554
+
2555
+ INPUT:
2556
+
2557
+ - ``row`` -- integer
2558
+ - ``multiple`` -- finite field element
2559
+ - ``start_col`` -- integer
2560
+
2561
+ EXAMPLES::
2562
+
2563
+ sage: A = matrix(GF(19), 4, 4, range(16)); A
2564
+ [ 0 1 2 3]
2565
+ [ 4 5 6 7]
2566
+ [ 8 9 10 11]
2567
+ [12 13 14 15]
2568
+
2569
+ sage: A.rescale_row(1, 17, 2); A
2570
+ [ 0 1 2 3]
2571
+ [ 4 5 7 5]
2572
+ [ 8 9 10 11]
2573
+ [12 13 14 15]
2574
+
2575
+ sage: 6*17 % 19 == A[1,2]
2576
+ True
2577
+
2578
+ sage: A = matrix(Integers(2^4), 4, 4, range(16)); A
2579
+ [ 0 1 2 3]
2580
+ [ 4 5 6 7]
2581
+ [ 8 9 10 11]
2582
+ [12 13 14 15]
2583
+
2584
+ sage: A.rescale_row(1, 3, 2); A
2585
+ [ 0 1 2 3]
2586
+ [ 4 5 2 5]
2587
+ [ 8 9 10 11]
2588
+ [12 13 14 15]
2589
+
2590
+ sage: 6*3 % 16 == A[1,2]
2591
+ True
2592
+ """
2593
+ cdef celement r, p
2594
+ cdef celement* v
2595
+ cdef Py_ssize_t i
2596
+ p = self.p
2597
+ v = self._matrix[row]
2598
+ for i from start_col <= i < self._ncols:
2599
+ v[i] = (v[i]*<celement>multiple) % p
2600
+
2601
+ cdef rescale_col_c(self, Py_ssize_t col, multiple, Py_ssize_t start_row):
2602
+ """
2603
+ EXAMPLES::
2604
+
2605
+ sage: B = MatrixSpace(Integers(37),3,3)([1]*9)
2606
+ sage: B
2607
+ [1 1 1]
2608
+ [1 1 1]
2609
+ [1 1 1]
2610
+ sage: B.rescale_col(1,5)
2611
+ sage: B
2612
+ [1 5 1]
2613
+ [1 5 1]
2614
+ [1 5 1]
2615
+
2616
+ Recaling need not include the entire row.::
2617
+
2618
+ sage: B.rescale_col(0,2,1); B
2619
+ [1 5 1]
2620
+ [2 5 1]
2621
+ [2 5 1]
2622
+
2623
+ Bounds are checked.::
2624
+
2625
+ sage: B.rescale_col(3,2)
2626
+ Traceback (most recent call last):
2627
+ ...
2628
+ IndexError: matrix column index out of range
2629
+
2630
+ Rescaling by a negative number::
2631
+
2632
+ sage: B.rescale_col(2,-3); B
2633
+ [ 1 5 34]
2634
+ [ 2 5 34]
2635
+ [ 2 5 34]
2636
+ """
2637
+ cdef celement r, p
2638
+ cdef celement* v
2639
+ cdef Py_ssize_t i
2640
+ p = self.p
2641
+ for i from start_row <= i < self._nrows:
2642
+ self._matrix[i][col] = (self._matrix[i][col]*<celement>multiple) % p
2643
+
2644
+ cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple, Py_ssize_t start_col):
2645
+ """
2646
+ Add ``multiple`` times ``self[row_from]`` to ``self[row_to]``
2647
+ statting in column ``start_col``.
2648
+
2649
+ EXAMPLES::
2650
+
2651
+ sage: A = random_matrix(GF(37), 10, 10)
2652
+ sage: B = copy(A)
2653
+
2654
+ sage: A.add_multiple_of_row(2, 3, 10)
2655
+ sage: all(A[i] == B[i] for i in range(10) if not i == 2)
2656
+ True
2657
+ sage: A[2] == B[2] + 10*B[3]
2658
+ True
2659
+
2660
+ sage: A.add_multiple_of_row(2, 3, 10, 4)
2661
+ sage: all(A[i] == B[i] for i in range(10) if not i == 2)
2662
+ True
2663
+ sage: A[2][:4] == B[2][:4] + 10*B[3][:4]
2664
+ True
2665
+ sage: A[2][4:] == B[2][4:] + 20*B[3][4:]
2666
+ True
2667
+ """
2668
+ cdef celement p
2669
+ cdef celement *v_from
2670
+ cdef celement *v_to
2671
+
2672
+ p = self.p
2673
+ v_from = self._matrix[row_from]
2674
+ v_to = self._matrix[row_to]
2675
+
2676
+ cdef Py_ssize_t i, nc
2677
+ nc = self._ncols
2678
+ for i from start_col <= i < nc:
2679
+ v_to[i] = ((<celement>multiple) * v_from[i] + v_to[i]) % p
2680
+
2681
+ cdef add_multiple_of_column_c(self, Py_ssize_t col_to, Py_ssize_t col_from, multiple, Py_ssize_t start_row):
2682
+ """
2683
+ Add ``multiple`` times ``self[row_from]`` to ``self[row_to]``
2684
+ statting in column ``start_col``.
2685
+
2686
+ EXAMPLES::
2687
+
2688
+ sage: A = random_matrix(GF(37), 10, 10)
2689
+ sage: B = copy(A)
2690
+
2691
+ sage: A.add_multiple_of_column(2, 3, 10)
2692
+ sage: all(A.column(i) == B.column(i) for i in range(10) if not i == 2)
2693
+ True
2694
+ sage: A.column(2) == B.column(2) + 10*B.column(3)
2695
+ True
2696
+
2697
+ sage: A.add_multiple_of_column(2, 3, 10, 4)
2698
+ sage: all(A.column(i) == B.column(i) for i in range(10) if not i == 2)
2699
+ True
2700
+ sage: A.column(2)[:4] == B.column(2)[:4] + 10*B.column(3)[:4]
2701
+ True
2702
+ sage: A.column(2)[4:] == B.column(2)[4:] + 20*B.column(3)[4:]
2703
+ True
2704
+ """
2705
+ cdef celement p
2706
+ cdef celement **m
2707
+
2708
+ m = self._matrix
2709
+ p = self.p
2710
+
2711
+ cdef Py_ssize_t i, nr
2712
+ nr = self._nrows
2713
+ for i from start_row <= i < self._nrows:
2714
+ m[i][col_to] = (m[i][col_to] + (<celement>multiple) * m[i][col_from]) %p
2715
+
2716
+ cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
2717
+ """
2718
+ EXAMPLES::
2719
+
2720
+ sage: A = matrix(Integers(8), 2,[1,2,3,4])
2721
+ sage: A.swap_rows(0,1)
2722
+ sage: A
2723
+ [3 4]
2724
+ [1 2]
2725
+ """
2726
+ cdef celement* r1 = self._matrix[row1]
2727
+ cdef celement* r2 = self._matrix[row2]
2728
+ cdef celement temp
2729
+ for i in range(self._ncols):
2730
+ temp = r1[i]
2731
+ r1[i] = r2[i]
2732
+ r2[i] = temp
2733
+
2734
+ cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
2735
+ """
2736
+ EXAMPLES::
2737
+
2738
+ sage: A = matrix(Integers(8), 2,[1,2,3,4])
2739
+ sage: A.swap_columns(0,1)
2740
+ sage: A
2741
+ [2 1]
2742
+ [4 3]
2743
+ """
2744
+ cdef Py_ssize_t i, nr
2745
+ cdef celement t
2746
+ cdef celement **m
2747
+ m = self._matrix
2748
+ nr = self._nrows
2749
+ for i from 0 <= i < nr:
2750
+ t = m[i][col1]
2751
+ m[i][col1] = m[i][col2]
2752
+ m[i][col2] = t
2753
+
2754
+ def randomize(self, density=1, nonzero=False):
2755
+ """
2756
+ Randomize ``density`` proportion of the entries of this
2757
+ matrix, leaving the rest unchanged.
2758
+
2759
+ INPUT:
2760
+
2761
+ - ``density`` -- integer; proportion (roughly) to be considered
2762
+ for changes
2763
+ - ``nonzero`` -- boolean (default: ``False``); whether the new
2764
+ entries are forced to be nonzero
2765
+
2766
+ OUTPUT: none, the matrix is modified in-space
2767
+
2768
+ EXAMPLES::
2769
+
2770
+ sage: A = matrix(GF(5), 5, 5, 0)
2771
+ sage: total_count = 0
2772
+ sage: from collections import defaultdict
2773
+ sage: dic = defaultdict(Integer)
2774
+ sage: def add_samples(density):
2775
+ ....: global dic, total_count
2776
+ ....: for _ in range(100):
2777
+ ....: A = Matrix(GF(5), 5, 5, 0)
2778
+ ....: A.randomize(density)
2779
+ ....: for a in A.list():
2780
+ ....: dic[a] += 1
2781
+ ....: total_count += 1.0
2782
+
2783
+ sage: add_samples(1.0)
2784
+ sage: while not all(abs(dic[a]/total_count - 1/5) < 0.01 for a in dic):
2785
+ ....: add_samples(1.0)
2786
+
2787
+ sage: def add_sample(density):
2788
+ ....: global density_sum, total_count
2789
+ ....: total_count += 1.0
2790
+ ....: density_sum += random_matrix(GF(5), 1000, 1000, density=density).density()
2791
+
2792
+ sage: density_sum = 0.0
2793
+ sage: total_count = 0.0
2794
+ sage: add_sample(0.5)
2795
+ sage: expected_density = 1.0 - (999/1000)^500
2796
+ sage: expected_density
2797
+ 0.3936...
2798
+ sage: while abs(density_sum/total_count - expected_density) > 0.001:
2799
+ ....: add_sample(0.5)
2800
+
2801
+ The matrix is updated instead of overwritten::
2802
+
2803
+ sage: def add_sample(density):
2804
+ ....: global density_sum, total_count
2805
+ ....: total_count += 1.0
2806
+ ....: A = random_matrix(GF(5), 1000, 1000, density=density)
2807
+ ....: A.randomize(density=density, nonzero=True)
2808
+ ....: density_sum += A.density()
2809
+
2810
+ sage: density_sum = 0.0
2811
+ sage: total_count = 0.0
2812
+ sage: add_sample(0.5)
2813
+ sage: expected_density = 1.0 - (999/1000)^1000
2814
+ sage: expected_density
2815
+ 0.6323...
2816
+ sage: while abs(density_sum/total_count - expected_density) > 0.001:
2817
+ ....: add_sample(0.5)
2818
+
2819
+ sage: density_sum = 0.0
2820
+ sage: total_count = 0.0
2821
+ sage: add_sample(0.1)
2822
+ sage: expected_density = 1.0 - (999/1000)^200
2823
+ sage: expected_density
2824
+ 0.1813...
2825
+ sage: while abs(density_sum/total_count - expected_density) > 0.001:
2826
+ ....: add_sample(0.1)
2827
+ """
2828
+ density = float(density)
2829
+ if density <= 0:
2830
+ return
2831
+ if density > 1:
2832
+ density = float(1)
2833
+
2834
+ self.check_mutability()
2835
+ self.clear_cache()
2836
+
2837
+ cdef randstate rstate = current_randstate()
2838
+
2839
+ cdef int nc, p = <int>self.p
2840
+ cdef long pm1
2841
+
2842
+ if not nonzero:
2843
+ # Original code, before adding the ``nonzero`` option.
2844
+ if density == 1:
2845
+ for i from 0 <= i < self._nrows*self._ncols:
2846
+ self._entries[i] = rstate.c_random() % p
2847
+ else:
2848
+ nc = self._ncols
2849
+ num_per_row = int(density * nc)
2850
+ sig_on()
2851
+ for i from 0 <= i < self._nrows:
2852
+ for j from 0 <= j < num_per_row:
2853
+ k = rstate.c_random() % nc
2854
+ self._matrix[i][k] = rstate.c_random() % p
2855
+ sig_off()
2856
+ else:
2857
+ # New code, to implement the ``nonzero`` option.
2858
+ pm1 = p - 1
2859
+ if density == 1:
2860
+ for i from 0 <= i < self._nrows*self._ncols:
2861
+ self._entries[i] = (rstate.c_random() % pm1) + 1
2862
+ else:
2863
+ nc = self._ncols
2864
+ num_per_row = int(density * nc)
2865
+ sig_on()
2866
+ for i from 0 <= i < self._nrows:
2867
+ for j from 0 <= j < num_per_row:
2868
+ k = rstate.c_random() % nc
2869
+ self._matrix[i][k] = (rstate.c_random() % pm1) + 1
2870
+ sig_off()
2871
+
2872
+ def _magma_init_(self, magma):
2873
+ """
2874
+ Return a string representation of ``self`` in Magma form.
2875
+
2876
+ INPUT:
2877
+
2878
+ - ``magma`` -- a Magma session
2879
+
2880
+ OUTPUT: string
2881
+
2882
+ EXAMPLES::
2883
+
2884
+ sage: a = matrix(GF(389),2,2,[1..4])
2885
+ sage: magma(a) # optional - magma
2886
+ [ 1 2]
2887
+ [ 3 4]
2888
+ sage: a._magma_init_(magma) # optional - magma
2889
+ 'Matrix(GF(389),2,2,StringToIntegerSequence("1 2 3 4"))'
2890
+
2891
+ A consistency check::
2892
+
2893
+ sage: a = random_matrix(GF(13),50); b = random_matrix(GF(13),50)
2894
+ sage: magma(a*b) == magma(a)*magma(b) # optional - magma
2895
+ True
2896
+ """
2897
+ s = self.base_ring()._magma_init_(magma)
2898
+ return 'Matrix(%s,%s,%s,StringToIntegerSequence("%s"))' % (
2899
+ s, self._nrows, self._ncols, self._export_as_string())
2900
+
2901
+ cpdef _export_as_string(self):
2902
+ """
2903
+ Return space separated string of the entries in this matrix.
2904
+
2905
+ EXAMPLES::
2906
+
2907
+ sage: A = matrix(GF(997),2,3,[1,2,5,-3,8,2]); A
2908
+ [ 1 2 5]
2909
+ [994 8 2]
2910
+ sage: A._export_as_string()
2911
+ '1 2 5 994 8 2'
2912
+ """
2913
+ cdef int ndigits = len(str(self.p))
2914
+
2915
+ cdef Py_ssize_t i, n
2916
+ cdef char *s
2917
+ cdef char *t
2918
+
2919
+ if self._nrows == 0 or self._ncols == 0:
2920
+ data = ''
2921
+ else:
2922
+ n = self._nrows*self._ncols*(ndigits + 1) + 2 # spaces between each number plus trailing null
2923
+ s = <char*>check_malloc(n * sizeof(char))
2924
+ t = s
2925
+ sig_on()
2926
+ for i in range(self._nrows * self._ncols):
2927
+ t += snprintf(t, ndigits+2, "%ld ", <long>self._entries[i])
2928
+
2929
+ sig_off()
2930
+ data = char_to_str(s)[:-1]
2931
+ sig_free(s)
2932
+ return data
2933
+
2934
+ def _list(self):
2935
+ """
2936
+ Return list of elements of ``self``. This method is called by ``self.list()``.
2937
+
2938
+ EXAMPLES::
2939
+
2940
+ sage: w = matrix(GF(19), 2, 3, [1..6])
2941
+ sage: w.list()
2942
+ [1, 2, 3, 4, 5, 6]
2943
+ sage: w._list()
2944
+ [1, 2, 3, 4, 5, 6]
2945
+ sage: w.list()[0].parent()
2946
+ Finite Field of size 19
2947
+
2948
+ TESTS::
2949
+
2950
+ sage: w = random_matrix(GF(3),100)
2951
+ sage: w.parent()(w.list()) == w
2952
+ True
2953
+ """
2954
+ cdef Py_ssize_t i
2955
+ F = self.base_ring()
2956
+ return [F(<int>self._entries[i]) for i in range(self._nrows*self._ncols)]
2957
+
2958
+ def lift(self):
2959
+ """
2960
+ Return the lift of this matrix to the integers.
2961
+
2962
+ EXAMPLES::
2963
+
2964
+ sage: A = matrix(GF(7),2,3,[1..6])
2965
+ sage: A.lift()
2966
+ [1 2 3]
2967
+ [4 5 6]
2968
+ sage: A.lift().parent()
2969
+ Full MatrixSpace of 2 by 3 dense matrices over Integer Ring
2970
+
2971
+ sage: # needs sage.rings.finite_rings
2972
+ sage: A = matrix(GF(16007),2,3,[1..6])
2973
+ sage: A.lift()
2974
+ [1 2 3]
2975
+ [4 5 6]
2976
+ sage: A.lift().parent()
2977
+ Full MatrixSpace of 2 by 3 dense matrices over Integer Ring
2978
+
2979
+ Subdivisions are preserved when lifting::
2980
+
2981
+ sage: A.subdivide([], [1,1]); A
2982
+ [1||2 3]
2983
+ [4||5 6]
2984
+ sage: A.lift()
2985
+ [1||2 3]
2986
+ [4||5 6]
2987
+ """
2988
+ cdef Py_ssize_t i, j
2989
+
2990
+ cdef Matrix_integer_dense L
2991
+ cdef object P = matrix_space.MatrixSpace(ZZ, self._nrows,
2992
+ self._ncols, sparse=False)
2993
+ L = Matrix_integer_dense(P, ZZ(0), False, False)
2994
+ cdef celement* A_row
2995
+ for i in range(self._nrows):
2996
+ A_row = self._matrix[i]
2997
+ for j in range(self._ncols):
2998
+ L.set_unsafe_double(i, j, A_row[j])
2999
+ if self._subdivisions is not None:
3000
+ L.subdivide(*self.subdivisions())
3001
+ return L
3002
+
3003
+ def transpose(self):
3004
+ """
3005
+ Return the transpose of ``self``, without changing ``self``.
3006
+
3007
+ EXAMPLES:
3008
+
3009
+ We create a matrix, compute its transpose, and note that
3010
+ the original matrix is not changed. ::
3011
+
3012
+ sage: M = MatrixSpace(GF(41), 2)
3013
+ sage: A = M([1,2,3,4])
3014
+ sage: B = A.transpose()
3015
+ sage: B
3016
+ [1 3]
3017
+ [2 4]
3018
+ sage: A
3019
+ [1 2]
3020
+ [3 4]
3021
+
3022
+ ``.T`` is a convenient shortcut for the transpose::
3023
+
3024
+ sage: A.T
3025
+ [1 3]
3026
+ [2 4]
3027
+
3028
+ ::
3029
+
3030
+ sage: A.subdivide(None, 1); A
3031
+ [1|2]
3032
+ [3|4]
3033
+ sage: A.transpose()
3034
+ [1 3]
3035
+ [---]
3036
+ [2 4]
3037
+ """
3038
+ cdef Py_ssize_t nrows = self._nrows
3039
+ cdef Py_ssize_t ncols = self._ncols
3040
+
3041
+ cdef Matrix_modn_dense_template M = self.new_matrix(nrows=ncols, ncols=nrows)
3042
+ cdef Py_ssize_t i, j
3043
+
3044
+ for i from 0 <= i < ncols:
3045
+ for j from 0 <= j < nrows:
3046
+ M._entries[j+i*nrows] = self._entries[i+j*ncols]
3047
+
3048
+ if self._subdivisions is not None:
3049
+ row_divs, col_divs = self.subdivisions()
3050
+ M.subdivide(col_divs, row_divs)
3051
+
3052
+ return M
3053
+
3054
+ cdef _stack_impl(self, bottom):
3055
+ r"""
3056
+ Implementation of :meth:`stack` by returning a new matrix
3057
+ formed by appending the matrix ``bottom`` beneath ``self``.
3058
+
3059
+ Assume that ``self`` and ``other`` are compatible in the sense
3060
+ that they have the same base ring and that both are dense.
3061
+
3062
+ INPUT:
3063
+
3064
+ - ``bottom`` -- a matrix compatible with ``self``
3065
+
3066
+ EXAMPLES:
3067
+
3068
+ Stacking with a matrix::
3069
+
3070
+ sage: A = matrix(GF(41), 4, 3, range(12))
3071
+ sage: B = matrix(GF(41), 3, 3, range(9))
3072
+ sage: A.stack(B)
3073
+ [ 0 1 2]
3074
+ [ 3 4 5]
3075
+ [ 6 7 8]
3076
+ [ 9 10 11]
3077
+ [ 0 1 2]
3078
+ [ 3 4 5]
3079
+ [ 6 7 8]
3080
+
3081
+ Stacking with a vector::
3082
+
3083
+ sage: A = matrix(GF(41), 3, 2, [0, 2, 4, 6, 8, 10])
3084
+ sage: v = vector(GF(41), 2, [100, 200])
3085
+ sage: A.stack(v)
3086
+ [ 0 2]
3087
+ [ 4 6]
3088
+ [ 8 10]
3089
+ [18 36]
3090
+
3091
+ Errors are raised if the sizes are incompatible::
3092
+
3093
+ sage: A = matrix(GF(41), [[1, 2],[3, 4]])
3094
+ sage: B = matrix(GF(41), [[10, 20, 30], [40, 50, 60]])
3095
+ sage: A.stack(B)
3096
+ Traceback (most recent call last):
3097
+ ...
3098
+ TypeError: number of columns must be the same, not 2 and 3
3099
+
3100
+ sage: v = vector(GF(41), [100, 200, 300])
3101
+ sage: A.stack(v)
3102
+ Traceback (most recent call last):
3103
+ ...
3104
+ TypeError: number of columns must be the same, not 2 and 3
3105
+
3106
+ Setting ``subdivide`` to ``True`` will, in its simplest form,
3107
+ add a subdivision between ``self`` and ``bottom``::
3108
+
3109
+ sage: A = matrix(GF(41), 2, 5, range(10))
3110
+ sage: B = matrix(GF(41), 3, 5, range(15))
3111
+ sage: A.stack(B, subdivide=True)
3112
+ [ 0 1 2 3 4]
3113
+ [ 5 6 7 8 9]
3114
+ [--------------]
3115
+ [ 0 1 2 3 4]
3116
+ [ 5 6 7 8 9]
3117
+ [10 11 12 13 14]
3118
+
3119
+ Row subdivisions are preserved by stacking, and enriched,
3120
+ if subdivisions are requested. (So multiple stackings can
3121
+ be recorded.) ::
3122
+
3123
+ sage: A = matrix(GF(41), 2, 4, range(8))
3124
+ sage: A.subdivide([1], None)
3125
+ sage: B = matrix(GF(41), 3, 4, range(12))
3126
+ sage: B.subdivide([2], None)
3127
+ sage: A.stack(B, subdivide=True)
3128
+ [ 0 1 2 3]
3129
+ [-----------]
3130
+ [ 4 5 6 7]
3131
+ [-----------]
3132
+ [ 0 1 2 3]
3133
+ [ 4 5 6 7]
3134
+ [-----------]
3135
+ [ 8 9 10 11]
3136
+
3137
+ Column subdivisions can be preserved, but only if they are identical.
3138
+ Otherwise, this information is discarded and must be managed
3139
+ separately. ::
3140
+
3141
+ sage: A = matrix(GF(41), 2, 5, range(10))
3142
+ sage: A.subdivide(None, [2,4])
3143
+ sage: B = matrix(GF(41), 3, 5, range(15))
3144
+ sage: B.subdivide(None, [2,4])
3145
+ sage: A.stack(B, subdivide=True)
3146
+ [ 0 1| 2 3| 4]
3147
+ [ 5 6| 7 8| 9]
3148
+ [-----+-----+--]
3149
+ [ 0 1| 2 3| 4]
3150
+ [ 5 6| 7 8| 9]
3151
+ [10 11|12 13|14]
3152
+
3153
+ sage: A.subdivide(None, [1,2])
3154
+ sage: A.stack(B, subdivide=True)
3155
+ [ 0 1 2 3 4]
3156
+ [ 5 6 7 8 9]
3157
+ [--------------]
3158
+ [ 0 1 2 3 4]
3159
+ [ 5 6 7 8 9]
3160
+ [10 11 12 13 14]
3161
+
3162
+ The result retains the base ring of ``self`` by coercing
3163
+ the elements of ``bottom`` into the base ring of ``self``::
3164
+
3165
+ sage: A = matrix(GF(41), 1, 2, [1,2])
3166
+ sage: B = matrix(ZZ, 1, 2, [100, 100])
3167
+ sage: C = A.stack(B); C
3168
+ [ 1 2]
3169
+ [18 18]
3170
+
3171
+ sage: C.parent()
3172
+ Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 41
3173
+
3174
+ sage: D = B.stack(A); D
3175
+ [18 18]
3176
+ [ 1 2]
3177
+
3178
+ sage: D.parent()
3179
+ Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 41
3180
+ """
3181
+ cdef Matrix_modn_dense_template other = <Matrix_modn_dense_template> bottom
3182
+ cdef Matrix_modn_dense_template M = self.new_matrix(nrows=self._nrows+other._nrows,
3183
+ ncols=self._ncols)
3184
+ cdef Py_ssize_t selfsize = self._ncols * self._nrows
3185
+ memcpy(M._entries, self._entries, sizeof(celement)*selfsize)
3186
+ memcpy(M._entries+selfsize, other._entries, sizeof(celement)*other._ncols*other._nrows)
3187
+ return M
3188
+
3189
+ def submatrix(self,
3190
+ Py_ssize_t row=0, Py_ssize_t col=0,
3191
+ Py_ssize_t nrows=-1, Py_ssize_t ncols=-1):
3192
+ r"""
3193
+ Return the matrix constructed from ``self`` using the specified
3194
+ range of rows and columns.
3195
+
3196
+ INPUT:
3197
+
3198
+ - ``row``, ``col`` -- index of the starting row and column;
3199
+ indices start at zero
3200
+
3201
+ - ``nrows``, ``ncols`` -- (optional) number of rows and columns to
3202
+ take; if not provided, take all rows below and all columns to
3203
+ the right of the starting entry
3204
+
3205
+ .. SEEALSO::
3206
+
3207
+ The functions :func:`matrix_from_rows`,
3208
+ :func:`matrix_from_columns`, and
3209
+ :func:`matrix_from_rows_and_columns` allow one to select
3210
+ arbitrary subsets of rows and/or columns.
3211
+
3212
+ EXAMPLES:
3213
+
3214
+ Take the `3 \times 3` submatrix starting from entry `(1,1)` in a
3215
+ `4 \times 4` matrix::
3216
+
3217
+ sage: m = matrix(GF(17),4, [1..16])
3218
+ sage: m.submatrix(1, 1)
3219
+ [ 6 7 8]
3220
+ [10 11 12]
3221
+ [14 15 16]
3222
+
3223
+ Same thing, except take only two rows::
3224
+
3225
+ sage: m.submatrix(1, 1, 2)
3226
+ [ 6 7 8]
3227
+ [10 11 12]
3228
+
3229
+ And now take only one column::
3230
+
3231
+ sage: m.submatrix(1, 1, 2, 1)
3232
+ [ 6]
3233
+ [10]
3234
+
3235
+ You can take zero rows or columns if you want::
3236
+
3237
+ sage: m.submatrix(0, 0, 0)
3238
+ []
3239
+ sage: parent(m.submatrix(0, 0, 0))
3240
+ Full MatrixSpace of 0 by 4 dense matrices over Finite Field of size 17
3241
+ """
3242
+ if ncols == -1:
3243
+ ncols = self._ncols - col
3244
+
3245
+ if nrows == -1:
3246
+ nrows = self._nrows - row
3247
+
3248
+ if nrows < 0 or row < 0 or row + nrows > self._nrows:
3249
+ raise IndexError("rows out of range")
3250
+ if ncols < 0 or col < 0 or col + ncols > self._ncols:
3251
+ raise IndexError("columns out of range")
3252
+
3253
+ cdef Matrix_modn_dense_template M = self.new_matrix(nrows=nrows, ncols=ncols)
3254
+
3255
+ if col == 0 and ncols == self._ncols:
3256
+ memcpy(M._entries, self._matrix[row], sizeof(celement)*ncols*nrows)
3257
+ return M
3258
+
3259
+ cdef Py_ssize_t i, r
3260
+ for i, r in enumerate(range(row, row+nrows)) :
3261
+ memcpy(M._matrix[i], self._matrix[r]+col, sizeof(celement)*ncols)
3262
+
3263
+ return M
3264
+
3265
+ def _matrices_from_rows(self, Py_ssize_t nrows, Py_ssize_t ncols):
3266
+ """
3267
+ Make a list of matrix from the rows of this matrix. This is a
3268
+ fairly technical function which is used internally, e.g., by
3269
+ the cyclotomic field linear algebra code.
3270
+
3271
+ INPUT:
3272
+
3273
+ - ``nrows`` -- integer
3274
+
3275
+ - ``ncols`` -- integer
3276
+
3277
+ EXAMPLES::
3278
+
3279
+ sage: A = matrix(GF(127), 4, 4, range(16))
3280
+ sage: A
3281
+ [ 0 1 2 3]
3282
+ [ 4 5 6 7]
3283
+ [ 8 9 10 11]
3284
+ [12 13 14 15]
3285
+ sage: A._matrices_from_rows(2,2)
3286
+ [
3287
+ [0 1] [4 5] [ 8 9] [12 13]
3288
+ [2 3], [6 7], [10 11], [14 15]
3289
+ ]
3290
+
3291
+ OUTPUT: list of matrices
3292
+ """
3293
+ if nrows * ncols != self._ncols:
3294
+ raise ValueError("nrows * ncols must equal self's number of columns")
3295
+
3296
+ cdef Matrix_modn_dense_template M
3297
+ cdef Py_ssize_t i
3298
+ cdef Py_ssize_t n = nrows * ncols
3299
+ ans = []
3300
+ for i from 0 <= i < self._nrows:
3301
+ M = self.new_matrix(nrows = nrows, ncols = ncols)
3302
+ memcpy(M._entries, self._entries+i*n, sizeof(celement)*n)
3303
+ ans.append(M)
3304
+ return ans
3305
+
3306
+ def matrix_from_columns(self, columns):
3307
+ """
3308
+ Return the matrix constructed from ``self`` using columns with indices
3309
+ in the columns list.
3310
+
3311
+ EXAMPLES::
3312
+
3313
+ sage: M = MatrixSpace(Integers(8),3,3)
3314
+ sage: A = M(range(9)); A
3315
+ [0 1 2]
3316
+ [3 4 5]
3317
+ [6 7 0]
3318
+ sage: A.matrix_from_columns([2,1])
3319
+ [2 1]
3320
+ [5 4]
3321
+ [0 7]
3322
+ """
3323
+ cdef Py_ssize_t ncols = len(columns)
3324
+
3325
+ # Construct new matrix
3326
+ cdef Matrix_modn_dense_template A = self.new_matrix(ncols=ncols)
3327
+ cdef Py_ssize_t i, j, col
3328
+ for j, col in enumerate(columns):
3329
+ if col < 0 or col >= self._ncols:
3330
+ raise IndexError("column index out of range")
3331
+ for i in range(self._nrows):
3332
+ A._matrix[i][j] = self._matrix[i][col]
3333
+
3334
+ return A
3335
+
3336
+ def matrix_from_rows(self, rows):
3337
+ """
3338
+ Return the matrix constructed from ``self`` using rows with indices in
3339
+ the rows list.
3340
+
3341
+ EXAMPLES::
3342
+
3343
+ sage: M = MatrixSpace(Integers(8),3,3)
3344
+ sage: A = M(range(9)); A
3345
+ [0 1 2]
3346
+ [3 4 5]
3347
+ [6 7 0]
3348
+ sage: A.matrix_from_rows([2,1])
3349
+ [6 7 0]
3350
+ [3 4 5]
3351
+ """
3352
+ cdef Py_ssize_t nrows = len(rows)
3353
+
3354
+ # Construct new matrix
3355
+ cdef Matrix_modn_dense_template A = self.new_matrix(nrows=nrows)
3356
+
3357
+ cdef Py_ssize_t i, row
3358
+ for i, row in enumerate(rows):
3359
+ if row < 0 or row >= self._nrows:
3360
+ raise IndexError("row index out of range")
3361
+ memcpy(A._matrix[i], self._matrix[row], sizeof(celement)*self._ncols)
3362
+
3363
+ return A
3364
+
3365
+ def matrix_from_rows_and_columns(self, rows, columns):
3366
+ """
3367
+ Return the matrix constructed from ``self`` from the given rows and
3368
+ columns.
3369
+
3370
+ EXAMPLES::
3371
+
3372
+ sage: M = MatrixSpace(Integers(8),3,3)
3373
+ sage: A = M(range(9)); A
3374
+ [0 1 2]
3375
+ [3 4 5]
3376
+ [6 7 0]
3377
+ sage: A.matrix_from_rows_and_columns([1], [0,2])
3378
+ [3 5]
3379
+ sage: A.matrix_from_rows_and_columns([1,2], [1,2])
3380
+ [4 5]
3381
+ [7 0]
3382
+
3383
+ Note that row and column indices can be reordered or repeated::
3384
+
3385
+ sage: A.matrix_from_rows_and_columns([2,1], [2,1])
3386
+ [0 7]
3387
+ [5 4]
3388
+
3389
+ For example here we take from row 1 columns 2 then 0 twice, and do
3390
+ this 3 times::
3391
+
3392
+ sage: A.matrix_from_rows_and_columns([1,1,1],[2,0,0])
3393
+ [5 3 3]
3394
+ [5 3 3]
3395
+ [5 3 3]
3396
+
3397
+ AUTHORS:
3398
+
3399
+ - Jaap Spies (2006-02-18)
3400
+
3401
+ - Didier Deshommes: some Pyrex speedups implemented
3402
+ """
3403
+ cdef Py_ssize_t ncols = len(columns)
3404
+ cdef Py_ssize_t nrows = len(rows)
3405
+
3406
+ # Check whether column indices are valid
3407
+ cdef Py_ssize_t i, j, row, col
3408
+ for col in columns:
3409
+ if col < 0 or col >= self._ncols:
3410
+ raise IndexError("column index out of range")
3411
+
3412
+ # Construct new matrix
3413
+ cdef Matrix_modn_dense_template A = self.new_matrix(nrows=nrows, ncols=ncols)
3414
+ for i, row in enumerate(rows):
3415
+ if row < 0 or row >= self._nrows:
3416
+ raise IndexError("row index out of range")
3417
+ for j, col in enumerate(columns):
3418
+ A._matrix[i][j] = self._matrix[row][col]
3419
+
3420
+ return A
3421
+
3422
+ def __bool__(self):
3423
+ """
3424
+ Test whether this matrix is zero.
3425
+
3426
+ EXAMPLES::
3427
+
3428
+ sage: A = matrix(GF(7), 10, 10, range(100))
3429
+ sage: A == 0 # indirect doctest
3430
+ False
3431
+ sage: A.is_zero()
3432
+ False
3433
+
3434
+ sage: A = matrix(Integers(10), 10, 10)
3435
+ sage: bool(A)
3436
+ False
3437
+
3438
+ sage: # needs sage.rings.finite_rings
3439
+ sage: A = matrix(GF(16007), 0, 0)
3440
+ sage: A.is_zero()
3441
+ True
3442
+ sage: A = matrix(GF(16007), 1, 0)
3443
+ sage: A.is_zero()
3444
+ True
3445
+ sage: A = matrix(GF(16007), 0, 1)
3446
+ sage: A.is_zero()
3447
+ True
3448
+ """
3449
+ return not linbox_is_zero(self.p, self._entries, self._nrows, self._ncols)
3450
+
3451
+ _matrix_from_rows_of_matrices = staticmethod(__matrix_from_rows_of_matrices)
3452
+
3453
+ cdef int _copy_row_to_mod_int_array(self, mod_int *to, Py_ssize_t i) noexcept:
3454
+ cdef Py_ssize_t j
3455
+ cdef celement *_from = self._entries+(i*self._ncols)
3456
+ for j in range(self._ncols):
3457
+ to[j] = <mod_int>_from[j]
3458
+
3459
+ cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) except -1:
3460
+ r"""
3461
+ Return 1 if the entry ``(i, j)`` is zero, otherwise 0.
3462
+
3463
+ EXAMPLES::
3464
+
3465
+ sage: M = Matrix(GF(49), 2, [1,2,-2,0]) # needs sage.rings.finite_rings
3466
+ sage: M.zero_pattern_matrix() # indirect doctest # needs sage.rings.finite_rings
3467
+ [0 0]
3468
+ [0 1]
3469
+
3470
+ sage: M = Matrix(Integers(10), 2, [1,2,-2,0])
3471
+ sage: M.zero_pattern_matrix() # indirect doctest
3472
+ [0 0]
3473
+ [0 1]
3474
+ """
3475
+ return self._entries[j+i*self._ncols] == 0