passagemath-linbox 10.6.43__cp313-cp313-macosx_13_0_arm64.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.
- passagemath_linbox/.dylibs/libfflas.1.dylib +0 -0
- passagemath_linbox/.dylibs/libffpack.1.dylib +0 -0
- passagemath_linbox/.dylibs/libflint.22.0.dylib +0 -0
- passagemath_linbox/.dylibs/libgd.3.dylib +0 -0
- passagemath_linbox/.dylibs/libgfortran.5.dylib +0 -0
- passagemath_linbox/.dylibs/libgivaro.9.dylib +0 -0
- passagemath_linbox/.dylibs/libgmp.10.dylib +0 -0
- passagemath_linbox/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_linbox/.dylibs/libiml.0.dylib +0 -0
- passagemath_linbox/.dylibs/liblinbox.0.dylib +0 -0
- passagemath_linbox/.dylibs/libm4ri.1.dylib +0 -0
- passagemath_linbox/.dylibs/libm4rie.1.dylib +0 -0
- passagemath_linbox/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_linbox/.dylibs/libopenblasp-r0.3.29.dylib +0 -0
- passagemath_linbox/.dylibs/libpng16.16.dylib +0 -0
- passagemath_linbox/.dylibs/libquadmath.0.dylib +0 -0
- passagemath_linbox/.dylibs/libz.1.3.1.dylib +0 -0
- passagemath_linbox/__init__.py +3 -0
- passagemath_linbox-10.6.43.dist-info/METADATA +100 -0
- passagemath_linbox-10.6.43.dist-info/RECORD +75 -0
- passagemath_linbox-10.6.43.dist-info/WHEEL +6 -0
- passagemath_linbox-10.6.43.dist-info/top_level.txt +3 -0
- sage/all__sagemath_linbox.py +2 -0
- sage/geometry/all__sagemath_linbox.py +1 -0
- sage/geometry/integral_points.pxi +1426 -0
- sage/geometry/integral_points_integer_dense.cpython-313-darwin.so +0 -0
- sage/geometry/integral_points_integer_dense.pyx +7 -0
- sage/libs/all__sagemath_linbox.py +1 -0
- sage/libs/iml.pxd +10 -0
- sage/libs/linbox/__init__.py +1 -0
- sage/libs/linbox/conversion.pxd +185 -0
- sage/libs/linbox/fflas.pxd +189 -0
- sage/libs/linbox/givaro.pxd +109 -0
- sage/libs/linbox/linbox.pxd +219 -0
- sage/libs/linbox/linbox_flint_interface.cpython-313-darwin.so +0 -0
- sage/libs/linbox/linbox_flint_interface.pxd +18 -0
- sage/libs/linbox/linbox_flint_interface.pyx +192 -0
- sage/libs/m4ri.pxd +198 -0
- sage/libs/m4rie.pxd +204 -0
- sage/matrix/all__sagemath_linbox.py +1 -0
- sage/matrix/matrix_cyclo_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_cyclo_linbox.pyx +361 -0
- sage/matrix/matrix_gf2e_dense.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_gf2e_dense.pxd +15 -0
- sage/matrix/matrix_gf2e_dense.pyx +1573 -0
- sage/matrix/matrix_integer_iml.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_integer_iml.pyx +316 -0
- sage/matrix/matrix_integer_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_integer_linbox.pxd +5 -0
- sage/matrix/matrix_integer_linbox.pyx +358 -0
- sage/matrix/matrix_integer_sparse_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_integer_sparse_linbox.pyx +465 -0
- sage/matrix/matrix_mod2_dense.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_mod2_dense.pxd +14 -0
- sage/matrix/matrix_mod2_dense.pyx +2789 -0
- sage/matrix/matrix_modn_dense_double.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_modn_dense_double.pyx +179 -0
- sage/matrix/matrix_modn_dense_float.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_modn_dense_float.pyx +154 -0
- sage/matrix/matrix_modn_sparse.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_modn_sparse.pyx +871 -0
- sage/matrix/matrix_rational_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_rational_linbox.pyx +36 -0
- sage/matrix/misc.cpython-313-darwin.so +0 -0
- sage/matrix/misc.pyx +418 -0
- sage/modules/all__sagemath_linbox.py +1 -0
- sage/modules/numpy_util.cpython-313-darwin.so +0 -0
- sage/modules/numpy_util.pxd +10 -0
- sage/modules/numpy_util.pyx +136 -0
- sage/modules/vector_mod2_dense.cpython-313-darwin.so +0 -0
- sage/modules/vector_mod2_dense.pxd +11 -0
- sage/modules/vector_mod2_dense.pyx +547 -0
- sage/rings/all__sagemath_linbox.py +1 -0
- sage/rings/finite_rings/all__sagemath_linbox.py +1 -0
- sage/rings/polynomial/all__sagemath_linbox.py +1 -0
|
@@ -0,0 +1,1573 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-linbox
|
|
2
|
+
# distutils: libraries = m4rie M4RI_LIBRARIES M_LIBRARIES
|
|
3
|
+
# distutils: library_dirs = M4RI_LIBDIR
|
|
4
|
+
# distutils: include_dirs = M4RI_INCDIR
|
|
5
|
+
# distutils: extra_compile_args = M4RI_CFLAGS
|
|
6
|
+
# sage.doctest: needs sage.rings.finite_rings
|
|
7
|
+
r"""
|
|
8
|
+
Dense matrices over `\GF{2^e}` for `2 \leq e \leq 16` using the M4RIE library
|
|
9
|
+
|
|
10
|
+
The M4RIE library offers two matrix representations:
|
|
11
|
+
|
|
12
|
+
1) ``mzed_t``
|
|
13
|
+
|
|
14
|
+
m x n matrices over `\GF{2^e}` are internally represented roughly as
|
|
15
|
+
m x (en) matrices over `\GF{2}`. Several elements are packed into
|
|
16
|
+
words such that each element is filled with zeroes until the next
|
|
17
|
+
power of two. Thus, for example, elements of `\GF{2^3}` are
|
|
18
|
+
represented as ``[0xxx|0xxx|0xxx|0xxx|...]``. This representation is
|
|
19
|
+
wrapped as :class:`Matrix_gf2e_dense` in Sage.
|
|
20
|
+
|
|
21
|
+
Multiplication and elimination both use "Newton-John" tables. These
|
|
22
|
+
tables are simply all possible multiples of a given row in a matrix
|
|
23
|
+
such that a scale+add operation is reduced to a table lookup +
|
|
24
|
+
add. On top of Newton-John multiplication M4RIE implements
|
|
25
|
+
asymptotically fast Strassen-Winograd multiplication. Elimination
|
|
26
|
+
uses simple Gaussian elimination which requires `O(n^3)` additions
|
|
27
|
+
but only `O(n^2 * 2^e)` multiplications.
|
|
28
|
+
|
|
29
|
+
2) ``mzd_slice_t``
|
|
30
|
+
|
|
31
|
+
m x n matrices over `\GF{2^e}` are internally represented as slices
|
|
32
|
+
of m x n matrices over `\GF{2}`. This representation allows for very
|
|
33
|
+
fast matrix times matrix products using Karatsuba's polynomial
|
|
34
|
+
multiplication for polynomials over matrices. However, it is not
|
|
35
|
+
feature complete yet and hence not wrapped in Sage for now.
|
|
36
|
+
|
|
37
|
+
See http://m4ri.sagemath.org for more details on the M4RIE library.
|
|
38
|
+
|
|
39
|
+
EXAMPLES::
|
|
40
|
+
|
|
41
|
+
sage: K.<a> = GF(2^8)
|
|
42
|
+
sage: A = random_matrix(K, 3,4)
|
|
43
|
+
sage: E = A.echelon_form()
|
|
44
|
+
sage: A.row_space() == E.row_space()
|
|
45
|
+
True
|
|
46
|
+
sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r)
|
|
47
|
+
True
|
|
48
|
+
|
|
49
|
+
AUTHOR:
|
|
50
|
+
|
|
51
|
+
* Martin Albrecht <martinralbrecht@googlemail.com>
|
|
52
|
+
|
|
53
|
+
TESTS::
|
|
54
|
+
|
|
55
|
+
sage: TestSuite(sage.matrix.matrix_gf2e_dense.Matrix_gf2e_dense).run(verbose=True)
|
|
56
|
+
running ._test_new() . . . pass
|
|
57
|
+
running ._test_pickling() . . . pass
|
|
58
|
+
|
|
59
|
+
Test hashing::
|
|
60
|
+
|
|
61
|
+
sage: K.<a> = GF(2^4)
|
|
62
|
+
sage: A = random_matrix(K, 1000, 1000)
|
|
63
|
+
sage: A.set_immutable()
|
|
64
|
+
sage: {A:1}
|
|
65
|
+
{1000 x 1000 dense matrix over Finite Field in a of size 2^4: 1}
|
|
66
|
+
|
|
67
|
+
.. TODO::
|
|
68
|
+
|
|
69
|
+
Wrap ``mzd_slice_t``.
|
|
70
|
+
|
|
71
|
+
REFERENCES:
|
|
72
|
+
|
|
73
|
+
- [BB2009]_
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
#*****************************************************************************
|
|
77
|
+
# Copyright (C) 2010 Martin Albrecht <martinralbrecht@googlemail.com>
|
|
78
|
+
#
|
|
79
|
+
# This program is free software: you can redistribute it and/or modify
|
|
80
|
+
# it under the terms of the GNU General Public License as published by
|
|
81
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
82
|
+
# (at your option) any later version.
|
|
83
|
+
# https://www.gnu.org/licenses/
|
|
84
|
+
#*****************************************************************************
|
|
85
|
+
|
|
86
|
+
from cysignals.signals cimport sig_on, sig_off
|
|
87
|
+
|
|
88
|
+
cimport sage.matrix.matrix_dense as matrix_dense
|
|
89
|
+
from sage.structure.element cimport Matrix
|
|
90
|
+
from sage.structure.element cimport Element
|
|
91
|
+
from sage.structure.richcmp cimport rich_to_bool
|
|
92
|
+
from sage.rings.finite_rings.element_base cimport Cache_base
|
|
93
|
+
|
|
94
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
|
95
|
+
from sage.misc.randstate cimport randstate, current_randstate
|
|
96
|
+
|
|
97
|
+
from sage.matrix.matrix_mod2_dense cimport Matrix_mod2_dense
|
|
98
|
+
from sage.matrix.args cimport SparseEntry, MatrixArgs_init
|
|
99
|
+
|
|
100
|
+
from sage.libs.m4ri cimport m4ri_word, mzd_copy
|
|
101
|
+
from sage.libs.m4rie cimport *
|
|
102
|
+
from sage.libs.m4rie cimport mzed_t
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# we must keep a copy of the internal finite field representation
|
|
106
|
+
# around to avoid re-creating it over and over again. Furthermore,
|
|
107
|
+
# M4RIE assumes pointer equivalence of identical fields.
|
|
108
|
+
|
|
109
|
+
_m4rie_finite_field_cache = {}
|
|
110
|
+
|
|
111
|
+
cdef class M4RIE_finite_field:
|
|
112
|
+
"""
|
|
113
|
+
A thin wrapper around the M4RIE finite field class such that we
|
|
114
|
+
can put it in a hash table. This class is not meant for public
|
|
115
|
+
consumption.
|
|
116
|
+
"""
|
|
117
|
+
cdef gf2e *ff
|
|
118
|
+
|
|
119
|
+
def __dealloc__(self):
|
|
120
|
+
"""
|
|
121
|
+
EXAMPLES::
|
|
122
|
+
|
|
123
|
+
sage: from sage.matrix.matrix_gf2e_dense import M4RIE_finite_field
|
|
124
|
+
sage: K = M4RIE_finite_field(); K
|
|
125
|
+
<sage.matrix.matrix_gf2e_dense.M4RIE_finite_field object at 0x...>
|
|
126
|
+
sage: del K
|
|
127
|
+
"""
|
|
128
|
+
if self.ff:
|
|
129
|
+
gf2e_free(self.ff)
|
|
130
|
+
|
|
131
|
+
cdef m4ri_word poly_to_word(f) noexcept:
|
|
132
|
+
return f.to_integer()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense):
|
|
136
|
+
def __cinit__(self, *args, bint alloc=True, **kwds):
|
|
137
|
+
"""
|
|
138
|
+
INPUT:
|
|
139
|
+
|
|
140
|
+
- ``alloc`` -- if ``True`` the matrix is allocated first (default: ``True``)
|
|
141
|
+
|
|
142
|
+
EXAMPLES::
|
|
143
|
+
|
|
144
|
+
sage: K.<a> = GF(2^4)
|
|
145
|
+
sage: A = Matrix(K, 3, 4); A
|
|
146
|
+
[0 0 0 0]
|
|
147
|
+
[0 0 0 0]
|
|
148
|
+
[0 0 0 0]
|
|
149
|
+
|
|
150
|
+
sage: A.randomize()
|
|
151
|
+
sage: TestSuite(A).run()
|
|
152
|
+
|
|
153
|
+
sage: K.<a> = GF(2^3)
|
|
154
|
+
sage: A = Matrix(K,3,4); A
|
|
155
|
+
[0 0 0 0]
|
|
156
|
+
[0 0 0 0]
|
|
157
|
+
[0 0 0 0]
|
|
158
|
+
|
|
159
|
+
sage: A.randomize()
|
|
160
|
+
sage: TestSuite(A).run()
|
|
161
|
+
"""
|
|
162
|
+
cdef M4RIE_finite_field FF
|
|
163
|
+
|
|
164
|
+
R = self._base_ring
|
|
165
|
+
f = R.polynomial()
|
|
166
|
+
|
|
167
|
+
cdef long i
|
|
168
|
+
cdef m4ri_word poly = sum(((<m4ri_word>c) << i) for (i, c) in enumerate(f))
|
|
169
|
+
|
|
170
|
+
if alloc and self._nrows and self._ncols:
|
|
171
|
+
if poly in _m4rie_finite_field_cache:
|
|
172
|
+
self._entries = mzed_init((<M4RIE_finite_field>_m4rie_finite_field_cache[poly]).ff, self._nrows, self._ncols)
|
|
173
|
+
else:
|
|
174
|
+
FF = M4RIE_finite_field.__new__(M4RIE_finite_field)
|
|
175
|
+
FF.ff = gf2e_init(poly)
|
|
176
|
+
self._entries = mzed_init(FF.ff, self._nrows, self._ncols)
|
|
177
|
+
_m4rie_finite_field_cache[poly] = FF
|
|
178
|
+
|
|
179
|
+
# cache elements
|
|
180
|
+
self._zero = self._base_ring(0)
|
|
181
|
+
self._zero_word = poly_to_word(self._zero)
|
|
182
|
+
self._one = self._base_ring(1)
|
|
183
|
+
|
|
184
|
+
def __dealloc__(self):
|
|
185
|
+
"""
|
|
186
|
+
TESTS::
|
|
187
|
+
|
|
188
|
+
sage: K.<a> = GF(2^4)
|
|
189
|
+
sage: A = Matrix(K, 1000, 1000)
|
|
190
|
+
sage: del A
|
|
191
|
+
sage: A = Matrix(K, 1000, 1000)
|
|
192
|
+
sage: del A
|
|
193
|
+
"""
|
|
194
|
+
if self._entries:
|
|
195
|
+
mzed_free(self._entries)
|
|
196
|
+
self._entries = NULL
|
|
197
|
+
|
|
198
|
+
def __init__(self, parent, entries=None, copy=None, bint coerce=True):
|
|
199
|
+
r"""
|
|
200
|
+
Create new matrix over `GF(2^e)` for `2 \leq e \leq 16`.
|
|
201
|
+
|
|
202
|
+
INPUT:
|
|
203
|
+
|
|
204
|
+
- ``parent`` -- a matrix space over ``GF(2^e)``
|
|
205
|
+
|
|
206
|
+
- ``entries`` -- see :func:`matrix`
|
|
207
|
+
|
|
208
|
+
- ``copy`` -- ignored (for backwards compatibility)
|
|
209
|
+
|
|
210
|
+
- ``coerce`` -- if ``False``, assume without checking that the
|
|
211
|
+
entries lie in the base ring
|
|
212
|
+
|
|
213
|
+
EXAMPLES::
|
|
214
|
+
|
|
215
|
+
sage: K.<a> = GF(2^4)
|
|
216
|
+
sage: l = [K.random_element() for _ in range(3*4)]
|
|
217
|
+
|
|
218
|
+
sage: A = Matrix(K, 3, 4, l)
|
|
219
|
+
sage: l == A.list()
|
|
220
|
+
True
|
|
221
|
+
|
|
222
|
+
sage: l[0] == A[0,0]
|
|
223
|
+
True
|
|
224
|
+
|
|
225
|
+
sage: A = Matrix(K, 3, 3, a); A
|
|
226
|
+
[a 0 0]
|
|
227
|
+
[0 a 0]
|
|
228
|
+
[0 0 a]
|
|
229
|
+
"""
|
|
230
|
+
ma = MatrixArgs_init(parent, entries)
|
|
231
|
+
for t in ma.iter(coerce, True):
|
|
232
|
+
se = <SparseEntry>t
|
|
233
|
+
mzed_write_elem(self._entries, se.i, se.j, poly_to_word(se.entry))
|
|
234
|
+
|
|
235
|
+
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
|
|
236
|
+
"""
|
|
237
|
+
A[i,j] = value without bound checks.
|
|
238
|
+
|
|
239
|
+
INPUT:
|
|
240
|
+
|
|
241
|
+
- ``i`` -- row index
|
|
242
|
+
- ``j`` -- column index
|
|
243
|
+
- ``value`` -- a finite field element (not checked but assumed)
|
|
244
|
+
|
|
245
|
+
EXAMPLES::
|
|
246
|
+
|
|
247
|
+
sage: K.<a> = GF(2^4)
|
|
248
|
+
sage: l = [K.random_element() for _ in range(3*4)]
|
|
249
|
+
sage: A = Matrix(K, 3, 4, l)
|
|
250
|
+
|
|
251
|
+
sage: i = randrange(3)
|
|
252
|
+
sage: j = randrange(4)
|
|
253
|
+
sage: A[i,j] = a # indirect doctest
|
|
254
|
+
sage: A[i,j] == a == A.list()[j + 4*i]
|
|
255
|
+
True
|
|
256
|
+
sage: A.list()[:j + 4*i] == l[:j + 4*i]
|
|
257
|
+
True
|
|
258
|
+
sage: A.list()[j + 4*i + 1:] == l[j + 4*i + 1:]
|
|
259
|
+
True
|
|
260
|
+
"""
|
|
261
|
+
mzed_write_elem(self._entries, i, j, poly_to_word(value))
|
|
262
|
+
|
|
263
|
+
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
|
|
264
|
+
"""
|
|
265
|
+
Get A[i,j] without bound checks.
|
|
266
|
+
|
|
267
|
+
INPUT:
|
|
268
|
+
|
|
269
|
+
- ``i`` -- row index
|
|
270
|
+
- ``j`` -- column index
|
|
271
|
+
|
|
272
|
+
EXAMPLES::
|
|
273
|
+
|
|
274
|
+
sage: K.<a> = GF(2^4)
|
|
275
|
+
sage: l = [K.random_element() for _ in range(3*4)]
|
|
276
|
+
sage: A = Matrix(K, 3, 4, l)
|
|
277
|
+
sage: A[2,3] == l[3 + 4*2] # indirect doctest
|
|
278
|
+
True
|
|
279
|
+
sage: K.<a> = GF(2^3)
|
|
280
|
+
sage: l = [K.random_element() for _ in range(3*4)]
|
|
281
|
+
sage: A = Matrix(K, 3, 4, l)
|
|
282
|
+
sage: A.list() == l
|
|
283
|
+
True
|
|
284
|
+
"""
|
|
285
|
+
cdef int r = mzed_read_elem(self._entries, i, j)
|
|
286
|
+
cdef Cache_base cache = <Cache_base> self._base_ring._cache
|
|
287
|
+
return cache.fetch_int(r)
|
|
288
|
+
|
|
289
|
+
cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) except -1:
|
|
290
|
+
r"""
|
|
291
|
+
Return 1 if the entry ``(i, j)`` is zero, otherwise 0.
|
|
292
|
+
|
|
293
|
+
EXAMPLES::
|
|
294
|
+
|
|
295
|
+
sage: K.<a> = GF(2^4)
|
|
296
|
+
sage: A = Matrix(K, 2, 2, a)
|
|
297
|
+
sage: A.zero_pattern_matrix() # indirect doctest
|
|
298
|
+
[0 1]
|
|
299
|
+
[1 0]
|
|
300
|
+
"""
|
|
301
|
+
return mzed_read_elem(self._entries, i, j) == self._zero_word
|
|
302
|
+
|
|
303
|
+
cpdef _add_(self, right):
|
|
304
|
+
r"""
|
|
305
|
+
Return ``A+B``.
|
|
306
|
+
|
|
307
|
+
INPUT:
|
|
308
|
+
|
|
309
|
+
- ``right`` -- a matrix
|
|
310
|
+
|
|
311
|
+
EXAMPLES::
|
|
312
|
+
|
|
313
|
+
sage: K.<a> = GF(2^4)
|
|
314
|
+
sage: A = random_matrix(K,3,4)
|
|
315
|
+
sage: B = random_matrix(K,3,4)
|
|
316
|
+
|
|
317
|
+
sage: C = A + B # indirect doctest
|
|
318
|
+
sage: all(C.list()[i] == A.list()[i] + B.list()[i] for i in range(12))
|
|
319
|
+
True
|
|
320
|
+
"""
|
|
321
|
+
cdef Matrix_gf2e_dense A
|
|
322
|
+
A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0, alloc=False)
|
|
323
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
324
|
+
return A
|
|
325
|
+
A._entries = mzed_add(NULL, self._entries, (<Matrix_gf2e_dense>right)._entries)
|
|
326
|
+
|
|
327
|
+
return A
|
|
328
|
+
|
|
329
|
+
cpdef _sub_(self, right):
|
|
330
|
+
"""
|
|
331
|
+
EXAMPLES::
|
|
332
|
+
|
|
333
|
+
sage: from sage.matrix.matrix_gf2e_dense import Matrix_gf2e_dense
|
|
334
|
+
sage: K.<a> = GF(2^4)
|
|
335
|
+
sage: A = random_matrix(K,3,4)
|
|
336
|
+
sage: B = random_matrix(K,3,4)
|
|
337
|
+
sage: C = A - B # indirect doctest
|
|
338
|
+
sage: all(C.list()[i] == A.list()[i] - B.list()[i] for i in range(12))
|
|
339
|
+
True
|
|
340
|
+
"""
|
|
341
|
+
return self._add_(right)
|
|
342
|
+
|
|
343
|
+
def _multiply_classical(self, Matrix right):
|
|
344
|
+
"""
|
|
345
|
+
Classical cubic matrix multiplication.
|
|
346
|
+
|
|
347
|
+
EXAMPLES::
|
|
348
|
+
|
|
349
|
+
sage: K.<a> = GF(2^2)
|
|
350
|
+
sage: A = random_matrix(K, 50, 50)
|
|
351
|
+
sage: B = random_matrix(K, 50, 50)
|
|
352
|
+
sage: A*B == A._multiply_classical(B)
|
|
353
|
+
True
|
|
354
|
+
|
|
355
|
+
sage: K.<a> = GF(2^4)
|
|
356
|
+
sage: A = random_matrix(K, 50, 50)
|
|
357
|
+
sage: B = random_matrix(K, 50, 50)
|
|
358
|
+
sage: A*B == A._multiply_classical(B)
|
|
359
|
+
True
|
|
360
|
+
|
|
361
|
+
sage: K.<a> = GF(2^8)
|
|
362
|
+
sage: A = random_matrix(K, 50, 50)
|
|
363
|
+
sage: B = random_matrix(K, 50, 50)
|
|
364
|
+
sage: A*B == A._multiply_classical(B)
|
|
365
|
+
True
|
|
366
|
+
|
|
367
|
+
sage: K.<a> = GF(2^10)
|
|
368
|
+
sage: A = random_matrix(K, 50, 50)
|
|
369
|
+
sage: B = random_matrix(K, 50, 50)
|
|
370
|
+
sage: A*B == A._multiply_classical(B)
|
|
371
|
+
True
|
|
372
|
+
|
|
373
|
+
.. NOTE::
|
|
374
|
+
|
|
375
|
+
This function is very slow. Use ``*`` operator instead.
|
|
376
|
+
"""
|
|
377
|
+
if self._ncols != right._nrows:
|
|
378
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
379
|
+
|
|
380
|
+
cdef Matrix_gf2e_dense ans
|
|
381
|
+
|
|
382
|
+
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
|
|
383
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
384
|
+
return ans
|
|
385
|
+
sig_on()
|
|
386
|
+
ans._entries = mzed_mul_naive(ans._entries, self._entries, (<Matrix_gf2e_dense>right)._entries)
|
|
387
|
+
sig_off()
|
|
388
|
+
return ans
|
|
389
|
+
|
|
390
|
+
cdef _matrix_times_matrix_(self, Matrix right):
|
|
391
|
+
r"""
|
|
392
|
+
Return ``A*B``.
|
|
393
|
+
|
|
394
|
+
Uses the M4RIE machinery to decide which function to call.
|
|
395
|
+
|
|
396
|
+
INPUT:
|
|
397
|
+
|
|
398
|
+
- ``right`` -- a matrix
|
|
399
|
+
|
|
400
|
+
EXAMPLES::
|
|
401
|
+
|
|
402
|
+
sage: K.<a> = GF(2^3)
|
|
403
|
+
sage: A = random_matrix(K, 51, 50)
|
|
404
|
+
sage: B = random_matrix(K, 50, 52)
|
|
405
|
+
sage: A*B == A._multiply_newton_john(B) # indirect doctest
|
|
406
|
+
True
|
|
407
|
+
|
|
408
|
+
sage: K.<a> = GF(2^5)
|
|
409
|
+
sage: A = random_matrix(K, 10, 50)
|
|
410
|
+
sage: B = random_matrix(K, 50, 12)
|
|
411
|
+
sage: A*B == A._multiply_newton_john(B)
|
|
412
|
+
True
|
|
413
|
+
|
|
414
|
+
sage: K.<a> = GF(2^7)
|
|
415
|
+
sage: A = random_matrix(K,100, 50)
|
|
416
|
+
sage: B = random_matrix(K, 50, 17)
|
|
417
|
+
sage: A*B == A._multiply_classical(B)
|
|
418
|
+
True
|
|
419
|
+
"""
|
|
420
|
+
if self._ncols != right._nrows:
|
|
421
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
422
|
+
|
|
423
|
+
cdef Matrix_gf2e_dense ans
|
|
424
|
+
|
|
425
|
+
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
|
|
426
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
427
|
+
return ans
|
|
428
|
+
sig_on()
|
|
429
|
+
ans._entries = mzed_mul(ans._entries, self._entries, (<Matrix_gf2e_dense>right)._entries)
|
|
430
|
+
sig_off()
|
|
431
|
+
return ans
|
|
432
|
+
|
|
433
|
+
cpdef Matrix_gf2e_dense _multiply_newton_john(Matrix_gf2e_dense self, Matrix_gf2e_dense right):
|
|
434
|
+
"""
|
|
435
|
+
Return A*B using Newton-John tables.
|
|
436
|
+
|
|
437
|
+
We can write classical cubic multiplication (``C=A*B``) as::
|
|
438
|
+
|
|
439
|
+
for i in range(A.ncols()):
|
|
440
|
+
for j in range(A.nrows()):
|
|
441
|
+
C[j] += A[j,i] * B[j]
|
|
442
|
+
|
|
443
|
+
Hence, in the inner-most loop we compute multiples of ``B[j]``
|
|
444
|
+
by the values ``A[j,i]``. If the matrix ``A`` is big and the
|
|
445
|
+
finite field is small, there is a very high chance that
|
|
446
|
+
``e * B[j]`` is computed more than once for any ``e`` in the finite
|
|
447
|
+
field. Instead, we compute all possible
|
|
448
|
+
multiples of ``B[j]`` and reuse this data in the inner loop.
|
|
449
|
+
This is what is called a "Newton-John" table in M4RIE.
|
|
450
|
+
|
|
451
|
+
INPUT:
|
|
452
|
+
|
|
453
|
+
- ``right`` -- a matrix
|
|
454
|
+
|
|
455
|
+
EXAMPLES::
|
|
456
|
+
|
|
457
|
+
sage: K.<a> = GF(2^2)
|
|
458
|
+
sage: A = random_matrix(K, 50, 50)
|
|
459
|
+
sage: B = random_matrix(K, 50, 50)
|
|
460
|
+
sage: A._multiply_newton_john(B) == A._multiply_classical(B) # indirect doctest
|
|
461
|
+
True
|
|
462
|
+
|
|
463
|
+
sage: K.<a> = GF(2^4)
|
|
464
|
+
sage: A = random_matrix(K, 50, 50)
|
|
465
|
+
sage: B = random_matrix(K, 50, 50)
|
|
466
|
+
sage: A._multiply_newton_john(B) == A._multiply_classical(B)
|
|
467
|
+
True
|
|
468
|
+
|
|
469
|
+
sage: K.<a> = GF(2^8)
|
|
470
|
+
sage: A = random_matrix(K, 50, 50)
|
|
471
|
+
sage: B = random_matrix(K, 50, 50)
|
|
472
|
+
sage: A._multiply_newton_john(B) == A._multiply_classical(B)
|
|
473
|
+
True
|
|
474
|
+
|
|
475
|
+
sage: K.<a> = GF(2^10)
|
|
476
|
+
sage: A = random_matrix(K, 50, 50)
|
|
477
|
+
sage: B = random_matrix(K, 50, 50)
|
|
478
|
+
sage: A._multiply_newton_john(B) == A._multiply_classical(B)
|
|
479
|
+
True
|
|
480
|
+
"""
|
|
481
|
+
if self._ncols != right._nrows:
|
|
482
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
483
|
+
|
|
484
|
+
cdef Matrix_gf2e_dense ans
|
|
485
|
+
|
|
486
|
+
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
|
|
487
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
488
|
+
return ans
|
|
489
|
+
|
|
490
|
+
sig_on()
|
|
491
|
+
ans._entries = mzed_mul_newton_john(ans._entries, self._entries, (<Matrix_gf2e_dense>right)._entries)
|
|
492
|
+
sig_off()
|
|
493
|
+
return ans
|
|
494
|
+
|
|
495
|
+
cpdef Matrix_gf2e_dense _multiply_karatsuba(Matrix_gf2e_dense self, Matrix_gf2e_dense right):
|
|
496
|
+
r"""
|
|
497
|
+
Matrix multiplication using Karatsuba over polynomials with
|
|
498
|
+
matrix coefficients over GF(2).
|
|
499
|
+
|
|
500
|
+
The idea behind Karatsuba multiplication for matrices over
|
|
501
|
+
`\GF{p^n}` is to treat these matrices as polynomials with
|
|
502
|
+
coefficients of matrices over `\GF{p}`. Then, Karatsuba-style
|
|
503
|
+
formulas can be used to perform multiplication, cf. [BB2009]_.
|
|
504
|
+
|
|
505
|
+
INPUT:
|
|
506
|
+
|
|
507
|
+
- ``right`` -- a matrix
|
|
508
|
+
|
|
509
|
+
EXAMPLES::
|
|
510
|
+
|
|
511
|
+
sage: K.<a> = GF(2^2)
|
|
512
|
+
sage: A = random_matrix(K, 50, 50)
|
|
513
|
+
sage: B = random_matrix(K, 50, 50)
|
|
514
|
+
sage: A._multiply_karatsuba(B) == A._multiply_classical(B) # indirect doctest
|
|
515
|
+
True
|
|
516
|
+
|
|
517
|
+
sage: K.<a> = GF(2^2)
|
|
518
|
+
sage: A = random_matrix(K, 137, 11)
|
|
519
|
+
sage: B = random_matrix(K, 11, 23)
|
|
520
|
+
sage: A._multiply_karatsuba(B) == A._multiply_classical(B)
|
|
521
|
+
True
|
|
522
|
+
|
|
523
|
+
sage: K.<a> = GF(2^10)
|
|
524
|
+
sage: A = random_matrix(K, 50, 50)
|
|
525
|
+
sage: B = random_matrix(K, 50, 50)
|
|
526
|
+
sage: A._multiply_karatsuba(B) == A._multiply_classical(B)
|
|
527
|
+
True
|
|
528
|
+
"""
|
|
529
|
+
if self._ncols != right._nrows:
|
|
530
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
531
|
+
|
|
532
|
+
cdef Matrix_gf2e_dense ans
|
|
533
|
+
|
|
534
|
+
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
|
|
535
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
536
|
+
return ans
|
|
537
|
+
|
|
538
|
+
sig_on()
|
|
539
|
+
ans._entries = mzed_mul_karatsuba(ans._entries, self._entries, (<Matrix_gf2e_dense>right)._entries)
|
|
540
|
+
sig_off()
|
|
541
|
+
return ans
|
|
542
|
+
|
|
543
|
+
cpdef Matrix_gf2e_dense _multiply_strassen(Matrix_gf2e_dense self, Matrix_gf2e_dense right, cutoff=0):
|
|
544
|
+
"""
|
|
545
|
+
Winograd-Strassen matrix multiplication with Newton-John
|
|
546
|
+
multiplication as base case.
|
|
547
|
+
|
|
548
|
+
INPUT:
|
|
549
|
+
|
|
550
|
+
- ``right`` -- a matrix
|
|
551
|
+
- ``cutoff`` -- row or column dimension to switch over to
|
|
552
|
+
Newton-John multiplication (default: 64)
|
|
553
|
+
|
|
554
|
+
EXAMPLES::
|
|
555
|
+
|
|
556
|
+
sage: K.<a> = GF(2^2)
|
|
557
|
+
sage: A = random_matrix(K, 50, 50)
|
|
558
|
+
sage: B = random_matrix(K, 50, 50)
|
|
559
|
+
sage: A._multiply_strassen(B) == A._multiply_classical(B) # indirect doctest
|
|
560
|
+
True
|
|
561
|
+
|
|
562
|
+
sage: K.<a> = GF(2^4)
|
|
563
|
+
sage: A = random_matrix(K, 50, 50)
|
|
564
|
+
sage: B = random_matrix(K, 50, 50)
|
|
565
|
+
sage: A._multiply_strassen(B) == A._multiply_classical(B)
|
|
566
|
+
True
|
|
567
|
+
|
|
568
|
+
sage: K.<a> = GF(2^8)
|
|
569
|
+
sage: A = random_matrix(K, 50, 50)
|
|
570
|
+
sage: B = random_matrix(K, 50, 50)
|
|
571
|
+
sage: A._multiply_strassen(B) == A._multiply_classical(B)
|
|
572
|
+
True
|
|
573
|
+
|
|
574
|
+
sage: K.<a> = GF(2^10)
|
|
575
|
+
sage: A = random_matrix(K, 50, 50)
|
|
576
|
+
sage: B = random_matrix(K, 50, 50)
|
|
577
|
+
sage: A._multiply_strassen(B) == A._multiply_classical(B)
|
|
578
|
+
True
|
|
579
|
+
"""
|
|
580
|
+
if self._ncols != right._nrows:
|
|
581
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
582
|
+
|
|
583
|
+
cdef Matrix_gf2e_dense ans
|
|
584
|
+
|
|
585
|
+
ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())
|
|
586
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
587
|
+
return ans
|
|
588
|
+
|
|
589
|
+
if cutoff == 0:
|
|
590
|
+
cutoff = _mzed_strassen_cutoff(ans._entries, self._entries, (<Matrix_gf2e_dense>right)._entries)
|
|
591
|
+
|
|
592
|
+
sig_on()
|
|
593
|
+
ans._entries = mzed_mul_strassen(ans._entries, self._entries, (<Matrix_gf2e_dense>right)._entries, cutoff)
|
|
594
|
+
sig_off()
|
|
595
|
+
return ans
|
|
596
|
+
|
|
597
|
+
cpdef _lmul_(self, Element right):
|
|
598
|
+
"""
|
|
599
|
+
Return ``a*B`` for ``a`` an element of the base field.
|
|
600
|
+
|
|
601
|
+
INPUT:
|
|
602
|
+
|
|
603
|
+
- ``right`` -- an element of the base field
|
|
604
|
+
|
|
605
|
+
EXAMPLES::
|
|
606
|
+
|
|
607
|
+
sage: K.<a> = GF(4)
|
|
608
|
+
sage: A = random_matrix(K,10,10)
|
|
609
|
+
sage: B = a*A # indirect doctest
|
|
610
|
+
sage: all(B.list()[i] == a*A.list()[i] for i in range(100))
|
|
611
|
+
True
|
|
612
|
+
"""
|
|
613
|
+
cdef m4ri_word a = poly_to_word(right)
|
|
614
|
+
cdef Matrix_gf2e_dense C = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0)
|
|
615
|
+
mzed_mul_scalar(C._entries, a, self._entries)
|
|
616
|
+
return C
|
|
617
|
+
|
|
618
|
+
def __neg__(self):
|
|
619
|
+
"""
|
|
620
|
+
EXAMPLES::
|
|
621
|
+
|
|
622
|
+
sage: K.<a> = GF(2^4)
|
|
623
|
+
sage: A = random_matrix(K, 3, 4)
|
|
624
|
+
sage: B = -A
|
|
625
|
+
sage: all(B.list()[i] == -A.list()[i] for i in range(12))
|
|
626
|
+
True
|
|
627
|
+
"""
|
|
628
|
+
return self.__copy__()
|
|
629
|
+
|
|
630
|
+
cpdef _richcmp_(self, right, int op):
|
|
631
|
+
"""
|
|
632
|
+
EXAMPLES::
|
|
633
|
+
|
|
634
|
+
sage: K.<a> = GF(2^4)
|
|
635
|
+
sage: A = random_matrix(K,3,4)
|
|
636
|
+
sage: A[0,0] = 0
|
|
637
|
+
sage: B = copy(A)
|
|
638
|
+
sage: A == B
|
|
639
|
+
True
|
|
640
|
+
sage: A[0,0] = a
|
|
641
|
+
sage: A == B
|
|
642
|
+
False
|
|
643
|
+
"""
|
|
644
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
645
|
+
return rich_to_bool(op, 0)
|
|
646
|
+
return rich_to_bool(op, mzed_cmp(self._entries,
|
|
647
|
+
(<Matrix_gf2e_dense>right)._entries))
|
|
648
|
+
|
|
649
|
+
def __copy__(self):
|
|
650
|
+
"""
|
|
651
|
+
EXAMPLES::
|
|
652
|
+
|
|
653
|
+
sage: K.<a> = GF(2^4)
|
|
654
|
+
sage: m,n = 3, 4
|
|
655
|
+
sage: A = random_matrix(K,3,4)
|
|
656
|
+
sage: A2 = copy(A)
|
|
657
|
+
sage: A2.list() == A.list()
|
|
658
|
+
True
|
|
659
|
+
|
|
660
|
+
sage: A[0,0] = 1 if A[0,0] != 1 else 0
|
|
661
|
+
sage: A2[0,0] == A[0,0]
|
|
662
|
+
False
|
|
663
|
+
"""
|
|
664
|
+
cdef Matrix_gf2e_dense A
|
|
665
|
+
A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0)
|
|
666
|
+
|
|
667
|
+
if self._nrows and self._ncols:
|
|
668
|
+
mzed_copy(A._entries, <const_mzed_t *>self._entries)
|
|
669
|
+
|
|
670
|
+
return A
|
|
671
|
+
|
|
672
|
+
def __bool__(self):
|
|
673
|
+
"""
|
|
674
|
+
Return if ``self`` is a zero matrix or not.
|
|
675
|
+
|
|
676
|
+
EXAMPLES::
|
|
677
|
+
|
|
678
|
+
sage: K.<a> = GF(2^4)
|
|
679
|
+
sage: A = Matrix(K, 2, 2, a)
|
|
680
|
+
sage: bool(A)
|
|
681
|
+
True
|
|
682
|
+
sage: zero = MatrixSpace(K, 3, 3).zero()
|
|
683
|
+
sage: bool(zero)
|
|
684
|
+
False
|
|
685
|
+
"""
|
|
686
|
+
if self._nrows and self._ncols:
|
|
687
|
+
return not mzed_is_zero(self._entries)
|
|
688
|
+
return False
|
|
689
|
+
|
|
690
|
+
def _list(self):
|
|
691
|
+
"""
|
|
692
|
+
EXAMPLES::
|
|
693
|
+
|
|
694
|
+
sage: K.<a> = GF(2^4)
|
|
695
|
+
sage: l = [K.random_element() for _ in range(3*4)]
|
|
696
|
+
sage: A = Matrix(K, 3, 4, l)
|
|
697
|
+
sage: A.list() == l # indirect doctest
|
|
698
|
+
True
|
|
699
|
+
"""
|
|
700
|
+
cdef Py_ssize_t i,j
|
|
701
|
+
l = []
|
|
702
|
+
for i from 0 <= i < self._nrows:
|
|
703
|
+
for j from 0 <= j < self._ncols:
|
|
704
|
+
l.append(self.get_unsafe(i,j))
|
|
705
|
+
return l
|
|
706
|
+
|
|
707
|
+
def randomize(self, density=1, nonzero=False, *args, **kwds):
|
|
708
|
+
"""
|
|
709
|
+
Randomize ``density`` proportion of the entries of this matrix,
|
|
710
|
+
leaving the rest unchanged.
|
|
711
|
+
|
|
712
|
+
INPUT:
|
|
713
|
+
|
|
714
|
+
- ``density`` -- float; proportion (roughly) to be considered for
|
|
715
|
+
changes
|
|
716
|
+
- ``nonzero`` -- boolean (default: ``False``); whether the new entries
|
|
717
|
+
are forced to be nonzero
|
|
718
|
+
|
|
719
|
+
OUTPUT: none, the matrix is modified in-place
|
|
720
|
+
|
|
721
|
+
EXAMPLES::
|
|
722
|
+
|
|
723
|
+
sage: K.<a> = GF(2^4)
|
|
724
|
+
sage: total_count = 0
|
|
725
|
+
sage: from collections import defaultdict
|
|
726
|
+
sage: dic = defaultdict(Integer)
|
|
727
|
+
sage: def add_samples():
|
|
728
|
+
....: global dic, total_count
|
|
729
|
+
....: for _ in range(100):
|
|
730
|
+
....: A = Matrix(K,3,3)
|
|
731
|
+
....: A.randomize()
|
|
732
|
+
....: for a in A.list():
|
|
733
|
+
....: dic[a] += 1
|
|
734
|
+
....: total_count += 1.0
|
|
735
|
+
sage: add_samples()
|
|
736
|
+
sage: while not all(abs(dic[a]/total_count - 1/16) < 0.01 for a in dic):
|
|
737
|
+
....: add_samples()
|
|
738
|
+
|
|
739
|
+
sage: def add_sample(density):
|
|
740
|
+
....: global density_sum, total_count
|
|
741
|
+
....: total_count += 1.0
|
|
742
|
+
....: density_sum += random_matrix(K, 1000, 1000, density=density).density()
|
|
743
|
+
|
|
744
|
+
sage: density_sum = 0.0
|
|
745
|
+
sage: total_count = 0.0
|
|
746
|
+
sage: add_sample(0.1)
|
|
747
|
+
sage: while abs(density_sum/total_count - 0.1) > 0.001:
|
|
748
|
+
....: add_sample(0.1)
|
|
749
|
+
|
|
750
|
+
sage: density_sum = 0.0
|
|
751
|
+
sage: total_count = 0.0
|
|
752
|
+
sage: add_sample(1.0)
|
|
753
|
+
sage: while abs(density_sum/total_count - 1.0) > 0.001:
|
|
754
|
+
....: add_sample(1.0)
|
|
755
|
+
|
|
756
|
+
sage: density_sum = 0.0
|
|
757
|
+
sage: total_count = 0.0
|
|
758
|
+
sage: add_sample(0.5)
|
|
759
|
+
sage: while abs(density_sum/total_count - 0.5) > 0.001:
|
|
760
|
+
....: add_sample(0.5)
|
|
761
|
+
|
|
762
|
+
Note, that the matrix is updated and not zero-ed out before
|
|
763
|
+
being randomized::
|
|
764
|
+
|
|
765
|
+
sage: def add_sample(density, nonzero):
|
|
766
|
+
....: global density_sum, total_count
|
|
767
|
+
....: total_count += 1.0
|
|
768
|
+
....: A = matrix(K, 1000, 1000)
|
|
769
|
+
....: A.randomize(nonzero=nonzero, density=density)
|
|
770
|
+
....: A.randomize(nonzero=nonzero, density=density)
|
|
771
|
+
....: density_sum += A.density()
|
|
772
|
+
|
|
773
|
+
sage: density_sum = 0.0
|
|
774
|
+
sage: total_count = 0.0
|
|
775
|
+
sage: add_sample(0.1, True)
|
|
776
|
+
sage: while abs(density_sum/total_count - (1 - 0.9^2)) > 0.001:
|
|
777
|
+
....: add_sample(0.1, True)
|
|
778
|
+
|
|
779
|
+
sage: density_sum = 0.0
|
|
780
|
+
sage: total_count = 0.0
|
|
781
|
+
sage: add_sample(0.1, False)
|
|
782
|
+
sage: while abs(density_sum/total_count - (1 - 0.9^2)*15/16) > 0.001:
|
|
783
|
+
....: add_sample(0.1, False)
|
|
784
|
+
|
|
785
|
+
sage: density_sum = 0.0
|
|
786
|
+
sage: total_count = 0.0
|
|
787
|
+
sage: add_sample(0.05, True)
|
|
788
|
+
sage: while abs(density_sum/total_count - (1 - 0.95^2)) > 0.001:
|
|
789
|
+
....: add_sample(0.05, True)
|
|
790
|
+
|
|
791
|
+
sage: density_sum = 0.0
|
|
792
|
+
sage: total_count = 0.0
|
|
793
|
+
sage: add_sample(0.5, True)
|
|
794
|
+
sage: while abs(density_sum/total_count - (1 - 0.5^2)) > 0.001:
|
|
795
|
+
....: add_sample(0.5, True)
|
|
796
|
+
"""
|
|
797
|
+
if self._ncols == 0 or self._nrows == 0:
|
|
798
|
+
return
|
|
799
|
+
|
|
800
|
+
cdef Py_ssize_t i,j
|
|
801
|
+
self.check_mutability()
|
|
802
|
+
self.clear_cache()
|
|
803
|
+
|
|
804
|
+
cdef m4ri_word mask = (1<<(self._parent.base_ring().degree())) - 1
|
|
805
|
+
|
|
806
|
+
cdef randstate rstate = current_randstate()
|
|
807
|
+
|
|
808
|
+
if self._ncols == 0 or self._nrows == 0:
|
|
809
|
+
return
|
|
810
|
+
|
|
811
|
+
cdef float _density = density
|
|
812
|
+
if _density <= 0:
|
|
813
|
+
return
|
|
814
|
+
if _density > 1:
|
|
815
|
+
_density = 1.0
|
|
816
|
+
|
|
817
|
+
if _density == 1:
|
|
818
|
+
if not nonzero:
|
|
819
|
+
sig_on()
|
|
820
|
+
for i in range(self._nrows):
|
|
821
|
+
for j in range(self._ncols):
|
|
822
|
+
tmp = rstate.c_random() & mask
|
|
823
|
+
mzed_write_elem(self._entries, i, j, tmp)
|
|
824
|
+
sig_off()
|
|
825
|
+
else:
|
|
826
|
+
sig_on()
|
|
827
|
+
for i in range(self._nrows):
|
|
828
|
+
for j in range(self._ncols):
|
|
829
|
+
tmp = rstate.c_random() & mask
|
|
830
|
+
while tmp == 0:
|
|
831
|
+
tmp = rstate.c_random() & mask
|
|
832
|
+
mzed_write_elem(self._entries, i, j, tmp)
|
|
833
|
+
sig_off()
|
|
834
|
+
else:
|
|
835
|
+
if not nonzero:
|
|
836
|
+
sig_on()
|
|
837
|
+
for i in range(self._nrows):
|
|
838
|
+
for j in range(self._ncols):
|
|
839
|
+
if rstate.c_rand_double() <= _density:
|
|
840
|
+
tmp = rstate.c_random() & mask
|
|
841
|
+
mzed_write_elem(self._entries, i, j, tmp)
|
|
842
|
+
sig_off()
|
|
843
|
+
else:
|
|
844
|
+
sig_on()
|
|
845
|
+
for i in range(self._nrows):
|
|
846
|
+
for j in range(self._ncols):
|
|
847
|
+
if rstate.c_rand_double() <= _density:
|
|
848
|
+
tmp = rstate.c_random() & mask
|
|
849
|
+
while tmp == 0:
|
|
850
|
+
tmp = rstate.c_random() & mask
|
|
851
|
+
mzed_write_elem(self._entries, i, j, tmp)
|
|
852
|
+
sig_off()
|
|
853
|
+
|
|
854
|
+
def echelonize(self, algorithm='heuristic', reduced=True, **kwds):
|
|
855
|
+
"""
|
|
856
|
+
Compute the row echelon form of ``self`` in place.
|
|
857
|
+
|
|
858
|
+
INPUT:
|
|
859
|
+
|
|
860
|
+
- ``algorithm`` -- one of the following
|
|
861
|
+
- ``heuristic`` -- let M4RIE decide (default)
|
|
862
|
+
- ``newton_john`` -- use newton_john table based algorithm
|
|
863
|
+
- ``ple`` -- use PLE decomposition
|
|
864
|
+
- ``naive`` -- use naive cubic Gaussian elimination (M4RIE implementation)
|
|
865
|
+
- ``builtin`` -- use naive cubic Gaussian elimination (Sage implementation)
|
|
866
|
+
- ``reduced`` -- if ``True`` return reduced echelon form. No
|
|
867
|
+
guarantee is given that the matrix is *not* reduced if
|
|
868
|
+
``False`` (default: ``True``)
|
|
869
|
+
|
|
870
|
+
EXAMPLES::
|
|
871
|
+
|
|
872
|
+
sage: K.<a> = GF(2^4)
|
|
873
|
+
sage: m,n = 3, 5
|
|
874
|
+
sage: A = random_matrix(K, 3, 5)
|
|
875
|
+
sage: R = A.row_space()
|
|
876
|
+
sage: A.echelonize()
|
|
877
|
+
sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r)
|
|
878
|
+
True
|
|
879
|
+
sage: A.row_space() == R
|
|
880
|
+
True
|
|
881
|
+
|
|
882
|
+
sage: K.<a> = GF(2^3)
|
|
883
|
+
sage: m,n = 3, 5
|
|
884
|
+
sage: MS = MatrixSpace(K,m,n)
|
|
885
|
+
sage: A = random_matrix(K, 3, 5)
|
|
886
|
+
sage: B = copy(A).echelon_form('newton_john')
|
|
887
|
+
sage: C = copy(A).echelon_form('naive')
|
|
888
|
+
sage: D = copy(A).echelon_form('builtin')
|
|
889
|
+
sage: B == C == D
|
|
890
|
+
True
|
|
891
|
+
sage: all(r[r.nonzero_positions()[0]] == 1 for r in B.rows() if r)
|
|
892
|
+
True
|
|
893
|
+
"""
|
|
894
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
895
|
+
self.cache('in_echelon_form',True)
|
|
896
|
+
self.cache('rank', 0)
|
|
897
|
+
self.cache('pivots', [])
|
|
898
|
+
return self
|
|
899
|
+
|
|
900
|
+
cdef int full
|
|
901
|
+
|
|
902
|
+
full = int(reduced)
|
|
903
|
+
|
|
904
|
+
x = self.fetch('in_echelon_form')
|
|
905
|
+
if x is not None:
|
|
906
|
+
return # already known to be in echelon form
|
|
907
|
+
|
|
908
|
+
self.check_mutability()
|
|
909
|
+
self.clear_cache()
|
|
910
|
+
|
|
911
|
+
if algorithm == 'naive':
|
|
912
|
+
sig_on()
|
|
913
|
+
r = mzed_echelonize_naive(self._entries, full)
|
|
914
|
+
sig_off()
|
|
915
|
+
|
|
916
|
+
elif algorithm == 'newton_john':
|
|
917
|
+
sig_on()
|
|
918
|
+
r = mzed_echelonize_newton_john(self._entries, full)
|
|
919
|
+
sig_off()
|
|
920
|
+
|
|
921
|
+
elif algorithm == 'ple':
|
|
922
|
+
sig_on()
|
|
923
|
+
r = mzed_echelonize_ple(self._entries, full)
|
|
924
|
+
sig_off()
|
|
925
|
+
|
|
926
|
+
elif algorithm == 'heuristic':
|
|
927
|
+
sig_on()
|
|
928
|
+
r = mzed_echelonize(self._entries, full)
|
|
929
|
+
sig_off()
|
|
930
|
+
|
|
931
|
+
elif algorithm == 'builtin':
|
|
932
|
+
self._echelon_in_place(algorithm='classical')
|
|
933
|
+
|
|
934
|
+
else:
|
|
935
|
+
raise ValueError("No algorithm '%s'." % algorithm)
|
|
936
|
+
|
|
937
|
+
self.cache('in_echelon_form',True)
|
|
938
|
+
self.cache('rank', r)
|
|
939
|
+
self.cache('pivots', self._pivots())
|
|
940
|
+
|
|
941
|
+
def _pivots(self):
|
|
942
|
+
"""
|
|
943
|
+
EXAMPLES::
|
|
944
|
+
|
|
945
|
+
sage: K.<a> = GF(2^8)
|
|
946
|
+
sage: A = random_matrix(K, 15, 15)
|
|
947
|
+
sage: while A.rank() != 15:
|
|
948
|
+
....: A = random_matrix(K, 15, 15)
|
|
949
|
+
sage: A.pivots() # indirect doctest
|
|
950
|
+
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
|
|
951
|
+
"""
|
|
952
|
+
if not self.fetch('in_echelon_form'):
|
|
953
|
+
raise ValueError("self must be in reduced row echelon form first.")
|
|
954
|
+
pivots = []
|
|
955
|
+
cdef Py_ssize_t i, j, nc
|
|
956
|
+
nc = self._ncols
|
|
957
|
+
i = 0
|
|
958
|
+
while i < self._nrows:
|
|
959
|
+
for j from i <= j < nc:
|
|
960
|
+
if not self.get_is_zero_unsafe(i,j):
|
|
961
|
+
pivots.append(j)
|
|
962
|
+
i += 1
|
|
963
|
+
break
|
|
964
|
+
if j == nc:
|
|
965
|
+
break
|
|
966
|
+
return pivots
|
|
967
|
+
|
|
968
|
+
def __invert__(self):
|
|
969
|
+
"""
|
|
970
|
+
EXAMPLES::
|
|
971
|
+
|
|
972
|
+
sage: K.<a> = GF(2^3)
|
|
973
|
+
sage: A = random_matrix(K, 3, 3)
|
|
974
|
+
sage: while A.rank() != 3:
|
|
975
|
+
....: A = random_matrix(K, 3, 3)
|
|
976
|
+
sage: B = ~A
|
|
977
|
+
sage: A*B
|
|
978
|
+
[1 0 0]
|
|
979
|
+
[0 1 0]
|
|
980
|
+
[0 0 1]
|
|
981
|
+
"""
|
|
982
|
+
cdef Matrix_gf2e_dense A
|
|
983
|
+
A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0)
|
|
984
|
+
|
|
985
|
+
if self.rank() != self._nrows:
|
|
986
|
+
raise ZeroDivisionError("Matrix does not have full rank.")
|
|
987
|
+
|
|
988
|
+
if self._nrows:
|
|
989
|
+
sig_on()
|
|
990
|
+
mzed_invert_newton_john(A._entries, self._entries)
|
|
991
|
+
sig_off()
|
|
992
|
+
|
|
993
|
+
return A
|
|
994
|
+
|
|
995
|
+
cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
|
|
996
|
+
"""
|
|
997
|
+
Return ``multiple * self[row][start_col:]``.
|
|
998
|
+
|
|
999
|
+
INPUT:
|
|
1000
|
+
|
|
1001
|
+
- ``row`` -- row index for row to rescale
|
|
1002
|
+
- ``multiple`` -- finite field element to scale by
|
|
1003
|
+
- ``start_col`` -- only start at this column index
|
|
1004
|
+
|
|
1005
|
+
EXAMPLES::
|
|
1006
|
+
|
|
1007
|
+
sage: K.<a> = GF(2^3)
|
|
1008
|
+
sage: A = random_matrix(K,3,3)
|
|
1009
|
+
sage: B = copy(A)
|
|
1010
|
+
sage: B.rescale_row(0, a, 0)
|
|
1011
|
+
sage: B[0] == a*A[0]
|
|
1012
|
+
True
|
|
1013
|
+
sage: B[1:] == A[1:]
|
|
1014
|
+
True
|
|
1015
|
+
|
|
1016
|
+
sage: B = copy(A)
|
|
1017
|
+
sage: B.rescale_row(1, 0, 0)
|
|
1018
|
+
sage: B[0] == A[0]
|
|
1019
|
+
True
|
|
1020
|
+
sage: B[2] == A[2]
|
|
1021
|
+
True
|
|
1022
|
+
sage: B[1].is_zero()
|
|
1023
|
+
True
|
|
1024
|
+
|
|
1025
|
+
sage: B = copy(A)
|
|
1026
|
+
sage: B.rescale_row(2, a^2, 2)
|
|
1027
|
+
sage: B.list()[:-1] == A.list()[:-1]
|
|
1028
|
+
True
|
|
1029
|
+
sage: B[2,2] == a^2*A[2,2]
|
|
1030
|
+
True
|
|
1031
|
+
"""
|
|
1032
|
+
cdef m4ri_word x = poly_to_word(multiple)
|
|
1033
|
+
mzed_rescale_row(self._entries, row, start_col, x)
|
|
1034
|
+
|
|
1035
|
+
cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple, Py_ssize_t start_col):
|
|
1036
|
+
"""
|
|
1037
|
+
Compute ``self[row_to][start_col:] += multiple * self[row_from][start_col:]``.
|
|
1038
|
+
|
|
1039
|
+
INPUT:
|
|
1040
|
+
|
|
1041
|
+
- ``row_to`` -- row index of source
|
|
1042
|
+
- ``row_from`` -- row index of destination
|
|
1043
|
+
- ``multiple`` -- finite field element
|
|
1044
|
+
- ``start_col`` -- only start at this column index
|
|
1045
|
+
|
|
1046
|
+
EXAMPLES::
|
|
1047
|
+
|
|
1048
|
+
sage: K.<a> = GF(2^3)
|
|
1049
|
+
sage: A = random_matrix(K,3,3)
|
|
1050
|
+
sage: B = copy(A)
|
|
1051
|
+
sage: B.add_multiple_of_row(0,1,a,0)
|
|
1052
|
+
sage: B[1:] == A[1:]
|
|
1053
|
+
True
|
|
1054
|
+
sage: B[0] == A[0] + a*A[1]
|
|
1055
|
+
True
|
|
1056
|
+
|
|
1057
|
+
sage: B = copy(A)
|
|
1058
|
+
sage: B.add_multiple_of_row(2,1,a,2)
|
|
1059
|
+
sage: B.list()[:-1] == A.list()[:-1]
|
|
1060
|
+
True
|
|
1061
|
+
sage: B[2,2] == A[2,2] + a*A[1,2]
|
|
1062
|
+
True
|
|
1063
|
+
"""
|
|
1064
|
+
|
|
1065
|
+
cdef m4ri_word x = poly_to_word(multiple)
|
|
1066
|
+
mzed_add_multiple_of_row(self._entries, row_to, self._entries, row_from, x, start_col)
|
|
1067
|
+
|
|
1068
|
+
cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
|
|
1069
|
+
"""
|
|
1070
|
+
Swap rows ``row1`` and ``row2``.
|
|
1071
|
+
|
|
1072
|
+
INPUT:
|
|
1073
|
+
|
|
1074
|
+
- ``row1`` -- row index
|
|
1075
|
+
- ``row2`` -- row index
|
|
1076
|
+
|
|
1077
|
+
EXAMPLES::
|
|
1078
|
+
|
|
1079
|
+
sage: K.<a> = GF(2^3)
|
|
1080
|
+
sage: A = random_matrix(K,3,3)
|
|
1081
|
+
sage: B = copy(A)
|
|
1082
|
+
sage: B.swap_rows(0,1)
|
|
1083
|
+
sage: B[0] == A[1]
|
|
1084
|
+
True
|
|
1085
|
+
sage: B[1] == A[0]
|
|
1086
|
+
True
|
|
1087
|
+
sage: B[2] == A[2]
|
|
1088
|
+
True
|
|
1089
|
+
"""
|
|
1090
|
+
mzed_row_swap(self._entries, row1, row2)
|
|
1091
|
+
|
|
1092
|
+
cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
|
|
1093
|
+
"""
|
|
1094
|
+
Swap columns ``col1`` and ``col2``.
|
|
1095
|
+
|
|
1096
|
+
INPUT:
|
|
1097
|
+
|
|
1098
|
+
- ``col1`` -- column index
|
|
1099
|
+
- ``col2`` -- column index
|
|
1100
|
+
|
|
1101
|
+
EXAMPLES::
|
|
1102
|
+
|
|
1103
|
+
sage: K.<a> = GF(2^3)
|
|
1104
|
+
sage: A = random_matrix(K,3,3)
|
|
1105
|
+
sage: B = copy(A)
|
|
1106
|
+
sage: B.swap_columns(0,1)
|
|
1107
|
+
sage: B.column(0) == A.column(1)
|
|
1108
|
+
True
|
|
1109
|
+
sage: B.column(1) == A.column(0)
|
|
1110
|
+
True
|
|
1111
|
+
sage: B.column(2) == A.column(2)
|
|
1112
|
+
True
|
|
1113
|
+
|
|
1114
|
+
sage: A = random_matrix(K,4,16)
|
|
1115
|
+
sage: B = copy(A)
|
|
1116
|
+
sage: B.swap_columns(0,1)
|
|
1117
|
+
sage: B.swap_columns(0,1)
|
|
1118
|
+
sage: A == B
|
|
1119
|
+
True
|
|
1120
|
+
|
|
1121
|
+
sage: A.swap_columns(0,15)
|
|
1122
|
+
sage: A.column(0) == B.column(15)
|
|
1123
|
+
True
|
|
1124
|
+
sage: A.swap_columns(14,15)
|
|
1125
|
+
sage: A.column(14) == B.column(0)
|
|
1126
|
+
True
|
|
1127
|
+
"""
|
|
1128
|
+
mzed_col_swap(self._entries, col1, col2)
|
|
1129
|
+
|
|
1130
|
+
def augment(self, right):
|
|
1131
|
+
"""
|
|
1132
|
+
Augments ``self`` with ``right``.
|
|
1133
|
+
|
|
1134
|
+
INPUT:
|
|
1135
|
+
|
|
1136
|
+
- ``right`` -- a matrix
|
|
1137
|
+
|
|
1138
|
+
EXAMPLES::
|
|
1139
|
+
|
|
1140
|
+
sage: K.<a> = GF(2^4)
|
|
1141
|
+
sage: MS = MatrixSpace(K,3,3)
|
|
1142
|
+
sage: A = random_matrix(K,3,3)
|
|
1143
|
+
sage: B = A.augment(MS(1))
|
|
1144
|
+
sage: B.echelonize()
|
|
1145
|
+
sage: C = B.matrix_from_columns([3,4,5])
|
|
1146
|
+
sage: A.rank() < 3 or C == ~A
|
|
1147
|
+
True
|
|
1148
|
+
sage: A.rank() < 3 or C*A == MS(1)
|
|
1149
|
+
True
|
|
1150
|
+
|
|
1151
|
+
TESTS::
|
|
1152
|
+
|
|
1153
|
+
sage: K.<a> = GF(2^4)
|
|
1154
|
+
sage: A = random_matrix(K,2,3)
|
|
1155
|
+
sage: B = random_matrix(K,2,0)
|
|
1156
|
+
sage: A.augment(B) == A
|
|
1157
|
+
True
|
|
1158
|
+
|
|
1159
|
+
sage: B.augment(A) == A
|
|
1160
|
+
True
|
|
1161
|
+
|
|
1162
|
+
sage: M = Matrix(K, 0, 0, 0)
|
|
1163
|
+
sage: N = Matrix(K, 0, 19, 0)
|
|
1164
|
+
sage: W = M.augment(N)
|
|
1165
|
+
sage: W.ncols()
|
|
1166
|
+
19
|
|
1167
|
+
|
|
1168
|
+
sage: M = Matrix(K, 0, 1, 0)
|
|
1169
|
+
sage: N = Matrix(K, 0, 1, 0)
|
|
1170
|
+
sage: M.augment(N)
|
|
1171
|
+
[]
|
|
1172
|
+
|
|
1173
|
+
sage: A = matrix(K, 3, range(12))
|
|
1174
|
+
sage: B = vector(QQ, [2,5/7,1.2]) # see issue: 38448
|
|
1175
|
+
sage: A.augment(B).ncols()
|
|
1176
|
+
5
|
|
1177
|
+
|
|
1178
|
+
sage: B = vector([])
|
|
1179
|
+
sage: A.augment(B) == A
|
|
1180
|
+
True
|
|
1181
|
+
"""
|
|
1182
|
+
cdef Matrix_gf2e_dense _right
|
|
1183
|
+
cdef Matrix_gf2e_dense A
|
|
1184
|
+
|
|
1185
|
+
if not isinstance(right, Matrix_gf2e_dense):
|
|
1186
|
+
# See issue: #36761 - Allow Vectors to be augmented
|
|
1187
|
+
if hasattr(right, '_vector_'):
|
|
1188
|
+
rsize = len(right)
|
|
1189
|
+
if rsize==0:
|
|
1190
|
+
return self.__copy__()
|
|
1191
|
+
if self._nrows != rsize:
|
|
1192
|
+
raise TypeError("Both numbers of rows must match.")
|
|
1193
|
+
if self.base_ring() is not right.base_ring():
|
|
1194
|
+
right = right.change_ring(self.base_ring())
|
|
1195
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
1196
|
+
M = MatrixSpace(self.base_ring(), nrows=rsize, ncols=1)
|
|
1197
|
+
_right = <Matrix_gf2e_dense>(M(right))
|
|
1198
|
+
else:
|
|
1199
|
+
raise TypeError("a matrix must be augmented with another matrix, "
|
|
1200
|
+
"or a vector")
|
|
1201
|
+
else:
|
|
1202
|
+
_right = <Matrix_gf2e_dense>right
|
|
1203
|
+
|
|
1204
|
+
if self._nrows != _right._nrows:
|
|
1205
|
+
raise TypeError("Both numbers of rows must match.")
|
|
1206
|
+
|
|
1207
|
+
if self._ncols == 0:
|
|
1208
|
+
return _right.__copy__()
|
|
1209
|
+
if _right._ncols == 0:
|
|
1210
|
+
return self.__copy__()
|
|
1211
|
+
|
|
1212
|
+
A = self.new_matrix(ncols = self._ncols + _right._ncols)
|
|
1213
|
+
if self._nrows == 0:
|
|
1214
|
+
return A
|
|
1215
|
+
A._entries = mzed_concat(A._entries, self._entries, _right._entries)
|
|
1216
|
+
return A
|
|
1217
|
+
|
|
1218
|
+
cdef _stack_impl(self, bottom):
|
|
1219
|
+
r"""
|
|
1220
|
+
Stack ``self`` on top of ``bottom``.
|
|
1221
|
+
|
|
1222
|
+
INPUT:
|
|
1223
|
+
|
|
1224
|
+
- ``bottom`` -- a matrix with the same number of columns as ``self``
|
|
1225
|
+
|
|
1226
|
+
EXAMPLES::
|
|
1227
|
+
|
|
1228
|
+
sage: K.<a> = GF(2^4)
|
|
1229
|
+
sage: A = random_matrix(K,2,2)
|
|
1230
|
+
sage: B = random_matrix(K,2,2)
|
|
1231
|
+
sage: C = A.stack(B)
|
|
1232
|
+
sage: C[:2] == A
|
|
1233
|
+
True
|
|
1234
|
+
sage: C[2:] == B
|
|
1235
|
+
True
|
|
1236
|
+
sage: D = B.stack(A)
|
|
1237
|
+
sage: D[:2] == B
|
|
1238
|
+
True
|
|
1239
|
+
sage: D[2:] == A
|
|
1240
|
+
True
|
|
1241
|
+
|
|
1242
|
+
TESTS::
|
|
1243
|
+
|
|
1244
|
+
sage: A = random_matrix(K,0,3)
|
|
1245
|
+
sage: B = random_matrix(K,3,3)
|
|
1246
|
+
sage: A.stack(B) == B
|
|
1247
|
+
True
|
|
1248
|
+
sage: B.stack(A) == B
|
|
1249
|
+
True
|
|
1250
|
+
|
|
1251
|
+
sage: M = Matrix(K, 0, 0, 0)
|
|
1252
|
+
sage: N = Matrix(K, 19, 0, 0)
|
|
1253
|
+
sage: W = M.stack(N)
|
|
1254
|
+
sage: W.nrows()
|
|
1255
|
+
19
|
|
1256
|
+
sage: M = Matrix(K, 1, 0, 0)
|
|
1257
|
+
sage: N = Matrix(K, 1, 0, 0)
|
|
1258
|
+
sage: M.stack(N)
|
|
1259
|
+
[]
|
|
1260
|
+
|
|
1261
|
+
Check that we can stack a vector (:issue:`31708`)::
|
|
1262
|
+
|
|
1263
|
+
sage: R.<a> = GF(2^3)
|
|
1264
|
+
sage: M = matrix(R, [[1,1],[0,a+1]])
|
|
1265
|
+
sage: M.stack(vector(R, [a,0]))
|
|
1266
|
+
[ 1 1]
|
|
1267
|
+
[ 0 a + 1]
|
|
1268
|
+
[ a 0]
|
|
1269
|
+
"""
|
|
1270
|
+
cdef Matrix_gf2e_dense other = <Matrix_gf2e_dense> bottom
|
|
1271
|
+
|
|
1272
|
+
if self._nrows == 0:
|
|
1273
|
+
return other.__copy__()
|
|
1274
|
+
if other._nrows == 0:
|
|
1275
|
+
return self.__copy__()
|
|
1276
|
+
|
|
1277
|
+
cdef Matrix_gf2e_dense A
|
|
1278
|
+
A = self.new_matrix(nrows=self._nrows + other._nrows)
|
|
1279
|
+
if self._ncols == 0:
|
|
1280
|
+
return A
|
|
1281
|
+
A._entries = mzed_stack(A._entries, self._entries, other._entries)
|
|
1282
|
+
return A
|
|
1283
|
+
|
|
1284
|
+
def submatrix(self, Py_ssize_t row=0, Py_ssize_t col=0,
|
|
1285
|
+
Py_ssize_t nrows=-1, Py_ssize_t ncols=-1):
|
|
1286
|
+
"""
|
|
1287
|
+
Return submatrix from the index ``row,col`` (inclusive) with
|
|
1288
|
+
dimension ``nrows x ncols``.
|
|
1289
|
+
|
|
1290
|
+
INPUT:
|
|
1291
|
+
|
|
1292
|
+
- ``row`` -- index of start row
|
|
1293
|
+
- ``col`` -- index of start column
|
|
1294
|
+
- ``nrows`` -- number of rows of submatrix
|
|
1295
|
+
- ``ncols`` -- number of columns of submatrix
|
|
1296
|
+
|
|
1297
|
+
EXAMPLES::
|
|
1298
|
+
|
|
1299
|
+
sage: K.<a> = GF(2^10)
|
|
1300
|
+
sage: A = random_matrix(K,200,200)
|
|
1301
|
+
sage: A[0:2,0:2] == A.submatrix(0,0,2,2)
|
|
1302
|
+
True
|
|
1303
|
+
sage: A[0:100,0:100] == A.submatrix(0,0,100,100)
|
|
1304
|
+
True
|
|
1305
|
+
sage: A == A.submatrix(0,0,200,200)
|
|
1306
|
+
True
|
|
1307
|
+
|
|
1308
|
+
sage: A[1:3,1:3] == A.submatrix(1,1,2,2)
|
|
1309
|
+
True
|
|
1310
|
+
sage: A[1:100,1:100] == A.submatrix(1,1,99,99)
|
|
1311
|
+
True
|
|
1312
|
+
sage: A[1:200,1:200] == A.submatrix(1,1,199,199)
|
|
1313
|
+
True
|
|
1314
|
+
|
|
1315
|
+
TESTS for handling of default arguments (:issue:`18761`)::
|
|
1316
|
+
|
|
1317
|
+
sage: A.submatrix(17,15) == A.submatrix(17,15,183,185)
|
|
1318
|
+
True
|
|
1319
|
+
sage: A.submatrix(row=100,col=37,nrows=1,ncols=3) == A.submatrix(100,37,1,3)
|
|
1320
|
+
True
|
|
1321
|
+
"""
|
|
1322
|
+
if nrows < 0:
|
|
1323
|
+
nrows = self._nrows - row
|
|
1324
|
+
|
|
1325
|
+
if ncols < 0:
|
|
1326
|
+
ncols = self._ncols - col
|
|
1327
|
+
|
|
1328
|
+
cdef int highr = row + nrows
|
|
1329
|
+
cdef int highc = col + ncols
|
|
1330
|
+
|
|
1331
|
+
if row < 0:
|
|
1332
|
+
raise TypeError("Expected row >= 0, but got %d instead." % row)
|
|
1333
|
+
|
|
1334
|
+
if col < 0:
|
|
1335
|
+
raise TypeError("Expected col >= 0, but got %d instead." % col)
|
|
1336
|
+
|
|
1337
|
+
if highc > self._entries.ncols:
|
|
1338
|
+
raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead." % (highc, self._entries.ncols))
|
|
1339
|
+
|
|
1340
|
+
if highr > self._entries.nrows:
|
|
1341
|
+
raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead." % (highr, self._entries.nrows))
|
|
1342
|
+
|
|
1343
|
+
cdef Matrix_gf2e_dense A = self.new_matrix(nrows=nrows, ncols=ncols)
|
|
1344
|
+
if ncols == 0 or nrows == 0:
|
|
1345
|
+
return A
|
|
1346
|
+
A._entries = mzed_submatrix(A._entries, self._entries, row, col, highr, highc)
|
|
1347
|
+
return A
|
|
1348
|
+
|
|
1349
|
+
def rank(self):
|
|
1350
|
+
"""
|
|
1351
|
+
Return the rank of this matrix (cached).
|
|
1352
|
+
|
|
1353
|
+
EXAMPLES::
|
|
1354
|
+
|
|
1355
|
+
sage: K.<a> = GF(2^4)
|
|
1356
|
+
sage: A = random_matrix(K, 10, 10, algorithm='unimodular')
|
|
1357
|
+
sage: A.rank()
|
|
1358
|
+
10
|
|
1359
|
+
sage: A = matrix(K, 10, 0)
|
|
1360
|
+
sage: A.rank()
|
|
1361
|
+
0
|
|
1362
|
+
"""
|
|
1363
|
+
x = self.fetch('rank')
|
|
1364
|
+
if x is not None:
|
|
1365
|
+
return x
|
|
1366
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
1367
|
+
return 0
|
|
1368
|
+
cdef mzed_t *A = mzed_copy(NULL, self._entries)
|
|
1369
|
+
|
|
1370
|
+
cdef size_t r = mzed_echelonize(A, 0)
|
|
1371
|
+
mzed_free(A)
|
|
1372
|
+
self.cache('rank', r)
|
|
1373
|
+
return r
|
|
1374
|
+
|
|
1375
|
+
def __reduce__(self):
|
|
1376
|
+
"""
|
|
1377
|
+
EXAMPLES::
|
|
1378
|
+
|
|
1379
|
+
sage: K.<a> = GF(2^8)
|
|
1380
|
+
sage: A = random_matrix(K,70,70)
|
|
1381
|
+
sage: f, s= A.__reduce__()
|
|
1382
|
+
sage: from sage.matrix.matrix_gf2e_dense import unpickle_matrix_gf2e_dense_v0
|
|
1383
|
+
sage: f == unpickle_matrix_gf2e_dense_v0
|
|
1384
|
+
True
|
|
1385
|
+
sage: f(*s) == A
|
|
1386
|
+
True
|
|
1387
|
+
|
|
1388
|
+
See :issue:`21669`::
|
|
1389
|
+
|
|
1390
|
+
sage: all(f(*s) == B
|
|
1391
|
+
....: for r,c in [(0,0),(0,1),(1,0)]
|
|
1392
|
+
....: for B in [Matrix(GF(4, 'a'), r,c)]
|
|
1393
|
+
....: for f,s in [B.__reduce__()])
|
|
1394
|
+
True
|
|
1395
|
+
"""
|
|
1396
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
1397
|
+
|
|
1398
|
+
cdef Matrix_mod2_dense A
|
|
1399
|
+
cdef int r,c
|
|
1400
|
+
|
|
1401
|
+
r, c = self.nrows(), self.ncols()
|
|
1402
|
+
if r == 0 or c == 0:
|
|
1403
|
+
return unpickle_matrix_gf2e_dense_v0, (None, self.base_ring(), r, c)
|
|
1404
|
+
MS = MatrixSpace(GF(2), self._entries.x.nrows, self._entries.x.ncols)
|
|
1405
|
+
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = False)
|
|
1406
|
+
A._entries = mzd_copy( NULL, self._entries.x)
|
|
1407
|
+
return unpickle_matrix_gf2e_dense_v0, (A, self.base_ring(), self.nrows(), self.ncols())
|
|
1408
|
+
|
|
1409
|
+
def slice(self):
|
|
1410
|
+
r"""
|
|
1411
|
+
Unpack this matrix into matrices over `\GF{2}`.
|
|
1412
|
+
|
|
1413
|
+
Elements in `\GF{2^e}` can be represented as `\sum c_i a^i`
|
|
1414
|
+
where `a` is a root the minimal polynomial. This function
|
|
1415
|
+
returns a tuple of matrices `C` whose entry `C_i[x,y]` is the
|
|
1416
|
+
coefficient of `c_i` in `A[x,y]` if this matrix is `A`.
|
|
1417
|
+
|
|
1418
|
+
EXAMPLES::
|
|
1419
|
+
|
|
1420
|
+
sage: K.<a> = GF(2^2)
|
|
1421
|
+
sage: A = random_matrix(K, 5, 5)
|
|
1422
|
+
sage: A0, A1 = A.slice()
|
|
1423
|
+
sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(25))
|
|
1424
|
+
True
|
|
1425
|
+
|
|
1426
|
+
sage: K.<a> = GF(2^3)
|
|
1427
|
+
sage: A = random_matrix(K, 5, 5)
|
|
1428
|
+
sage: A0, A1, A2 = A.slice()
|
|
1429
|
+
sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] + a^2*A2.list()[i] for i in range(25))
|
|
1430
|
+
True
|
|
1431
|
+
|
|
1432
|
+
Slicing and clinging are inverse operations::
|
|
1433
|
+
|
|
1434
|
+
sage: B = matrix(K, 5, 5)
|
|
1435
|
+
sage: B.cling(A0, A1, A2)
|
|
1436
|
+
sage: B == A
|
|
1437
|
+
True
|
|
1438
|
+
"""
|
|
1439
|
+
if self._entries.finite_field.degree > 4:
|
|
1440
|
+
raise NotImplementedError("Slicing is only implemented for degree <= 4.")
|
|
1441
|
+
|
|
1442
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
1443
|
+
|
|
1444
|
+
MS = MatrixSpace(GF(2), self._nrows, self._ncols)
|
|
1445
|
+
cdef mzd_slice_t *a = mzed_slice(NULL, self._entries)
|
|
1446
|
+
|
|
1447
|
+
cdef Matrix_mod2_dense A0, A1, A2, A3
|
|
1448
|
+
A0 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
|
|
1449
|
+
A1 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
|
|
1450
|
+
mzd_copy(A0._entries, a.x[0])
|
|
1451
|
+
mzd_copy(A1._entries, a.x[1])
|
|
1452
|
+
if self._entries.finite_field.degree > 2:
|
|
1453
|
+
A2 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
|
|
1454
|
+
mzd_copy(A2._entries, a.x[2])
|
|
1455
|
+
if self._entries.finite_field.degree > 3:
|
|
1456
|
+
A3 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)
|
|
1457
|
+
mzd_copy(A3._entries, a.x[3])
|
|
1458
|
+
|
|
1459
|
+
mzd_slice_free(a)
|
|
1460
|
+
if self._entries.finite_field.degree == 2:
|
|
1461
|
+
return A0,A1
|
|
1462
|
+
elif self._entries.finite_field.degree == 3:
|
|
1463
|
+
return A0,A1,A2
|
|
1464
|
+
elif self._entries.finite_field.degree == 4:
|
|
1465
|
+
return A0,A1,A2,A3
|
|
1466
|
+
|
|
1467
|
+
def cling(self, *C):
|
|
1468
|
+
r"""
|
|
1469
|
+
Pack the matrices over `\GF{2}` into this matrix over `\GF{2^e}`.
|
|
1470
|
+
|
|
1471
|
+
Elements in `\GF{2^e}` can be represented as `\sum c_i a^i` where
|
|
1472
|
+
`a` is a root the minimal polynomial. If this matrix is `A`
|
|
1473
|
+
then this function writes `c_i a^i` to the entry `A[x,y]`
|
|
1474
|
+
where `c_i` is the entry `C_i[x,y]`.
|
|
1475
|
+
|
|
1476
|
+
INPUT:
|
|
1477
|
+
|
|
1478
|
+
- ``C`` -- list of matrices over GF(2)
|
|
1479
|
+
|
|
1480
|
+
EXAMPLES::
|
|
1481
|
+
|
|
1482
|
+
sage: K.<a> = GF(2^2)
|
|
1483
|
+
sage: A = matrix(K, 5, 5)
|
|
1484
|
+
sage: A0 = random_matrix(GF(2), 5, 5)
|
|
1485
|
+
sage: A1 = random_matrix(GF(2), 5, 5)
|
|
1486
|
+
sage: A.cling(A0, A1)
|
|
1487
|
+
sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(25))
|
|
1488
|
+
True
|
|
1489
|
+
|
|
1490
|
+
Slicing and clinging are inverse operations::
|
|
1491
|
+
|
|
1492
|
+
sage: B0, B1 = A.slice()
|
|
1493
|
+
sage: B0 == A0 and B1 == A1
|
|
1494
|
+
True
|
|
1495
|
+
|
|
1496
|
+
TESTS::
|
|
1497
|
+
|
|
1498
|
+
sage: K.<a> = GF(2^2)
|
|
1499
|
+
sage: A = matrix(K, 5, 5)
|
|
1500
|
+
sage: A0 = random_matrix(GF(2), 5, 5)
|
|
1501
|
+
sage: A1 = random_matrix(GF(2), 5, 5)
|
|
1502
|
+
sage: A.cling(A0, A1)
|
|
1503
|
+
sage: B = copy(A)
|
|
1504
|
+
sage: A.cling(A0, A1)
|
|
1505
|
+
sage: A == B
|
|
1506
|
+
True
|
|
1507
|
+
|
|
1508
|
+
sage: A.cling(A0)
|
|
1509
|
+
Traceback (most recent call last):
|
|
1510
|
+
...
|
|
1511
|
+
ValueError: The number of input matrices must be equal to the degree of the base field.
|
|
1512
|
+
|
|
1513
|
+
sage: K.<a> = GF(2^5)
|
|
1514
|
+
sage: A = matrix(K, 5, 5)
|
|
1515
|
+
sage: A0 = random_matrix(GF(2), 5, 5)
|
|
1516
|
+
sage: A1 = random_matrix(GF(2), 5, 5)
|
|
1517
|
+
sage: A2 = random_matrix(GF(2), 5, 5)
|
|
1518
|
+
sage: A3 = random_matrix(GF(2), 5, 5)
|
|
1519
|
+
sage: A4 = random_matrix(GF(2), 5, 5)
|
|
1520
|
+
sage: A.cling(A0, A1, A2, A3, A4)
|
|
1521
|
+
Traceback (most recent call last):
|
|
1522
|
+
...
|
|
1523
|
+
NotImplementedError: Cling is only implemented for degree <= 4.
|
|
1524
|
+
"""
|
|
1525
|
+
cdef Py_ssize_t i
|
|
1526
|
+
|
|
1527
|
+
if self._entries.finite_field.degree > 4:
|
|
1528
|
+
raise NotImplementedError("Cling is only implemented for degree <= 4.")
|
|
1529
|
+
|
|
1530
|
+
if self._is_immutable:
|
|
1531
|
+
raise TypeError("Immutable matrices cannot be modified.")
|
|
1532
|
+
|
|
1533
|
+
if len(C) != self._entries.finite_field.degree:
|
|
1534
|
+
raise ValueError("The number of input matrices must be equal to the degree of the base field.")
|
|
1535
|
+
|
|
1536
|
+
cdef mzd_slice_t *v = mzd_slice_init(self._entries.finite_field, self._nrows, self._ncols)
|
|
1537
|
+
for i in range(self._entries.finite_field.degree):
|
|
1538
|
+
if not isinstance(C[i], Matrix_mod2_dense):
|
|
1539
|
+
mzd_slice_free(v)
|
|
1540
|
+
raise TypeError("All input matrices must be over GF(2).")
|
|
1541
|
+
mzd_copy(v.x[i], (<Matrix_mod2_dense>C[i])._entries)
|
|
1542
|
+
mzed_set_ui(self._entries, 0)
|
|
1543
|
+
mzed_cling(self._entries, v)
|
|
1544
|
+
mzd_slice_free(v)
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
def unpickle_matrix_gf2e_dense_v0(Matrix_mod2_dense a, base_ring, nrows, ncols):
|
|
1548
|
+
r"""
|
|
1549
|
+
EXAMPLES::
|
|
1550
|
+
|
|
1551
|
+
sage: K.<a> = GF(2^2)
|
|
1552
|
+
sage: A = random_matrix(K,10,10)
|
|
1553
|
+
sage: f, s= A.__reduce__()
|
|
1554
|
+
sage: from sage.matrix.matrix_gf2e_dense import unpickle_matrix_gf2e_dense_v0
|
|
1555
|
+
sage: f == unpickle_matrix_gf2e_dense_v0
|
|
1556
|
+
True
|
|
1557
|
+
sage: f(*s) == A
|
|
1558
|
+
True
|
|
1559
|
+
|
|
1560
|
+
We can still unpickle pickles from before :issue:`19240`::
|
|
1561
|
+
|
|
1562
|
+
sage: old_pickle = b'x\x9c\x85RKo\xd3@\x10\xae\xdd$$\xdb&\xe5U\x1e-\x8f\xc2\xc9\x12RD#$\xce\xa0\xb4\x80\x07\xa2\xca\xc2\x07\x0e\xd5\xe2:\x1b\xdb\x8acg\x1c\xa7J\x85*!\xa4\x90\xe6\x07p\xe0\xc4\x01q\xe5\xc4\x19\xf5\xd0?\xc1\x81\xdf\x80\xb8q\x0b\xb3\x8eMS\xa1\x82V;;\xb3\xdf\xce\xf7\xcd\x8e\xe6\xb5j\xf7,GT;V\x1cy\x83\xf4\xe0\x9d\xb0Y\x13\xbc)\x82\x9e`\xfd\xa0\xeb\xd9m_\xf0\xbf1\xbe{\x97\xa1\xa2\x9d\xc6\xf0\x0f\x82,\x7f\x9d\xa1\xaa\x81\n\xb9m\x9c\xd7\xf4\xf1d2\x81-h\xc0#(\x03\x83\x15\xdas\xc9*\xc3\x13x\x0cu0\xd28\x97\x9e*(0\x9f\xfa\x1b\xd0\xd2\x7fH\x82\xb5\xf4\xa2@TO\xe19\x01I\xac\x136\x991\x9f\xa4\xf9&\xcd\x07i\xbe\xcb\xd4ib\t\xba\xa4\xf6\x02zIT\xd1\x8f2(u\x15\xfd\x9d<\xee@\x05V\xd3\x94E*\xb0\x0e\x0fH\xad\xa8\xbf\x97\xa0\r\x03\xfd\xf0\xb8\x1aU\xff\x92\x90\xe8?\xa5\xd6\x814_\xa5\xf9(\xcd\xafc\xe99\xe2\xd9\xa0\x06\xd4\xf5\xcf\xf2\xf2!\xbc\xd4\xdf\x90#\xc0\x8f\r\xccM\x1b\xdd\x8b\xa3\xbe\x1d\xf7#QmYv\x1cF{\xcc\x11\x81\x88<\x9b\xa71\xcf:\xce0\xaf\x9d\x96\xe3\x87a\xbb\xdf\xe5\x8e\x1f\xeeX>\xc3\x82\xb9\xb0\xe9\x05^,6=\xe17\xf1\xcc\xd0\xc0"u\xb0d\xe6wDl\xdd\x1fa)e\x8a\xbc\xc0\xe9U\xbd \x16\x8e\x88X\xc7j\x0b\x9e\x05\xc8L\xe5\x1e%.\x98\x8a5\xc4\xc5\xd9\xf7\xdd\xd0\xdf\x0b\xc2\x8eg\xf93.wZ\xb5\xc1\x94B\xf8\xa2#\x82\x98a\xf9\xffY\x12\xe3v\x18L\xff\x14Fl\xeb\x0ff\x10\xc4\xb0\xa2\xb9y\xcd-\xba%\xcd\xa5\x8ajT\xd1\x92\xa9\x0c\x86x\xb6a\xe6h\xf8\x02<g\xaa\xaf\xf6\xdd%\x89\xae\x13z\xfe \xc6\x0b\xfb1^4p\x99\x1e6\xc6\xd4\xebK\xdbx\xf9\xc4\x8f[Iw\xf8\x89\xef\xcbQf\xcfh\xe3\x95\x8c\xebj&\xb9\xe2.\x8f\x0c\\ui\x89\xf1x\xf4\xd6\xc0kf\xc1\xf1v\xad(\xc4\xeb\x89~\xfa\xf0\x06\xa8\xa4\x7f\x93\xf4\xd7\x0c\xbcE#\xad\x92\xfc\xed\xeao\xefX\\\x03'
|
|
1563
|
+
sage: loads(old_pickle)
|
|
1564
|
+
[ 0 a]
|
|
1565
|
+
[a + 1 1]
|
|
1566
|
+
"""
|
|
1567
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
1568
|
+
|
|
1569
|
+
MS = MatrixSpace(base_ring, nrows, ncols)
|
|
1570
|
+
cdef Matrix_gf2e_dense A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, MS, 0, 0, 0)
|
|
1571
|
+
if nrows != 0 and ncols != 0:
|
|
1572
|
+
mzd_copy(A._entries.x, a._entries)
|
|
1573
|
+
return A
|