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