passagemath-linbox 10.6.32__cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-linbox might be problematic. Click here for more details.
- passagemath_linbox-10.6.32.dist-info/METADATA +100 -0
- passagemath_linbox-10.6.32.dist-info/RECORD +73 -0
- passagemath_linbox-10.6.32.dist-info/WHEEL +6 -0
- passagemath_linbox-10.6.32.dist-info/top_level.txt +2 -0
- passagemath_linbox.libs/libfflas-d452d784.so.1.0.0 +0 -0
- passagemath_linbox.libs/libffpack-32579c9b.so.1.0.0 +0 -0
- passagemath_linbox.libs/libflint-66e12231.so.21.0.0 +0 -0
- passagemath_linbox.libs/libgd-76eb082b.so.3.0.11 +0 -0
- passagemath_linbox.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
- passagemath_linbox.libs/libgivaro-fc554fc9.so.9.2.1 +0 -0
- passagemath_linbox.libs/libgmp-6e109695.so.10.5.0 +0 -0
- passagemath_linbox.libs/libgmpxx-ecb9d6e3.so.4.7.0 +0 -0
- passagemath_linbox.libs/libiml-aeb1d147.so.0.1.1 +0 -0
- passagemath_linbox.libs/liblinbox-f1d24fc1.so.0.0.0 +0 -0
- passagemath_linbox.libs/libm4ri-9da2b874.so.1.0.0 +0 -0
- passagemath_linbox.libs/libm4rie-cf8cc058.so.1.0.0 +0 -0
- passagemath_linbox.libs/libmpfr-82690d50.so.6.2.1 +0 -0
- passagemath_linbox.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
- passagemath_linbox.libs/libpng16-b4a91cd1.so.16.43.0 +0 -0
- passagemath_linbox.libs/libquadmath-2284e583.so.0.0.0 +0 -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-x86_64-linux-gnu.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-x86_64-linux-gnu.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-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_cyclo_linbox.pyx +361 -0
- sage/matrix/matrix_gf2e_dense.cpython-313-x86_64-linux-gnu.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-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_integer_iml.pyx +316 -0
- sage/matrix/matrix_integer_linbox.cpython-313-x86_64-linux-gnu.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-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_integer_sparse_linbox.pyx +465 -0
- sage/matrix/matrix_mod2_dense.cpython-313-x86_64-linux-gnu.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-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_modn_dense_double.pyx +179 -0
- sage/matrix/matrix_modn_dense_float.cpython-313-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_modn_dense_float.pyx +154 -0
- sage/matrix/matrix_modn_sparse.cpython-313-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_modn_sparse.pyx +871 -0
- sage/matrix/matrix_rational_linbox.cpython-313-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_rational_linbox.pyx +36 -0
- sage/matrix/misc.cpython-313-x86_64-linux-gnu.so +0 -0
- sage/matrix/misc.pyx +418 -0
- sage/modules/all__sagemath_linbox.py +1 -0
- sage/modules/numpy_util.cpython-313-x86_64-linux-gnu.so +0 -0
- sage/modules/numpy_util.pxd +10 -0
- sage/modules/numpy_util.pyx +136 -0
- sage/modules/vector_mod2_dense.cpython-313-x86_64-linux-gnu.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,2789 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-linbox
|
|
2
|
+
# distutils: libraries = M4RI_LIBRARIES GDLIB_LIBRARIES LIBPNG_LIBRARIES ZLIB_LIBRARIES
|
|
3
|
+
# distutils: library_dirs = M4RI_LIBDIR GDLIB_LIBDIR LIBPNG_LIBDIR ZLIB_LIBDIR
|
|
4
|
+
# distutils: include_dirs = M4RI_INCDIR GDLIB_INCDIR LIBPNG_INCDIR ZLIB_INCDIR
|
|
5
|
+
# distutils: extra_compile_args = M4RI_CFLAGS
|
|
6
|
+
"""
|
|
7
|
+
Dense matrices over GF(2) using the M4RI library
|
|
8
|
+
|
|
9
|
+
AUTHOR: Martin Albrecht <malb@informatik.uni-bremen.de>
|
|
10
|
+
|
|
11
|
+
EXAMPLES::
|
|
12
|
+
|
|
13
|
+
sage: a = matrix(GF(2),3,range(9),sparse=False); a
|
|
14
|
+
[0 1 0]
|
|
15
|
+
[1 0 1]
|
|
16
|
+
[0 1 0]
|
|
17
|
+
sage: a.rank()
|
|
18
|
+
2
|
|
19
|
+
sage: type(a)
|
|
20
|
+
<class 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>
|
|
21
|
+
sage: a[0,0] = 1
|
|
22
|
+
sage: a.rank()
|
|
23
|
+
3
|
|
24
|
+
sage: parent(a)
|
|
25
|
+
Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 2
|
|
26
|
+
|
|
27
|
+
sage: a^2
|
|
28
|
+
[0 1 1]
|
|
29
|
+
[1 0 0]
|
|
30
|
+
[1 0 1]
|
|
31
|
+
sage: a+a
|
|
32
|
+
[0 0 0]
|
|
33
|
+
[0 0 0]
|
|
34
|
+
[0 0 0]
|
|
35
|
+
|
|
36
|
+
sage: b = a.new_matrix(2,3,range(6)); b
|
|
37
|
+
[0 1 0]
|
|
38
|
+
[1 0 1]
|
|
39
|
+
|
|
40
|
+
sage: a*b
|
|
41
|
+
Traceback (most recent call last):
|
|
42
|
+
...
|
|
43
|
+
TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 2' and 'Full MatrixSpace of 2 by 3 dense matrices over Finite Field of size 2'
|
|
44
|
+
sage: b*a
|
|
45
|
+
[1 0 1]
|
|
46
|
+
[1 0 0]
|
|
47
|
+
|
|
48
|
+
sage: TestSuite(a).run()
|
|
49
|
+
sage: TestSuite(b).run()
|
|
50
|
+
|
|
51
|
+
sage: a.echelonize(); a
|
|
52
|
+
[1 0 0]
|
|
53
|
+
[0 1 0]
|
|
54
|
+
[0 0 1]
|
|
55
|
+
sage: b.echelonize(); b
|
|
56
|
+
[1 0 1]
|
|
57
|
+
[0 1 0]
|
|
58
|
+
|
|
59
|
+
TESTS::
|
|
60
|
+
|
|
61
|
+
sage: FF = FiniteField(2)
|
|
62
|
+
sage: V = VectorSpace(FF,2)
|
|
63
|
+
sage: v = V([0,1]); v
|
|
64
|
+
(0, 1)
|
|
65
|
+
sage: W = V.subspace([v])
|
|
66
|
+
sage: W
|
|
67
|
+
Vector space of degree 2 and dimension 1 over Finite Field of size 2
|
|
68
|
+
Basis matrix:
|
|
69
|
+
[0 1]
|
|
70
|
+
sage: v in W
|
|
71
|
+
True
|
|
72
|
+
|
|
73
|
+
sage: M = Matrix(GF(2), [[1,1,0],[0,1,0]])
|
|
74
|
+
sage: M.row_space()
|
|
75
|
+
Vector space of degree 3 and dimension 2 over Finite Field of size 2
|
|
76
|
+
Basis matrix:
|
|
77
|
+
[1 0 0]
|
|
78
|
+
[0 1 0]
|
|
79
|
+
|
|
80
|
+
sage: M = Matrix(GF(2), [[1,1,0],[0,0,1]])
|
|
81
|
+
sage: M.row_space()
|
|
82
|
+
Vector space of degree 3 and dimension 2 over Finite Field of size 2
|
|
83
|
+
Basis matrix:
|
|
84
|
+
[1 1 0]
|
|
85
|
+
[0 0 1]
|
|
86
|
+
|
|
87
|
+
.. TODO::
|
|
88
|
+
|
|
89
|
+
- make LinBox frontend and use it
|
|
90
|
+
|
|
91
|
+
- charpoly ?
|
|
92
|
+
- minpoly ?
|
|
93
|
+
|
|
94
|
+
- make Matrix_modn_frontend and use it (?)
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
# ****************************************************************************
|
|
98
|
+
# Copyright (C) 2004,2005,2006 William Stein <wstein@gmail.com>
|
|
99
|
+
# Copyright (C) 2007,2008,2009 Martin Albrecht <M.R.Albrecht@rhul.ac.uk>
|
|
100
|
+
#
|
|
101
|
+
# This program is free software: you can redistribute it and/or modify
|
|
102
|
+
# it under the terms of the GNU General Public License as published by
|
|
103
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
104
|
+
# (at your option) any later version.
|
|
105
|
+
# https://www.gnu.org/licenses/
|
|
106
|
+
# ****************************************************************************
|
|
107
|
+
|
|
108
|
+
from cysignals.memory cimport check_malloc, sig_free
|
|
109
|
+
from cysignals.signals cimport sig_on, sig_str, sig_off
|
|
110
|
+
|
|
111
|
+
cimport sage.matrix.matrix_dense as matrix_dense
|
|
112
|
+
from sage.matrix.args cimport SparseEntry, MatrixArgs_init, MA_ENTRIES_NDARRAY
|
|
113
|
+
from libc.stdio cimport *
|
|
114
|
+
from sage.structure.element cimport (Matrix, Vector)
|
|
115
|
+
from sage.modules.free_module_element cimport FreeModuleElement
|
|
116
|
+
from sage.libs.gmp.random cimport *
|
|
117
|
+
from sage.misc.randstate cimport randstate, current_randstate
|
|
118
|
+
from sage.misc.verbose import verbose, get_verbose
|
|
119
|
+
VectorSpace = None
|
|
120
|
+
from sage.modules.vector_mod2_dense cimport Vector_mod2_dense
|
|
121
|
+
from sage.structure.richcmp cimport rich_to_bool
|
|
122
|
+
from sage.cpython.string cimport char_to_str
|
|
123
|
+
from sage.cpython.string import FS_ENCODING
|
|
124
|
+
|
|
125
|
+
cdef extern from "gd.h":
|
|
126
|
+
ctypedef struct gdImagePtr "gdImagePtr":
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
gdImagePtr gdImageCreateFromPng(FILE *f)
|
|
130
|
+
gdImagePtr gdImageCreateFromPngPtr(int size, void *s)
|
|
131
|
+
gdImagePtr gdImageCreate(int x, int y)
|
|
132
|
+
void gdImagePng(gdImagePtr im, FILE *out)
|
|
133
|
+
void *gdImagePngPtr(gdImagePtr im, int *size)
|
|
134
|
+
void gdImageDestroy(gdImagePtr im)
|
|
135
|
+
int gdImageSX(gdImagePtr im)
|
|
136
|
+
int gdImageSY(gdImagePtr im)
|
|
137
|
+
int gdImageGetPixel(gdImagePtr im, int x, int y)
|
|
138
|
+
void gdImageSetPixel(gdImagePtr im, int x, int y, int value)
|
|
139
|
+
int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)
|
|
140
|
+
void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
|
|
141
|
+
void gdFree(void *m)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# Construct global Gray code tables
|
|
145
|
+
m4ri_build_all_codes()
|
|
146
|
+
import atexit
|
|
147
|
+
atexit.register(m4ri_destroy_all_codes)
|
|
148
|
+
|
|
149
|
+
cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
|
|
150
|
+
"""
|
|
151
|
+
Dense matrix over GF(2).
|
|
152
|
+
"""
|
|
153
|
+
def __cinit__(self):
|
|
154
|
+
"""
|
|
155
|
+
TESTS:
|
|
156
|
+
|
|
157
|
+
See :issue:`10858`::
|
|
158
|
+
|
|
159
|
+
sage: matrix(GF(2),0,[]) * vector(GF(2),0,[])
|
|
160
|
+
()
|
|
161
|
+
|
|
162
|
+
Large matrices fail gracefully::
|
|
163
|
+
|
|
164
|
+
sage: MatrixSpace(GF(2), 1, 2^40).zero()
|
|
165
|
+
Traceback (most recent call last):
|
|
166
|
+
...
|
|
167
|
+
OverflowError: ...
|
|
168
|
+
sage: MatrixSpace(GF(2), 2^40, 1).zero()
|
|
169
|
+
Traceback (most recent call last):
|
|
170
|
+
...
|
|
171
|
+
OverflowError: ...
|
|
172
|
+
|
|
173
|
+
Allocation fails if a memory limit is set (Linux only) lower
|
|
174
|
+
than is needed to construct a matrix but still high enough
|
|
175
|
+
that it doesn't crash the rest of SageMath::
|
|
176
|
+
|
|
177
|
+
sage: from platform import system
|
|
178
|
+
sage: import resource
|
|
179
|
+
sage: orig_soft, orig_hard = resource.getrlimit(resource.RLIMIT_AS)
|
|
180
|
+
sage: if system() != "Linux":
|
|
181
|
+
....: raise RuntimeError("matrix allocation failed")
|
|
182
|
+
....: else:
|
|
183
|
+
....: four_GiB = 4*1024^3
|
|
184
|
+
....: resource.setrlimit(resource.RLIMIT_AS, (four_GiB, orig_hard))
|
|
185
|
+
....: MatrixSpace(GF(2), 2^30)(1)
|
|
186
|
+
Traceback (most recent call last):
|
|
187
|
+
...
|
|
188
|
+
RuntimeError: matrix allocation failed
|
|
189
|
+
sage: resource.setrlimit(resource.RLIMIT_AS, (orig_soft, orig_hard))
|
|
190
|
+
sage: (orig_soft, orig_hard) == resource.getrlimit(resource.RLIMIT_AS)
|
|
191
|
+
True
|
|
192
|
+
"""
|
|
193
|
+
# m4ri assumes that nrows and ncols are of type rci_t:
|
|
194
|
+
# check for overflow
|
|
195
|
+
cdef rci_t rci_nrows = self._nrows
|
|
196
|
+
cdef rci_t rci_ncols = self._ncols
|
|
197
|
+
if <Py_ssize_t>(rci_nrows) != self._nrows:
|
|
198
|
+
raise OverflowError(f"matrices with {self._nrows} rows over {self._base_ring} are not supported")
|
|
199
|
+
if <Py_ssize_t>(rci_ncols) != self._ncols:
|
|
200
|
+
raise OverflowError(f"matrices with {self._ncols} columns over {self._base_ring} are not supported")
|
|
201
|
+
|
|
202
|
+
sig_str("matrix allocation failed")
|
|
203
|
+
self._entries = mzd_init(self._nrows, self._ncols)
|
|
204
|
+
sig_off()
|
|
205
|
+
|
|
206
|
+
# cache elements
|
|
207
|
+
self._zero = self._base_ring(0)
|
|
208
|
+
self._one = self._base_ring(1)
|
|
209
|
+
|
|
210
|
+
def __dealloc__(self):
|
|
211
|
+
if self._entries:
|
|
212
|
+
mzd_free(self._entries)
|
|
213
|
+
self._entries = NULL
|
|
214
|
+
|
|
215
|
+
def __init__(self, parent, entries=None, copy=None, bint coerce=True):
|
|
216
|
+
"""
|
|
217
|
+
Construct a dense matrix over GF(2).
|
|
218
|
+
|
|
219
|
+
INPUT:
|
|
220
|
+
|
|
221
|
+
- ``parent`` -- a matrix space over ``GF(2)``
|
|
222
|
+
|
|
223
|
+
- ``entries`` -- see :func:`matrix`
|
|
224
|
+
|
|
225
|
+
- ``copy`` -- ignored (for backwards compatibility)
|
|
226
|
+
|
|
227
|
+
- ``coerce`` -- if ``False``, assume without checking that the
|
|
228
|
+
entries lie in the base ring
|
|
229
|
+
|
|
230
|
+
EXAMPLES::
|
|
231
|
+
|
|
232
|
+
sage: type(random_matrix(GF(2),2,2))
|
|
233
|
+
<class 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>
|
|
234
|
+
|
|
235
|
+
sage: Matrix(GF(2),3,3,1)
|
|
236
|
+
[1 0 0]
|
|
237
|
+
[0 1 0]
|
|
238
|
+
[0 0 1]
|
|
239
|
+
|
|
240
|
+
sage: Matrix(GF(2),2,2,[1,1,1,0])
|
|
241
|
+
[1 1]
|
|
242
|
+
[1 0]
|
|
243
|
+
|
|
244
|
+
sage: Matrix(GF(2),2,2,4)
|
|
245
|
+
[0 0]
|
|
246
|
+
[0 0]
|
|
247
|
+
|
|
248
|
+
sage: Matrix(GF(2),1,1, 1/3)
|
|
249
|
+
[1]
|
|
250
|
+
sage: Matrix(GF(2),1,1, [1/3])
|
|
251
|
+
[1]
|
|
252
|
+
|
|
253
|
+
TESTS::
|
|
254
|
+
|
|
255
|
+
sage: Matrix(GF(2),0,0)
|
|
256
|
+
[]
|
|
257
|
+
sage: Matrix(GF(2),2,0)
|
|
258
|
+
[]
|
|
259
|
+
sage: Matrix(GF(2),0,2)
|
|
260
|
+
[]
|
|
261
|
+
|
|
262
|
+
Make sure construction from numpy array is reasonably fast::
|
|
263
|
+
|
|
264
|
+
sage: # needs numpy
|
|
265
|
+
sage: import numpy as np
|
|
266
|
+
sage: n = 5000
|
|
267
|
+
sage: M = matrix(GF(2), np.random.randint(0, 2, (n, n))) # around 700ms
|
|
268
|
+
|
|
269
|
+
Unsupported numpy data types (slower but still works)::
|
|
270
|
+
|
|
271
|
+
sage: # needs numpy
|
|
272
|
+
sage: n = 100
|
|
273
|
+
sage: M = matrix(GF(2), np.random.randint(0, 2, (n, n)).astype(np.float32))
|
|
274
|
+
"""
|
|
275
|
+
ma = MatrixArgs_init(parent, entries)
|
|
276
|
+
if ma.get_type() == MA_ENTRIES_NDARRAY:
|
|
277
|
+
from ..modules.numpy_util import set_matrix_mod2_from_numpy
|
|
278
|
+
if set_matrix_mod2_from_numpy(self, ma.entries):
|
|
279
|
+
return
|
|
280
|
+
for t in ma.iter(coerce, True):
|
|
281
|
+
se = <SparseEntry>t
|
|
282
|
+
mzd_write_bit(self._entries, se.i, se.j, se.entry)
|
|
283
|
+
|
|
284
|
+
cdef long _hash_(self) except -1:
|
|
285
|
+
r"""
|
|
286
|
+
EXAMPLES::
|
|
287
|
+
|
|
288
|
+
sage: B = random_matrix(GF(2),3,3)
|
|
289
|
+
sage: B.set_immutable()
|
|
290
|
+
sage: _ = {B:0} # indirect doctest
|
|
291
|
+
sage: M = random_matrix(GF(2), 123, 321)
|
|
292
|
+
sage: M.set_immutable()
|
|
293
|
+
sage: MZ = M.change_ring(ZZ)
|
|
294
|
+
sage: MZ.set_immutable()
|
|
295
|
+
sage: hash(M) == hash(MZ)
|
|
296
|
+
True
|
|
297
|
+
sage: MS = M.sparse_matrix()
|
|
298
|
+
sage: MS.set_immutable()
|
|
299
|
+
sage: hash(M) == hash(MS)
|
|
300
|
+
True
|
|
301
|
+
|
|
302
|
+
TESTS::
|
|
303
|
+
|
|
304
|
+
sage: A = matrix(GF(2),2,0)
|
|
305
|
+
sage: hash(A)
|
|
306
|
+
Traceback (most recent call last):
|
|
307
|
+
...
|
|
308
|
+
TypeError: mutable matrices are unhashable
|
|
309
|
+
sage: A.set_immutable()
|
|
310
|
+
sage: hash(A)
|
|
311
|
+
0
|
|
312
|
+
|
|
313
|
+
Check that there are no collisions for all matrices up to 4x4,
|
|
314
|
+
except for the zero matrix and the scalar matrix 1::
|
|
315
|
+
|
|
316
|
+
sage: L = []
|
|
317
|
+
sage: for nr in [1, 2, 3, 4]: # long time
|
|
318
|
+
....: for nc in [1, 2, 3, 4]:
|
|
319
|
+
....: MS = MatrixSpace(GF(2), nr, nc)
|
|
320
|
+
....: for M in MS:
|
|
321
|
+
....: if (M == 0) or (M == 1): continue
|
|
322
|
+
....: M.set_immutable()
|
|
323
|
+
....: L.append(hash(M))
|
|
324
|
+
sage: len(L) # long time
|
|
325
|
+
74934
|
|
326
|
+
sage: len(set(L)) # long time
|
|
327
|
+
74934
|
|
328
|
+
"""
|
|
329
|
+
cdef long C[5]
|
|
330
|
+
self.get_hash_constants(C)
|
|
331
|
+
|
|
332
|
+
cdef long h = 0, k, l
|
|
333
|
+
cdef Py_ssize_t i, j
|
|
334
|
+
sig_on()
|
|
335
|
+
for i in range(self._nrows):
|
|
336
|
+
k = C[0] if i == 0 else C[1] + C[2] * i
|
|
337
|
+
for j in range(self._ncols):
|
|
338
|
+
if mzd_read_bit(self._entries, i, j):
|
|
339
|
+
l = C[3] * (i - j) * (i ^ j)
|
|
340
|
+
h += (k ^ l)
|
|
341
|
+
h *= C[4]
|
|
342
|
+
sig_off()
|
|
343
|
+
|
|
344
|
+
if h == -1:
|
|
345
|
+
return -2
|
|
346
|
+
return h
|
|
347
|
+
|
|
348
|
+
# this exists for compatibility with matrix_modn_dense
|
|
349
|
+
cdef void set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value) noexcept:
|
|
350
|
+
"""
|
|
351
|
+
Set the (i,j) entry of ``self`` to the int value.
|
|
352
|
+
"""
|
|
353
|
+
mzd_write_bit(self._entries, i, j, int(value))
|
|
354
|
+
|
|
355
|
+
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
|
|
356
|
+
mzd_write_bit(self._entries, i, j, int(value))
|
|
357
|
+
|
|
358
|
+
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
|
|
359
|
+
if mzd_read_bit(self._entries, i, j):
|
|
360
|
+
return self._one
|
|
361
|
+
else:
|
|
362
|
+
return self._zero
|
|
363
|
+
|
|
364
|
+
def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None,
|
|
365
|
+
*, unicode=False, shape=None, character_art=False,
|
|
366
|
+
left_border=None, right_border=None,
|
|
367
|
+
top_border=None, bottom_border=None):
|
|
368
|
+
r"""
|
|
369
|
+
Return a nice string representation of the matrix.
|
|
370
|
+
|
|
371
|
+
INPUT:
|
|
372
|
+
|
|
373
|
+
- ``rep_mapping`` -- dictionary or callable used to override
|
|
374
|
+
the usual representation of elements. For a dictionary,
|
|
375
|
+
keys should be elements of the base ring and values the
|
|
376
|
+
desired string representation.
|
|
377
|
+
|
|
378
|
+
- ``zero`` -- string (default: ``None``); if not ``None`` use
|
|
379
|
+
the value of ``zero`` as the representation of the zero
|
|
380
|
+
element.
|
|
381
|
+
|
|
382
|
+
- ``plus_one`` -- string (default: ``None``); if not ``None``
|
|
383
|
+
use the value of ``plus_one`` as the representation of the
|
|
384
|
+
one element.
|
|
385
|
+
|
|
386
|
+
- ``minus_one`` -- ignored. Only for compatibility with
|
|
387
|
+
generic matrices.
|
|
388
|
+
|
|
389
|
+
- ``unicode`` -- boolean (default: ``False``);
|
|
390
|
+
whether to use Unicode symbols instead of ASCII symbols
|
|
391
|
+
for brackets and subdivision lines
|
|
392
|
+
|
|
393
|
+
- ``shape`` -- one of ``'square'`` or ``'round'`` (default: ``None``).
|
|
394
|
+
Switches between round and square brackets.
|
|
395
|
+
The default depends on the setting of the ``unicode`` keyword
|
|
396
|
+
argument. For Unicode symbols, the default is round brackets
|
|
397
|
+
in accordance with the TeX rendering,
|
|
398
|
+
while the ASCII rendering defaults to square brackets.
|
|
399
|
+
|
|
400
|
+
- ``character_art`` -- boolean (default: ``False``); if ``True``, the
|
|
401
|
+
result will be of type :class:`~sage.typeset.ascii_art.AsciiArt` or
|
|
402
|
+
:class:`~sage.typeset.unicode_art.UnicodeArt` which support line
|
|
403
|
+
breaking of wide matrices that exceed the window width
|
|
404
|
+
|
|
405
|
+
- ``left_border``, ``right_border`` -- sequence (default: ``None``);
|
|
406
|
+
if not ``None``, call :func:`str` on the elements and use the
|
|
407
|
+
results as labels for the rows of the matrix. The labels appear
|
|
408
|
+
outside of the parentheses.
|
|
409
|
+
|
|
410
|
+
- ``top_border``, ``bottom_border`` -- sequence (default: ``None``);
|
|
411
|
+
if not ``None``, call :func:`str` on the elements and use the
|
|
412
|
+
results as labels for the columns of the matrix. The labels appear
|
|
413
|
+
outside of the parentheses.
|
|
414
|
+
|
|
415
|
+
EXAMPLES::
|
|
416
|
+
|
|
417
|
+
sage: B = matrix(GF(2), 3, 3, [0, 1, 0, 0, 1, 1, 0, 0, 0])
|
|
418
|
+
sage: B # indirect doctest
|
|
419
|
+
[0 1 0]
|
|
420
|
+
[0 1 1]
|
|
421
|
+
[0 0 0]
|
|
422
|
+
sage: block_matrix([[B, 1], [0, B]])
|
|
423
|
+
[0 1 0|1 0 0]
|
|
424
|
+
[0 1 1|0 1 0]
|
|
425
|
+
[0 0 0|0 0 1]
|
|
426
|
+
[-----+-----]
|
|
427
|
+
[0 0 0|0 1 0]
|
|
428
|
+
[0 0 0|0 1 1]
|
|
429
|
+
[0 0 0|0 0 0]
|
|
430
|
+
sage: B.str(zero='.')
|
|
431
|
+
'[. 1 .]\n[. 1 1]\n[. . .]'
|
|
432
|
+
|
|
433
|
+
sage: M = matrix.identity(GF(2), 3)
|
|
434
|
+
sage: M.subdivide(None, 2)
|
|
435
|
+
sage: print(M.str(unicode=True, shape='square'))
|
|
436
|
+
⎡1 0│0⎤
|
|
437
|
+
⎢0 1│0⎥
|
|
438
|
+
⎣0 0│1⎦
|
|
439
|
+
sage: print(unicode_art(M)) # indirect doctest
|
|
440
|
+
⎛1 0│0⎞
|
|
441
|
+
⎜0 1│0⎟
|
|
442
|
+
⎝0 0│1⎠
|
|
443
|
+
"""
|
|
444
|
+
# Set the mapping based on keyword arguments
|
|
445
|
+
# We ignore minus_one (it's only there for compatibility with Matrix)
|
|
446
|
+
if (rep_mapping is not None or zero is not None or plus_one is not None
|
|
447
|
+
or unicode or shape is not None or character_art
|
|
448
|
+
or left_border is not None or right_border is not None
|
|
449
|
+
or top_border is not None or bottom_border is not None):
|
|
450
|
+
# Shunt mappings off to the generic code since they might not be
|
|
451
|
+
# single characters
|
|
452
|
+
return matrix_dense.Matrix_dense.str(self, rep_mapping=rep_mapping,
|
|
453
|
+
zero=zero, plus_one=plus_one,
|
|
454
|
+
unicode=unicode, shape=shape,
|
|
455
|
+
character_art=character_art,
|
|
456
|
+
left_border=left_border,
|
|
457
|
+
right_border=right_border,
|
|
458
|
+
top_border=top_border,
|
|
459
|
+
bottom_border=bottom_border)
|
|
460
|
+
|
|
461
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
462
|
+
return "[]"
|
|
463
|
+
|
|
464
|
+
cdef Py_ssize_t i,j, last_i
|
|
465
|
+
cdef list s = []
|
|
466
|
+
empty_row = b' '*(self._ncols*2-1)
|
|
467
|
+
cdef char *row_s
|
|
468
|
+
cdef char *div_s
|
|
469
|
+
|
|
470
|
+
cdef list row_div, col_div
|
|
471
|
+
if self._subdivisions is not None:
|
|
472
|
+
row_s = empty_row
|
|
473
|
+
div_s = row_divider = b'[' + (b'-' * (self._ncols*2-1)) + b']'
|
|
474
|
+
row_div, col_div = self.subdivisions()
|
|
475
|
+
last_i = 0
|
|
476
|
+
for i in col_div:
|
|
477
|
+
if i == last_i or i == self._ncols:
|
|
478
|
+
# Adjacent column divisions messy, use generic code
|
|
479
|
+
return matrix_dense.Matrix_dense.str(self, rep_mapping)
|
|
480
|
+
row_s[2*i-1] = c'|'
|
|
481
|
+
div_s[2*i] = c'+'
|
|
482
|
+
last_i = i
|
|
483
|
+
|
|
484
|
+
for i in range(self._nrows):
|
|
485
|
+
row_s = row = b'[' + empty_row + b']'
|
|
486
|
+
for j in range(self._ncols):
|
|
487
|
+
row_s[1+2*j] = c'0' + mzd_read_bit(self._entries, i, j)
|
|
488
|
+
s.append(row)
|
|
489
|
+
|
|
490
|
+
if self._subdivisions is not None:
|
|
491
|
+
for i in reversed(row_div):
|
|
492
|
+
s.insert(i, row_divider)
|
|
493
|
+
|
|
494
|
+
return (b"\n".join(s)).decode()
|
|
495
|
+
|
|
496
|
+
def row(self, Py_ssize_t i, from_list=False):
|
|
497
|
+
"""
|
|
498
|
+
Return the ``i``-th row of this matrix as a vector.
|
|
499
|
+
|
|
500
|
+
This row is a dense vector if and only if the matrix is a dense
|
|
501
|
+
matrix.
|
|
502
|
+
|
|
503
|
+
INPUT:
|
|
504
|
+
|
|
505
|
+
- ``i`` -- integer
|
|
506
|
+
|
|
507
|
+
- ``from_list`` -- boolean (default: ``False``); if ``True``,
|
|
508
|
+
returns the ``i``-th element of ``self.rows()`` (see
|
|
509
|
+
:func:`rows`), which may be faster, but requires building a
|
|
510
|
+
list of all rows the first time it is called after an entry
|
|
511
|
+
of the matrix is changed.
|
|
512
|
+
|
|
513
|
+
EXAMPLES::
|
|
514
|
+
|
|
515
|
+
sage: l = [GF(2).random_element() for _ in range(100)]
|
|
516
|
+
sage: A = matrix(GF(2), 10, 10 , l)
|
|
517
|
+
sage: list(A.row(0)) == l[:10]
|
|
518
|
+
True
|
|
519
|
+
sage: list(A.row(-1)) == l[-10:]
|
|
520
|
+
True
|
|
521
|
+
|
|
522
|
+
sage: list(A.row(2, from_list=True)) == l[20:30]
|
|
523
|
+
True
|
|
524
|
+
|
|
525
|
+
sage: A = Matrix(GF(2),1,0)
|
|
526
|
+
sage: A.row(0)
|
|
527
|
+
()
|
|
528
|
+
"""
|
|
529
|
+
if self._nrows == 0:
|
|
530
|
+
raise IndexError("matrix has no rows")
|
|
531
|
+
if i >= self._nrows or i < -self._nrows:
|
|
532
|
+
raise IndexError("row index out of range")
|
|
533
|
+
if i < 0:
|
|
534
|
+
i = i + self._nrows
|
|
535
|
+
if from_list:
|
|
536
|
+
return self.rows(copy=False)[i]
|
|
537
|
+
cdef Vector_mod2_dense z = Vector_mod2_dense.__new__(Vector_mod2_dense)
|
|
538
|
+
global VectorSpace
|
|
539
|
+
if VectorSpace is None:
|
|
540
|
+
from sage.modules.free_module import VectorSpace
|
|
541
|
+
z._init(self._ncols, VectorSpace(self.base_ring(),self._ncols))
|
|
542
|
+
if self._ncols:
|
|
543
|
+
mzd_submatrix(z._entries, self._entries, i, 0, i+1, self._ncols)
|
|
544
|
+
return z
|
|
545
|
+
|
|
546
|
+
def columns(self, copy=True):
|
|
547
|
+
"""
|
|
548
|
+
Return list of the columns of ``self``.
|
|
549
|
+
|
|
550
|
+
INPUT:
|
|
551
|
+
|
|
552
|
+
- ``copy`` -- (default: ``True``) if True, return a copy so you can
|
|
553
|
+
modify it safely
|
|
554
|
+
|
|
555
|
+
EXAMPLES:
|
|
556
|
+
|
|
557
|
+
An example with a small 3x3 matrix::
|
|
558
|
+
|
|
559
|
+
sage: M2 = Matrix(GF(2), [[1, 0, 0], [0, 1, 0], [0, 1, 1]])
|
|
560
|
+
sage: M2.columns()
|
|
561
|
+
[(1, 0, 0), (0, 1, 1), (0, 0, 1)]
|
|
562
|
+
"""
|
|
563
|
+
x = self.fetch('columns')
|
|
564
|
+
if x is not None:
|
|
565
|
+
if copy: return list(x)
|
|
566
|
+
return x
|
|
567
|
+
cdef Py_ssize_t i
|
|
568
|
+
|
|
569
|
+
# Note: due to the way M4ri represents values, extracting rows
|
|
570
|
+
# is fast, but columns are slow. Therefore we transpose
|
|
571
|
+
# then take rows. For more information, see the issue
|
|
572
|
+
# https://github.com/sagemath/sage/issues/38150
|
|
573
|
+
C = self.transpose().rows()
|
|
574
|
+
|
|
575
|
+
# Make the vectors immutable since we are caching them
|
|
576
|
+
for x in C:
|
|
577
|
+
x.set_immutable()
|
|
578
|
+
|
|
579
|
+
# cache result
|
|
580
|
+
self.cache('columns', C)
|
|
581
|
+
if copy:
|
|
582
|
+
return list(C)
|
|
583
|
+
return C
|
|
584
|
+
|
|
585
|
+
########################################################################
|
|
586
|
+
# LEVEL 2 functionality
|
|
587
|
+
# * def _pickle
|
|
588
|
+
# * def _unpickle
|
|
589
|
+
# * cdef _mul_
|
|
590
|
+
# * cpdef _richcmp_
|
|
591
|
+
# * _list -- list of underlying elements (need not be a copy)
|
|
592
|
+
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
|
|
593
|
+
########################################################################
|
|
594
|
+
# def _pickle(self):
|
|
595
|
+
# def _unpickle(self, data, int version): # use version >= 0
|
|
596
|
+
|
|
597
|
+
cpdef _add_(self, right):
|
|
598
|
+
"""
|
|
599
|
+
Matrix addition.
|
|
600
|
+
|
|
601
|
+
INPUT:
|
|
602
|
+
|
|
603
|
+
- ``right`` -- matrix of dimension self.nrows() x self.ncols()
|
|
604
|
+
|
|
605
|
+
EXAMPLES::
|
|
606
|
+
|
|
607
|
+
sage: A = random_matrix(GF(2),10,10)
|
|
608
|
+
sage: A + A == Matrix(GF(2),10,10,0)
|
|
609
|
+
True
|
|
610
|
+
|
|
611
|
+
sage: A = random_matrix(GF(2),257,253)
|
|
612
|
+
sage: A + A == Matrix(GF(2),257,253,0) # indirect doctest
|
|
613
|
+
True
|
|
614
|
+
|
|
615
|
+
TESTS::
|
|
616
|
+
|
|
617
|
+
sage: A = matrix(GF(2),2,0)
|
|
618
|
+
sage: A+A
|
|
619
|
+
[]
|
|
620
|
+
sage: A = matrix(GF(2),0,2)
|
|
621
|
+
sage: A+A
|
|
622
|
+
[]
|
|
623
|
+
sage: A = matrix(GF(2),0,0)
|
|
624
|
+
sage: A+A
|
|
625
|
+
[]
|
|
626
|
+
"""
|
|
627
|
+
cdef Matrix_mod2_dense A
|
|
628
|
+
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc=False)
|
|
629
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
630
|
+
return A
|
|
631
|
+
A._entries = mzd_add(A._entries, self._entries,(<Matrix_mod2_dense>right)._entries)
|
|
632
|
+
|
|
633
|
+
return A
|
|
634
|
+
|
|
635
|
+
cpdef _sub_(self, right):
|
|
636
|
+
"""
|
|
637
|
+
Matrix addition.
|
|
638
|
+
|
|
639
|
+
INPUT:
|
|
640
|
+
|
|
641
|
+
- ``right`` -- matrix of dimension self.nrows() x self.ncols()
|
|
642
|
+
|
|
643
|
+
EXAMPLES::
|
|
644
|
+
|
|
645
|
+
sage: A = random_matrix(GF(2),10,10)
|
|
646
|
+
sage: A - A == Matrix(GF(2),10,10,0) # indirect doctest
|
|
647
|
+
True
|
|
648
|
+
"""
|
|
649
|
+
return self._add_(right)
|
|
650
|
+
|
|
651
|
+
cdef _matrix_times_vector_(self, Vector v):
|
|
652
|
+
"""
|
|
653
|
+
EXAMPLES::
|
|
654
|
+
|
|
655
|
+
sage: A = random_matrix(GF(2),10^4,10^4)
|
|
656
|
+
sage: v0 = random_matrix(GF(2),10^4,1)
|
|
657
|
+
sage: v1 = v0.column(0)
|
|
658
|
+
sage: r0 = A*v0
|
|
659
|
+
sage: r1 = A*v1
|
|
660
|
+
sage: r0.column(0) == r1
|
|
661
|
+
True
|
|
662
|
+
|
|
663
|
+
TESTS:
|
|
664
|
+
|
|
665
|
+
Check that :issue:`19378` is fixed::
|
|
666
|
+
|
|
667
|
+
sage: m = matrix(GF(2), 11, 0)
|
|
668
|
+
sage: v = vector(GF(2), 0)
|
|
669
|
+
sage: m * v
|
|
670
|
+
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
|
671
|
+
|
|
672
|
+
Add a test involving a nonsquare matrix::
|
|
673
|
+
|
|
674
|
+
sage: A = random_matrix(GF(2),10^4,10^3)
|
|
675
|
+
sage: v0 = random_matrix(GF(2),10^3,1)
|
|
676
|
+
sage: v1 = v0.column(0)
|
|
677
|
+
sage: r0 = A*v0
|
|
678
|
+
sage: r1 = A*v1
|
|
679
|
+
sage: r0.column(0) == r1
|
|
680
|
+
True
|
|
681
|
+
|
|
682
|
+
Check that :issue:`40167` is fixed::
|
|
683
|
+
|
|
684
|
+
sage: M = matrix(GF(2), [[1,1],[0,1]])
|
|
685
|
+
sage: v = vector(GF(2), [0, 1])
|
|
686
|
+
sage: V = span([v]) # one-dimensional subspace of GF(2)^2
|
|
687
|
+
sage: image_basis = [M * b for b in V.basis()]
|
|
688
|
+
sage: image = span(image_basis)
|
|
689
|
+
sage: image_basis[0] in image.basis()
|
|
690
|
+
True
|
|
691
|
+
"""
|
|
692
|
+
cdef mzd_t *tmp
|
|
693
|
+
if (
|
|
694
|
+
self._nrows == self._ncols and
|
|
695
|
+
isinstance(v, Vector_mod2_dense) and
|
|
696
|
+
v.parent().rank() == self._ncols # check if the parent of v is full rank
|
|
697
|
+
):
|
|
698
|
+
VS = v.parent()
|
|
699
|
+
else:
|
|
700
|
+
global VectorSpace
|
|
701
|
+
if VectorSpace is None:
|
|
702
|
+
from sage.modules.free_module import VectorSpace
|
|
703
|
+
VS = VectorSpace(self._base_ring, self._nrows)
|
|
704
|
+
if not isinstance(v, Vector_mod2_dense):
|
|
705
|
+
v = VS(v)
|
|
706
|
+
if self.ncols() != v.degree():
|
|
707
|
+
raise ArithmeticError("number of columns of matrix must equal degree of vector")
|
|
708
|
+
|
|
709
|
+
# If the vector is 0-dimensional, the result will be the 0-vector
|
|
710
|
+
if not self.ncols():
|
|
711
|
+
return VS.zero()
|
|
712
|
+
cdef Vector_mod2_dense c = Vector_mod2_dense.__new__(Vector_mod2_dense)
|
|
713
|
+
sig_str("matrix allocation failed")
|
|
714
|
+
c._init(self._nrows, VS)
|
|
715
|
+
if c._entries.nrows and c._entries.ncols:
|
|
716
|
+
tmp = mzd_init(self._nrows, 1)
|
|
717
|
+
_mzd_mul_naive(tmp, self._entries, (<Vector_mod2_dense>v)._entries, 0)
|
|
718
|
+
mzd_transpose(c._entries, tmp)
|
|
719
|
+
mzd_free(tmp)
|
|
720
|
+
sig_off()
|
|
721
|
+
return c
|
|
722
|
+
|
|
723
|
+
cdef _matrix_times_matrix_(self, Matrix right):
|
|
724
|
+
"""
|
|
725
|
+
Matrix multiplication.
|
|
726
|
+
|
|
727
|
+
ALGORITHM: Uses the 'Method of the Four Russians
|
|
728
|
+
Multiplication', see :func:`_multiply_m4rm`.
|
|
729
|
+
"""
|
|
730
|
+
if get_verbose() >= 2:
|
|
731
|
+
verbose('matrix multiply of %s x %s matrix by %s x %s matrix' % (
|
|
732
|
+
self._nrows, self._ncols, right._nrows, right._ncols))
|
|
733
|
+
|
|
734
|
+
return self._multiply_strassen(right, 0)
|
|
735
|
+
|
|
736
|
+
cpdef Matrix_mod2_dense _multiply_m4rm(Matrix_mod2_dense self, Matrix_mod2_dense right, int k):
|
|
737
|
+
"""
|
|
738
|
+
Multiply matrices using the 'Method of the Four Russians
|
|
739
|
+
Multiplication' (M4RM) or Konrod's method.
|
|
740
|
+
|
|
741
|
+
The algorithm is based on an algorithm by Arlazarov, Dinic,
|
|
742
|
+
Kronrod, and Faradzev [ADKF1970]_ and appeared in [AHU1974]_. This
|
|
743
|
+
implementation is based on a description given in Gregory
|
|
744
|
+
Bard's 'Method of the Four Russians Inversion' paper [Bar06]_.
|
|
745
|
+
|
|
746
|
+
INPUT:
|
|
747
|
+
|
|
748
|
+
- ``right`` -- Matrix
|
|
749
|
+
- ``k`` -- parameter `k` for the Gray Code table size. If `k=0` a suitable
|
|
750
|
+
value is chosen by the function. (`0<= k <= 16`, default: 0)
|
|
751
|
+
|
|
752
|
+
EXAMPLES::
|
|
753
|
+
|
|
754
|
+
sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )
|
|
755
|
+
sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )
|
|
756
|
+
sage: A
|
|
757
|
+
[0 0 0]
|
|
758
|
+
[0 1 0]
|
|
759
|
+
[0 1 1]
|
|
760
|
+
[0 0 1]
|
|
761
|
+
sage: B
|
|
762
|
+
[0 0 1 0]
|
|
763
|
+
[1 0 0 1]
|
|
764
|
+
[1 1 0 0]
|
|
765
|
+
sage: A._multiply_m4rm(B, 0)
|
|
766
|
+
[0 0 0 0]
|
|
767
|
+
[1 0 0 1]
|
|
768
|
+
[0 1 0 1]
|
|
769
|
+
[1 1 0 0]
|
|
770
|
+
|
|
771
|
+
TESTS::
|
|
772
|
+
|
|
773
|
+
sage: A = random_matrix(GF(2),0,0)
|
|
774
|
+
sage: B = random_matrix(GF(2),0,0)
|
|
775
|
+
sage: A._multiply_m4rm(B, 0)
|
|
776
|
+
[]
|
|
777
|
+
sage: A = random_matrix(GF(2),3,0)
|
|
778
|
+
sage: B = random_matrix(GF(2),0,3)
|
|
779
|
+
sage: A._multiply_m4rm(B, 0)
|
|
780
|
+
[0 0 0]
|
|
781
|
+
[0 0 0]
|
|
782
|
+
[0 0 0]
|
|
783
|
+
sage: A = random_matrix(GF(2),0,3)
|
|
784
|
+
sage: B = random_matrix(GF(2),3,0)
|
|
785
|
+
sage: A._multiply_m4rm(B, 0)
|
|
786
|
+
[]
|
|
787
|
+
|
|
788
|
+
ALGORITHM: Uses the 'Method of the Four Russians'
|
|
789
|
+
multiplication as implemented in the M4RI library.
|
|
790
|
+
|
|
791
|
+
REFERENCES:
|
|
792
|
+
|
|
793
|
+
- [AHU1974]_
|
|
794
|
+
- [AKF1970]_
|
|
795
|
+
- [Bar2006]_
|
|
796
|
+
"""
|
|
797
|
+
if self._ncols != right._nrows:
|
|
798
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
799
|
+
|
|
800
|
+
if get_verbose() >= 2:
|
|
801
|
+
verbose('m4rm multiply of %s x %s matrix by %s x %s matrix' % (
|
|
802
|
+
self._nrows, self._ncols, right._nrows, right._ncols))
|
|
803
|
+
|
|
804
|
+
cdef Matrix_mod2_dense ans
|
|
805
|
+
|
|
806
|
+
ans = self.new_matrix(nrows = self._nrows, ncols = right._ncols)
|
|
807
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
808
|
+
return ans
|
|
809
|
+
sig_on()
|
|
810
|
+
ans._entries = mzd_mul_m4rm(ans._entries, self._entries, right._entries, k)
|
|
811
|
+
sig_off()
|
|
812
|
+
return ans
|
|
813
|
+
|
|
814
|
+
def _multiply_classical(Matrix_mod2_dense self, Matrix_mod2_dense right):
|
|
815
|
+
"""
|
|
816
|
+
Classical `O(n^3)` multiplication.
|
|
817
|
+
|
|
818
|
+
This can be quite fast for matrix vector multiplication but
|
|
819
|
+
the other routines fall back to this implementation in that
|
|
820
|
+
case anyway.
|
|
821
|
+
|
|
822
|
+
EXAMPLES::
|
|
823
|
+
|
|
824
|
+
sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )
|
|
825
|
+
sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )
|
|
826
|
+
sage: A
|
|
827
|
+
[0 0 0]
|
|
828
|
+
[0 1 0]
|
|
829
|
+
[0 1 1]
|
|
830
|
+
[0 0 1]
|
|
831
|
+
sage: B
|
|
832
|
+
[0 0 1 0]
|
|
833
|
+
[1 0 0 1]
|
|
834
|
+
[1 1 0 0]
|
|
835
|
+
sage: A._multiply_classical(B)
|
|
836
|
+
[0 0 0 0]
|
|
837
|
+
[1 0 0 1]
|
|
838
|
+
[0 1 0 1]
|
|
839
|
+
[1 1 0 0]
|
|
840
|
+
|
|
841
|
+
TESTS::
|
|
842
|
+
|
|
843
|
+
sage: A = random_matrix(GF(2),0,0)
|
|
844
|
+
sage: B = random_matrix(GF(2),0,0)
|
|
845
|
+
sage: A._multiply_classical(B)
|
|
846
|
+
[]
|
|
847
|
+
sage: A = random_matrix(GF(2),3,0)
|
|
848
|
+
sage: B = random_matrix(GF(2),0,3)
|
|
849
|
+
sage: A._multiply_classical(B)
|
|
850
|
+
[0 0 0]
|
|
851
|
+
[0 0 0]
|
|
852
|
+
[0 0 0]
|
|
853
|
+
sage: A = random_matrix(GF(2),0,3)
|
|
854
|
+
sage: B = random_matrix(GF(2),3,0)
|
|
855
|
+
sage: A._multiply_classical(B)
|
|
856
|
+
[]
|
|
857
|
+
"""
|
|
858
|
+
cdef Matrix_mod2_dense A
|
|
859
|
+
A = self.new_matrix(nrows = self._nrows, ncols = right._ncols)
|
|
860
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
861
|
+
return A
|
|
862
|
+
A._entries = mzd_mul_naive(A._entries, self._entries,(<Matrix_mod2_dense>right)._entries)
|
|
863
|
+
return A
|
|
864
|
+
|
|
865
|
+
cpdef Matrix_mod2_dense _multiply_strassen(Matrix_mod2_dense self, Matrix_mod2_dense right, int cutoff):
|
|
866
|
+
r"""
|
|
867
|
+
Strassen-Winograd `O(n^{2.807})` multiplication [Str1969]_.
|
|
868
|
+
|
|
869
|
+
This implementation in M4RI is inspired by Sage's generic
|
|
870
|
+
Strassen implementation [BHS2008]_ but uses a more memory
|
|
871
|
+
efficient operation schedule [DP2008]_.
|
|
872
|
+
|
|
873
|
+
The performance of this routine depends on the parameter
|
|
874
|
+
cutoff. On many modern machines 2048 should give acceptable
|
|
875
|
+
performance, a good rule of thumb for calculating the optimal
|
|
876
|
+
cutoff would that two matrices of the cutoff size should fit
|
|
877
|
+
in L2 cache, so: `cutoff = \sqrt{L2 * 8 * 1024^2 / 2}` where
|
|
878
|
+
`L2` is the size of the L2 cache in MB.
|
|
879
|
+
|
|
880
|
+
INPUT:
|
|
881
|
+
|
|
882
|
+
- ``right`` -- a matrix of matching dimensions
|
|
883
|
+
- ``cutoff`` -- matrix dimension where M4RM should be used
|
|
884
|
+
instead of Strassen (default: let M4RI decide)
|
|
885
|
+
|
|
886
|
+
EXAMPLES::
|
|
887
|
+
|
|
888
|
+
sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )
|
|
889
|
+
sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )
|
|
890
|
+
sage: A
|
|
891
|
+
[0 0 0]
|
|
892
|
+
[0 1 0]
|
|
893
|
+
[0 1 1]
|
|
894
|
+
[0 0 1]
|
|
895
|
+
sage: B
|
|
896
|
+
[0 0 1 0]
|
|
897
|
+
[1 0 0 1]
|
|
898
|
+
[1 1 0 0]
|
|
899
|
+
sage: A._multiply_strassen(B, 0)
|
|
900
|
+
[0 0 0 0]
|
|
901
|
+
[1 0 0 1]
|
|
902
|
+
[0 1 0 1]
|
|
903
|
+
[1 1 0 0]
|
|
904
|
+
sage: A = random_matrix(GF(2),2701,3000)
|
|
905
|
+
sage: B = random_matrix(GF(2),3000,3172)
|
|
906
|
+
sage: A._multiply_strassen(B, 256) == A._multiply_m4rm(B, 0) # long time
|
|
907
|
+
True
|
|
908
|
+
|
|
909
|
+
TESTS::
|
|
910
|
+
|
|
911
|
+
sage: A = random_matrix(GF(2),0,0)
|
|
912
|
+
sage: B = random_matrix(GF(2),0,0)
|
|
913
|
+
sage: A._multiply_strassen(B, 0)
|
|
914
|
+
[]
|
|
915
|
+
sage: A = random_matrix(GF(2),3,0)
|
|
916
|
+
sage: B = random_matrix(GF(2),0,3)
|
|
917
|
+
sage: A._multiply_strassen(B, 0)
|
|
918
|
+
[0 0 0]
|
|
919
|
+
[0 0 0]
|
|
920
|
+
[0 0 0]
|
|
921
|
+
sage: A = random_matrix(GF(2),0,3)
|
|
922
|
+
sage: B = random_matrix(GF(2),3,0)
|
|
923
|
+
sage: A._multiply_strassen(B, 0)
|
|
924
|
+
[]
|
|
925
|
+
|
|
926
|
+
ALGORITHM: Uses Strassen-Winograd matrix multiplication with
|
|
927
|
+
M4RM as base case as implemented in the M4RI library.
|
|
928
|
+
"""
|
|
929
|
+
if self._ncols != right._nrows:
|
|
930
|
+
raise ArithmeticError("left ncols must match right nrows")
|
|
931
|
+
|
|
932
|
+
cdef Matrix_mod2_dense ans
|
|
933
|
+
#ans = self.new_matrix(nrows = self._nrows, ncols = right._ncols)
|
|
934
|
+
# The following is a little faster:
|
|
935
|
+
ans = self.matrix_space(self._nrows, right._ncols, sparse=False).zero_matrix().__copy__()
|
|
936
|
+
if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:
|
|
937
|
+
return ans
|
|
938
|
+
|
|
939
|
+
sig_on()
|
|
940
|
+
ans._entries = mzd_mul(ans._entries, self._entries, right._entries, cutoff)
|
|
941
|
+
sig_off()
|
|
942
|
+
return ans
|
|
943
|
+
|
|
944
|
+
def __neg__(self):
|
|
945
|
+
"""
|
|
946
|
+
EXAMPLES::
|
|
947
|
+
|
|
948
|
+
sage: A = random_matrix(GF(2),100,100)
|
|
949
|
+
sage: A - A == A - -A
|
|
950
|
+
True
|
|
951
|
+
"""
|
|
952
|
+
return self.__copy__()
|
|
953
|
+
|
|
954
|
+
def __invert__(self):
|
|
955
|
+
"""
|
|
956
|
+
Invert ``self`` using the 'Method of the Four Russians'
|
|
957
|
+
inversion.
|
|
958
|
+
|
|
959
|
+
If ``self`` is not invertible a :exc:`ZeroDivisionError` is
|
|
960
|
+
raised.
|
|
961
|
+
|
|
962
|
+
EXAMPLES::
|
|
963
|
+
|
|
964
|
+
sage: A = Matrix(GF(2),3,3, [0, 0, 1, 0, 1, 1, 1, 0, 1])
|
|
965
|
+
sage: MS = A.parent()
|
|
966
|
+
sage: A
|
|
967
|
+
[0 0 1]
|
|
968
|
+
[0 1 1]
|
|
969
|
+
[1 0 1]
|
|
970
|
+
sage: ~A
|
|
971
|
+
[1 0 1]
|
|
972
|
+
[1 1 0]
|
|
973
|
+
[1 0 0]
|
|
974
|
+
sage: A * ~A == ~A * A == MS(1)
|
|
975
|
+
True
|
|
976
|
+
|
|
977
|
+
TESTS::
|
|
978
|
+
|
|
979
|
+
sage: A = matrix(GF(2),0,0)
|
|
980
|
+
sage: A^(-1)
|
|
981
|
+
[]
|
|
982
|
+
"""
|
|
983
|
+
cdef Matrix_mod2_dense A
|
|
984
|
+
|
|
985
|
+
if self._nrows != self._ncols:
|
|
986
|
+
raise ArithmeticError("self must be a square matrix")
|
|
987
|
+
|
|
988
|
+
if self._ncols == 0:
|
|
989
|
+
return self.__copy__()
|
|
990
|
+
|
|
991
|
+
if self.rank() != self._nrows:
|
|
992
|
+
raise ZeroDivisionError("Matrix does not have full rank.")
|
|
993
|
+
|
|
994
|
+
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc = False)
|
|
995
|
+
sig_on()
|
|
996
|
+
A._entries = mzd_inv_m4ri(A._entries, self._entries, 0)
|
|
997
|
+
sig_off()
|
|
998
|
+
|
|
999
|
+
if A._entries==NULL:
|
|
1000
|
+
raise ZeroDivisionError("input matrix must be nonsingular")
|
|
1001
|
+
else:
|
|
1002
|
+
return A
|
|
1003
|
+
|
|
1004
|
+
def __copy__(self):
|
|
1005
|
+
"""
|
|
1006
|
+
Return a copy of ``self``.
|
|
1007
|
+
|
|
1008
|
+
EXAMPLES::
|
|
1009
|
+
|
|
1010
|
+
sage: MS = MatrixSpace(GF(2),3,3)
|
|
1011
|
+
sage: A = MS(1)
|
|
1012
|
+
sage: A.__copy__() == A
|
|
1013
|
+
True
|
|
1014
|
+
sage: A.__copy__() is A
|
|
1015
|
+
False
|
|
1016
|
+
|
|
1017
|
+
sage: A = random_matrix(GF(2),100,100)
|
|
1018
|
+
sage: A.__copy__() == A
|
|
1019
|
+
True
|
|
1020
|
+
sage: A.__copy__() is A
|
|
1021
|
+
False
|
|
1022
|
+
|
|
1023
|
+
sage: A.echelonize()
|
|
1024
|
+
sage: A.__copy__() == A
|
|
1025
|
+
True
|
|
1026
|
+
"""
|
|
1027
|
+
cdef Matrix_mod2_dense A
|
|
1028
|
+
A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0)
|
|
1029
|
+
|
|
1030
|
+
if self._nrows and self._ncols:
|
|
1031
|
+
mzd_copy(A._entries, self._entries)
|
|
1032
|
+
|
|
1033
|
+
if self._subdivisions is not None:
|
|
1034
|
+
A.subdivide(*self.subdivisions())
|
|
1035
|
+
|
|
1036
|
+
return A
|
|
1037
|
+
|
|
1038
|
+
def _list(self):
|
|
1039
|
+
"""
|
|
1040
|
+
Return list of the elements of ``self`` in row major
|
|
1041
|
+
ordering.
|
|
1042
|
+
|
|
1043
|
+
EXAMPLES::
|
|
1044
|
+
|
|
1045
|
+
sage: A = Matrix(GF(2),2,2,[1,0,1,1])
|
|
1046
|
+
sage: A
|
|
1047
|
+
[1 0]
|
|
1048
|
+
[1 1]
|
|
1049
|
+
sage: A.list() #indirect doctest
|
|
1050
|
+
[1, 0, 1, 1]
|
|
1051
|
+
|
|
1052
|
+
TESTS::
|
|
1053
|
+
|
|
1054
|
+
sage: A = Matrix(GF(2),3,0)
|
|
1055
|
+
sage: A.list()
|
|
1056
|
+
[]
|
|
1057
|
+
"""
|
|
1058
|
+
cdef Py_ssize_t i,j
|
|
1059
|
+
l = []
|
|
1060
|
+
for i from 0 <= i < self._nrows:
|
|
1061
|
+
for j from 0 <= j < self._ncols:
|
|
1062
|
+
if mzd_read_bit(self._entries,i,j):
|
|
1063
|
+
l.append(self._one)
|
|
1064
|
+
else:
|
|
1065
|
+
l.append(self._zero)
|
|
1066
|
+
return l
|
|
1067
|
+
|
|
1068
|
+
# def _dict(self):
|
|
1069
|
+
|
|
1070
|
+
########################################################################
|
|
1071
|
+
# LEVEL 3 functionality (Optional)
|
|
1072
|
+
# * __deepcopy__
|
|
1073
|
+
# * Matrix windows -- only if you need strassen for that base
|
|
1074
|
+
########################################################################
|
|
1075
|
+
|
|
1076
|
+
def echelonize(self, algorithm='heuristic', cutoff=0, reduced=True, **kwds):
|
|
1077
|
+
"""
|
|
1078
|
+
Puts ``self`` in (reduced) row echelon form.
|
|
1079
|
+
|
|
1080
|
+
INPUT:
|
|
1081
|
+
|
|
1082
|
+
- ``self`` -- a mutable matrix
|
|
1083
|
+
- ``algorithm`` -- string; one of
|
|
1084
|
+
|
|
1085
|
+
- ``'heuristic'`` -- uses M4RI and PLUQ (default)
|
|
1086
|
+
- ``'m4ri'`` -- uses M4RI
|
|
1087
|
+
- ``'pluq'`` -- uses PLUQ factorization
|
|
1088
|
+
- ``'classical'`` -- uses classical Gaussian elimination
|
|
1089
|
+
|
|
1090
|
+
- ``k`` -- the parameter 'k' of the M4RI algorithm. It MUST be between 1
|
|
1091
|
+
and 16 (inclusive). If it is not specified it will be calculated as
|
|
1092
|
+
3/4 * log_2( min(nrows, ncols) ) as suggested in the M4RI paper.
|
|
1093
|
+
- ``reduced`` -- return reduced row echelon form (default: ``True``)
|
|
1094
|
+
|
|
1095
|
+
EXAMPLES::
|
|
1096
|
+
|
|
1097
|
+
sage: A = random_matrix(GF(2), 10, 10)
|
|
1098
|
+
sage: B = A.__copy__(); B.echelonize() # fastest
|
|
1099
|
+
sage: C = A.__copy__(); C.echelonize(k=2) # force k
|
|
1100
|
+
sage: E = A.__copy__(); E.echelonize(algorithm='classical') # force Gaussian elimination
|
|
1101
|
+
sage: B == C == E
|
|
1102
|
+
True
|
|
1103
|
+
|
|
1104
|
+
TESTS::
|
|
1105
|
+
|
|
1106
|
+
sage: VF2 = VectorSpace(GF(2),2)
|
|
1107
|
+
sage: WF2 = VF2.submodule([VF2([1,1])])
|
|
1108
|
+
sage: WF2
|
|
1109
|
+
Vector space of degree 2 and dimension 1 over Finite Field of size 2
|
|
1110
|
+
Basis matrix:
|
|
1111
|
+
[1 1]
|
|
1112
|
+
|
|
1113
|
+
sage: A2 = matrix(GF(2),2,[1,0,0,1])
|
|
1114
|
+
sage: A2.kernel()
|
|
1115
|
+
Vector space of degree 2 and dimension 0 over Finite Field of size 2
|
|
1116
|
+
Basis matrix:
|
|
1117
|
+
[]
|
|
1118
|
+
|
|
1119
|
+
ALGORITHM:
|
|
1120
|
+
|
|
1121
|
+
Uses M4RI library
|
|
1122
|
+
|
|
1123
|
+
REFERENCES:
|
|
1124
|
+
|
|
1125
|
+
- [Bar2006]_
|
|
1126
|
+
"""
|
|
1127
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
1128
|
+
self.cache('in_echelon_form',True)
|
|
1129
|
+
self.cache('rank', 0)
|
|
1130
|
+
self.cache('pivots', ())
|
|
1131
|
+
return self
|
|
1132
|
+
cdef int k, full
|
|
1133
|
+
|
|
1134
|
+
full = int(reduced)
|
|
1135
|
+
|
|
1136
|
+
x = self.fetch('in_echelon_form')
|
|
1137
|
+
if x is not None:
|
|
1138
|
+
return # already known to be in echelon form
|
|
1139
|
+
|
|
1140
|
+
if algorithm == 'heuristic':
|
|
1141
|
+
|
|
1142
|
+
self.check_mutability()
|
|
1143
|
+
self.clear_cache()
|
|
1144
|
+
|
|
1145
|
+
sig_on()
|
|
1146
|
+
r = mzd_echelonize(self._entries, full)
|
|
1147
|
+
sig_off()
|
|
1148
|
+
|
|
1149
|
+
self.cache('in_echelon_form',True)
|
|
1150
|
+
self.cache('rank', r)
|
|
1151
|
+
self.cache('pivots', tuple(self._pivots()))
|
|
1152
|
+
|
|
1153
|
+
elif algorithm == 'm4ri':
|
|
1154
|
+
|
|
1155
|
+
self.check_mutability()
|
|
1156
|
+
self.clear_cache()
|
|
1157
|
+
|
|
1158
|
+
if 'k' in kwds:
|
|
1159
|
+
k = int(kwds['k'])
|
|
1160
|
+
|
|
1161
|
+
if k < 1 or k > 16:
|
|
1162
|
+
raise RuntimeError("k must be between 1 and 16")
|
|
1163
|
+
k = round(k)
|
|
1164
|
+
else:
|
|
1165
|
+
k = 0
|
|
1166
|
+
|
|
1167
|
+
sig_on()
|
|
1168
|
+
r = mzd_echelonize_m4ri(self._entries, full, k)
|
|
1169
|
+
sig_off()
|
|
1170
|
+
|
|
1171
|
+
self.cache('in_echelon_form',True)
|
|
1172
|
+
self.cache('rank', r)
|
|
1173
|
+
self.cache('pivots', tuple(self._pivots()))
|
|
1174
|
+
|
|
1175
|
+
elif algorithm == 'pluq':
|
|
1176
|
+
|
|
1177
|
+
self.check_mutability()
|
|
1178
|
+
self.clear_cache()
|
|
1179
|
+
|
|
1180
|
+
sig_on()
|
|
1181
|
+
r = mzd_echelonize_pluq(self._entries, full)
|
|
1182
|
+
sig_off()
|
|
1183
|
+
|
|
1184
|
+
self.cache('in_echelon_form',True)
|
|
1185
|
+
self.cache('rank', r)
|
|
1186
|
+
self.cache('pivots', tuple(self._pivots()))
|
|
1187
|
+
|
|
1188
|
+
elif algorithm == 'linbox':
|
|
1189
|
+
|
|
1190
|
+
#self._echelonize_linbox()
|
|
1191
|
+
raise NotImplementedError
|
|
1192
|
+
|
|
1193
|
+
elif algorithm == 'classical':
|
|
1194
|
+
|
|
1195
|
+
# for debugging purposes only, it is slow
|
|
1196
|
+
self._echelon_in_place_classical()
|
|
1197
|
+
else:
|
|
1198
|
+
raise ValueError("no algorithm '%s'" % algorithm)
|
|
1199
|
+
|
|
1200
|
+
def _pivots(self):
|
|
1201
|
+
"""
|
|
1202
|
+
Return the pivot columns of ``self`` if ``self`` is in
|
|
1203
|
+
row echelon form.
|
|
1204
|
+
|
|
1205
|
+
EXAMPLES::
|
|
1206
|
+
|
|
1207
|
+
sage: A = matrix(GF(2),5,5,[0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,1])
|
|
1208
|
+
sage: E = A.echelon_form()
|
|
1209
|
+
sage: E
|
|
1210
|
+
[0 1 0 0 0]
|
|
1211
|
+
[0 0 1 0 0]
|
|
1212
|
+
[0 0 0 1 0]
|
|
1213
|
+
[0 0 0 0 1]
|
|
1214
|
+
[0 0 0 0 0]
|
|
1215
|
+
sage: E._pivots()
|
|
1216
|
+
[1, 2, 3, 4]
|
|
1217
|
+
"""
|
|
1218
|
+
if not self.fetch('in_echelon_form'):
|
|
1219
|
+
raise RuntimeError("self must be in reduced row echelon form first.")
|
|
1220
|
+
pivots = []
|
|
1221
|
+
cdef Py_ssize_t i, j, nc
|
|
1222
|
+
nc = self._ncols
|
|
1223
|
+
i = 0
|
|
1224
|
+
while i < self._nrows:
|
|
1225
|
+
for j from i <= j < nc:
|
|
1226
|
+
if mzd_read_bit(self._entries, i, j):
|
|
1227
|
+
pivots.append(j)
|
|
1228
|
+
i += 1
|
|
1229
|
+
break
|
|
1230
|
+
if j == nc:
|
|
1231
|
+
break
|
|
1232
|
+
return pivots
|
|
1233
|
+
|
|
1234
|
+
def randomize(self, density=1, nonzero=False):
|
|
1235
|
+
"""
|
|
1236
|
+
Randomize ``density`` proportion of the entries of this matrix,
|
|
1237
|
+
leaving the rest unchanged.
|
|
1238
|
+
|
|
1239
|
+
INPUT:
|
|
1240
|
+
|
|
1241
|
+
- ``density`` -- float; proportion (roughly) to be considered for
|
|
1242
|
+
changes
|
|
1243
|
+
- ``nonzero`` -- boolean (default: ``False``); whether the new entries
|
|
1244
|
+
are forced to be nonzero
|
|
1245
|
+
|
|
1246
|
+
OUTPUT: None, the matrix is modified in-space
|
|
1247
|
+
|
|
1248
|
+
EXAMPLES::
|
|
1249
|
+
|
|
1250
|
+
sage: A = matrix(GF(2), 5, 5, 0)
|
|
1251
|
+
sage: A.randomize(0.5)
|
|
1252
|
+
sage: A.density() < 0.5
|
|
1253
|
+
True
|
|
1254
|
+
sage: expected = 0.5
|
|
1255
|
+
sage: A = matrix(GF(2), 5, 5, 0)
|
|
1256
|
+
sage: A.randomize()
|
|
1257
|
+
sage: density_sum = float(A.density())
|
|
1258
|
+
sage: total = 1
|
|
1259
|
+
sage: while abs(density_sum/total - expected) > 0.001:
|
|
1260
|
+
....: A = matrix(GF(2), 5, 5, 0)
|
|
1261
|
+
....: A.randomize()
|
|
1262
|
+
....: density_sum += float(A.density())
|
|
1263
|
+
....: total += 1
|
|
1264
|
+
|
|
1265
|
+
TESTS:
|
|
1266
|
+
|
|
1267
|
+
With the libc random number generator random(), we had problems
|
|
1268
|
+
where the ranks of all of these matrices would be the same
|
|
1269
|
+
(and they would all be far too low). This verifies that the
|
|
1270
|
+
problem is gone, with Mersenne Twister::
|
|
1271
|
+
|
|
1272
|
+
sage: MS2 = MatrixSpace(GF(2), 1000)
|
|
1273
|
+
sage: from collections import defaultdict
|
|
1274
|
+
sage: found = defaultdict(bool)
|
|
1275
|
+
sage: while not all(found[i] for i in range(997, 1001)):
|
|
1276
|
+
....: found[MS2.random_element().rank()] = True
|
|
1277
|
+
|
|
1278
|
+
Testing corner case::
|
|
1279
|
+
|
|
1280
|
+
sage: A = random_matrix(GF(2),3,0)
|
|
1281
|
+
sage: A
|
|
1282
|
+
[]
|
|
1283
|
+
"""
|
|
1284
|
+
if self._ncols == 0 or self._nrows == 0:
|
|
1285
|
+
return
|
|
1286
|
+
|
|
1287
|
+
density = float(density)
|
|
1288
|
+
if density <= 0:
|
|
1289
|
+
return
|
|
1290
|
+
if density > 1:
|
|
1291
|
+
density = float(1)
|
|
1292
|
+
|
|
1293
|
+
self.check_mutability()
|
|
1294
|
+
self.clear_cache()
|
|
1295
|
+
|
|
1296
|
+
cdef randstate rstate = current_randstate()
|
|
1297
|
+
|
|
1298
|
+
cdef Py_ssize_t i, j
|
|
1299
|
+
cdef int k
|
|
1300
|
+
cdef int nc
|
|
1301
|
+
cdef unsigned int low, high
|
|
1302
|
+
cdef m4ri_word mask = 0
|
|
1303
|
+
|
|
1304
|
+
# Original code, before adding the ``nonzero`` option.
|
|
1305
|
+
cdef m4ri_word *row
|
|
1306
|
+
if not nonzero:
|
|
1307
|
+
if density == 1:
|
|
1308
|
+
assert sizeof(m4ri_word) == 8
|
|
1309
|
+
mask = __M4RI_LEFT_BITMASK(self._entries.ncols % m4ri_radix)
|
|
1310
|
+
for i from 0 <= i < self._nrows:
|
|
1311
|
+
row = mzd_row(self._entries, i)
|
|
1312
|
+
for j from 0 <= j < self._entries.width:
|
|
1313
|
+
# for portability we get 32-bit twice rather than 64-bit once
|
|
1314
|
+
low = gmp_urandomb_ui(rstate.gmp_state, 32)
|
|
1315
|
+
high = gmp_urandomb_ui(rstate.gmp_state, 32)
|
|
1316
|
+
row[j] = m4ri_swap_bits( ((<unsigned long long>high)<<32) | (<unsigned long long>low) )
|
|
1317
|
+
row[self._entries.width - 1] &= mask
|
|
1318
|
+
else:
|
|
1319
|
+
nc = self._ncols
|
|
1320
|
+
num_per_row = int(density * nc)
|
|
1321
|
+
sig_on()
|
|
1322
|
+
for i from 0 <= i < self._nrows:
|
|
1323
|
+
for j from 0 <= j < num_per_row:
|
|
1324
|
+
k = rstate.c_random() % nc
|
|
1325
|
+
mzd_write_bit(self._entries, i, k, rstate.c_random() % 2)
|
|
1326
|
+
sig_off()
|
|
1327
|
+
|
|
1328
|
+
# New code for the case when ``nonzero`` is ``True``.
|
|
1329
|
+
else:
|
|
1330
|
+
sig_on()
|
|
1331
|
+
for i from 0 <= i < self._nrows:
|
|
1332
|
+
for j from 0 <= j < self._ncols:
|
|
1333
|
+
if rstate.c_rand_double() <= density:
|
|
1334
|
+
mzd_write_bit(self._entries, i, j, 1)
|
|
1335
|
+
sig_off()
|
|
1336
|
+
|
|
1337
|
+
cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):
|
|
1338
|
+
"""
|
|
1339
|
+
EXAMPLES::
|
|
1340
|
+
|
|
1341
|
+
sage: A = random_matrix(GF(2),3,3)
|
|
1342
|
+
sage: A.rescale_row(0,0,0)
|
|
1343
|
+
sage: A.row(0)
|
|
1344
|
+
(0, 0, 0)
|
|
1345
|
+
"""
|
|
1346
|
+
if not int(multiple) % 2:
|
|
1347
|
+
mzd_row_clear_offset(self._entries, row, start_col)
|
|
1348
|
+
|
|
1349
|
+
cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple,
|
|
1350
|
+
Py_ssize_t start_col):
|
|
1351
|
+
"""
|
|
1352
|
+
EXAMPLES::
|
|
1353
|
+
|
|
1354
|
+
sage: A = random_matrix(GF(2),3,3)
|
|
1355
|
+
sage: B = copy(A)
|
|
1356
|
+
sage: A.add_multiple_of_row(0,1,1,0)
|
|
1357
|
+
sage: A.row(0) == B.row(0) + B.row(1)
|
|
1358
|
+
True
|
|
1359
|
+
"""
|
|
1360
|
+
if int(multiple) % 2:
|
|
1361
|
+
mzd_row_add_offset(self._entries, row_to, row_from, start_col)
|
|
1362
|
+
|
|
1363
|
+
cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):
|
|
1364
|
+
"""
|
|
1365
|
+
EXAMPLES::
|
|
1366
|
+
|
|
1367
|
+
sage: A = random_matrix(GF(2),3,3)
|
|
1368
|
+
sage: B = copy(A)
|
|
1369
|
+
sage: A.swap_rows(0,1)
|
|
1370
|
+
sage: A.row(0) == B.row(1)
|
|
1371
|
+
True
|
|
1372
|
+
sage: A.row(1) == B.row(0)
|
|
1373
|
+
True
|
|
1374
|
+
sage: A.row(2) == B.row(2)
|
|
1375
|
+
True
|
|
1376
|
+
"""
|
|
1377
|
+
mzd_row_swap(self._entries, row1, row2)
|
|
1378
|
+
|
|
1379
|
+
cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):
|
|
1380
|
+
"""
|
|
1381
|
+
EXAMPLES::
|
|
1382
|
+
|
|
1383
|
+
sage: A = random_matrix(GF(2),3,3)
|
|
1384
|
+
sage: B = copy(A)
|
|
1385
|
+
sage: A.swap_columns(0,1)
|
|
1386
|
+
sage: A.column(0) == B.column(1)
|
|
1387
|
+
True
|
|
1388
|
+
sage: A.column(1) == B.column(0)
|
|
1389
|
+
True
|
|
1390
|
+
sage: A.column(2) == B.column(2)
|
|
1391
|
+
True
|
|
1392
|
+
|
|
1393
|
+
sage: A = random_matrix(GF(2),3,65)
|
|
1394
|
+
|
|
1395
|
+
sage: B = A.__copy__()
|
|
1396
|
+
sage: B.swap_columns(0,1)
|
|
1397
|
+
sage: B.swap_columns(0,1)
|
|
1398
|
+
sage: A == B
|
|
1399
|
+
True
|
|
1400
|
+
|
|
1401
|
+
sage: A.swap_columns(0,64)
|
|
1402
|
+
sage: A.column(0) == B.column(64)
|
|
1403
|
+
True
|
|
1404
|
+
sage: A.swap_columns(63,64)
|
|
1405
|
+
sage: A.column(63) == B.column(0)
|
|
1406
|
+
True
|
|
1407
|
+
"""
|
|
1408
|
+
mzd_col_swap(self._entries, col1, col2)
|
|
1409
|
+
|
|
1410
|
+
def _magma_init_(self, magma):
|
|
1411
|
+
"""
|
|
1412
|
+
Return a string of ``self`` in ``Magma`` form.
|
|
1413
|
+
|
|
1414
|
+
This does not return a ``Magma`` object but a string.
|
|
1415
|
+
|
|
1416
|
+
EXAMPLES::
|
|
1417
|
+
|
|
1418
|
+
sage: A = random_matrix(GF(2),3,3)
|
|
1419
|
+
sage: A._magma_init_(magma) # optional - magma
|
|
1420
|
+
'Matrix(GF(2),3,3,StringToIntegerSequence("..."))'
|
|
1421
|
+
sage: A = random_matrix(GF(2),100,100)
|
|
1422
|
+
sage: B = random_matrix(GF(2),100,100)
|
|
1423
|
+
sage: magma(A*B) == magma(A) * magma(B) # optional - magma
|
|
1424
|
+
True
|
|
1425
|
+
|
|
1426
|
+
TESTS::
|
|
1427
|
+
|
|
1428
|
+
sage: A = random_matrix(GF(2),0,3)
|
|
1429
|
+
sage: magma(A) # optional - magma
|
|
1430
|
+
Matrix with 0 rows and 3 columns
|
|
1431
|
+
sage: A = matrix(GF(2),2,3,[0,1,1,1,0,0])
|
|
1432
|
+
sage: A._magma_init_(magma) # optional - magma
|
|
1433
|
+
'Matrix(GF(2),2,3,StringToIntegerSequence("0 1 1 1 0 0"))'
|
|
1434
|
+
sage: magma(A) # optional - magma
|
|
1435
|
+
[0 1 1]
|
|
1436
|
+
[1 0 0]
|
|
1437
|
+
"""
|
|
1438
|
+
s = self.base_ring()._magma_init_(magma)
|
|
1439
|
+
return 'Matrix(%s,%s,%s,StringToIntegerSequence("%s"))' % (
|
|
1440
|
+
s, self._nrows, self._ncols, self._export_as_string())
|
|
1441
|
+
|
|
1442
|
+
def determinant(self):
|
|
1443
|
+
"""
|
|
1444
|
+
Return the determinant of this matrix over GF(2).
|
|
1445
|
+
|
|
1446
|
+
EXAMPLES::
|
|
1447
|
+
|
|
1448
|
+
sage: matrix(GF(2),2,[1,1,0,1]).determinant()
|
|
1449
|
+
1
|
|
1450
|
+
sage: matrix(GF(2),2,[1,1,1,1]).determinant()
|
|
1451
|
+
0
|
|
1452
|
+
"""
|
|
1453
|
+
if not self.is_square():
|
|
1454
|
+
raise ValueError("self must be a square matrix")
|
|
1455
|
+
return self.base_ring()(1 if self.rank() == self.nrows() else 0)
|
|
1456
|
+
|
|
1457
|
+
def transpose(self):
|
|
1458
|
+
"""
|
|
1459
|
+
Return transpose of ``self`` and leaves ``self`` untouched.
|
|
1460
|
+
|
|
1461
|
+
EXAMPLES::
|
|
1462
|
+
|
|
1463
|
+
sage: A = Matrix(GF(2),3,5,[1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0])
|
|
1464
|
+
sage: A
|
|
1465
|
+
[1 0 1 0 0]
|
|
1466
|
+
[0 1 1 0 0]
|
|
1467
|
+
[1 1 0 1 0]
|
|
1468
|
+
sage: B = A.transpose(); B
|
|
1469
|
+
[1 0 1]
|
|
1470
|
+
[0 1 1]
|
|
1471
|
+
[1 1 0]
|
|
1472
|
+
[0 0 1]
|
|
1473
|
+
[0 0 0]
|
|
1474
|
+
sage: B.transpose() == A
|
|
1475
|
+
True
|
|
1476
|
+
|
|
1477
|
+
``.T`` is a convenient shortcut for the transpose::
|
|
1478
|
+
|
|
1479
|
+
sage: A.T
|
|
1480
|
+
[1 0 1]
|
|
1481
|
+
[0 1 1]
|
|
1482
|
+
[1 1 0]
|
|
1483
|
+
[0 0 1]
|
|
1484
|
+
[0 0 0]
|
|
1485
|
+
|
|
1486
|
+
TESTS::
|
|
1487
|
+
|
|
1488
|
+
sage: A = random_matrix(GF(2),0,40)
|
|
1489
|
+
sage: A.transpose()
|
|
1490
|
+
40 x 0 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
|
|
1491
|
+
|
|
1492
|
+
sage: A = Matrix(GF(2), [1,0])
|
|
1493
|
+
sage: B = A.transpose()
|
|
1494
|
+
sage: A[0,0] = 0
|
|
1495
|
+
sage: B[0,0]
|
|
1496
|
+
1
|
|
1497
|
+
"""
|
|
1498
|
+
cdef Matrix_mod2_dense A = self.new_matrix(ncols=self._nrows,
|
|
1499
|
+
nrows=self._ncols)
|
|
1500
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
1501
|
+
return A
|
|
1502
|
+
|
|
1503
|
+
A._entries = mzd_transpose(A._entries, self._entries)
|
|
1504
|
+
if self._subdivisions is not None:
|
|
1505
|
+
A.subdivide(*self.subdivisions())
|
|
1506
|
+
return A
|
|
1507
|
+
|
|
1508
|
+
cpdef _richcmp_(self, right, int op):
|
|
1509
|
+
"""
|
|
1510
|
+
Compare ``self`` with ``right``.
|
|
1511
|
+
|
|
1512
|
+
While equality and
|
|
1513
|
+
inequality are clearly defined, ``<`` and ``>`` are not. For
|
|
1514
|
+
those first the matrix dimensions of ``self`` and ``right``
|
|
1515
|
+
are compared. If these match then ``<`` means that there is a
|
|
1516
|
+
position smallest (i,j) in ``self`` where ``self[i,j]`` is
|
|
1517
|
+
zero but ``right[i,j]`` is one. This (i,j) is smaller than the
|
|
1518
|
+
(i,j) if ``self`` and ``right`` are exchanged for the
|
|
1519
|
+
comparison.
|
|
1520
|
+
|
|
1521
|
+
EXAMPLES::
|
|
1522
|
+
|
|
1523
|
+
sage: A = MatrixSpace(GF(2),3,3).one()
|
|
1524
|
+
sage: B = copy(MatrixSpace(GF(2),3,3).one())
|
|
1525
|
+
sage: B[0,1] = 1
|
|
1526
|
+
sage: A < B
|
|
1527
|
+
True
|
|
1528
|
+
|
|
1529
|
+
TESTS::
|
|
1530
|
+
|
|
1531
|
+
sage: A = matrix(GF(2),2,0)
|
|
1532
|
+
sage: B = matrix(GF(2),2,0)
|
|
1533
|
+
sage: A < B
|
|
1534
|
+
False
|
|
1535
|
+
"""
|
|
1536
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
1537
|
+
return rich_to_bool(op, 0)
|
|
1538
|
+
return rich_to_bool(op, mzd_cmp(self._entries,
|
|
1539
|
+
(<Matrix_mod2_dense>right)._entries))
|
|
1540
|
+
|
|
1541
|
+
def augment(self, right, subdivide=False):
|
|
1542
|
+
r"""
|
|
1543
|
+
Augments ``self`` with ``right``.
|
|
1544
|
+
|
|
1545
|
+
EXAMPLES::
|
|
1546
|
+
|
|
1547
|
+
sage: MS = MatrixSpace(GF(2),3,3)
|
|
1548
|
+
sage: A = MS([0, 1, 0, 1, 1, 0, 1, 1, 1]); A
|
|
1549
|
+
[0 1 0]
|
|
1550
|
+
[1 1 0]
|
|
1551
|
+
[1 1 1]
|
|
1552
|
+
sage: B = A.augment(MS(1)); B
|
|
1553
|
+
[0 1 0 1 0 0]
|
|
1554
|
+
[1 1 0 0 1 0]
|
|
1555
|
+
[1 1 1 0 0 1]
|
|
1556
|
+
sage: B.echelonize(); B
|
|
1557
|
+
[1 0 0 1 1 0]
|
|
1558
|
+
[0 1 0 1 0 0]
|
|
1559
|
+
[0 0 1 0 1 1]
|
|
1560
|
+
sage: C = B.matrix_from_columns([3,4,5]); C
|
|
1561
|
+
[1 1 0]
|
|
1562
|
+
[1 0 0]
|
|
1563
|
+
[0 1 1]
|
|
1564
|
+
sage: C == ~A
|
|
1565
|
+
True
|
|
1566
|
+
sage: C*A == MS(1)
|
|
1567
|
+
True
|
|
1568
|
+
|
|
1569
|
+
A vector may be augmented to a matrix. ::
|
|
1570
|
+
|
|
1571
|
+
sage: A = matrix(GF(2), 3, 4, range(12))
|
|
1572
|
+
sage: v = vector(GF(2), 3, range(3))
|
|
1573
|
+
sage: A.augment(v)
|
|
1574
|
+
[0 1 0 1 0]
|
|
1575
|
+
[0 1 0 1 1]
|
|
1576
|
+
[0 1 0 1 0]
|
|
1577
|
+
|
|
1578
|
+
The ``subdivide`` option will add a natural subdivision between
|
|
1579
|
+
``self`` and ``right``. For more details about how subdivisions
|
|
1580
|
+
are managed when augmenting, see
|
|
1581
|
+
:meth:`sage.matrix.matrix1.Matrix.augment`. ::
|
|
1582
|
+
|
|
1583
|
+
sage: A = matrix(GF(2), 3, 5, range(15))
|
|
1584
|
+
sage: B = matrix(GF(2), 3, 3, range(9))
|
|
1585
|
+
sage: A.augment(B, subdivide=True)
|
|
1586
|
+
[0 1 0 1 0|0 1 0]
|
|
1587
|
+
[1 0 1 0 1|1 0 1]
|
|
1588
|
+
[0 1 0 1 0|0 1 0]
|
|
1589
|
+
|
|
1590
|
+
TESTS::
|
|
1591
|
+
|
|
1592
|
+
sage: A = random_matrix(GF(2),2,3)
|
|
1593
|
+
sage: B = random_matrix(GF(2),2,0)
|
|
1594
|
+
sage: A.augment(B) == A
|
|
1595
|
+
True
|
|
1596
|
+
|
|
1597
|
+
sage: B.augment(A) == A
|
|
1598
|
+
True
|
|
1599
|
+
|
|
1600
|
+
sage: M = Matrix(GF(2), 0, 0, 0)
|
|
1601
|
+
sage: N = Matrix(GF(2), 0, 19, 0)
|
|
1602
|
+
sage: W = M.augment(N)
|
|
1603
|
+
sage: W.ncols()
|
|
1604
|
+
19
|
|
1605
|
+
sage: M = Matrix(GF(2), 0, 1, 0)
|
|
1606
|
+
sage: N = Matrix(GF(2), 0, 1, 0)
|
|
1607
|
+
sage: M.augment(N)
|
|
1608
|
+
[]
|
|
1609
|
+
|
|
1610
|
+
Check that :issue:`19165` is solved::
|
|
1611
|
+
|
|
1612
|
+
sage: m = matrix(GF(2), 2, range(4))
|
|
1613
|
+
sage: m.augment(matrix(GF(2), 2, range(4), sparse=True))
|
|
1614
|
+
[0 1 0 1]
|
|
1615
|
+
[0 1 0 1]
|
|
1616
|
+
|
|
1617
|
+
sage: m.augment(1)
|
|
1618
|
+
Traceback (most recent call last):
|
|
1619
|
+
...
|
|
1620
|
+
TypeError: right must either be a matrix or a vector. Not
|
|
1621
|
+
<class 'sage.rings.integer.Integer'>
|
|
1622
|
+
"""
|
|
1623
|
+
cdef Matrix_mod2_dense other
|
|
1624
|
+
|
|
1625
|
+
if isinstance(right, FreeModuleElement):
|
|
1626
|
+
right = right.column()
|
|
1627
|
+
|
|
1628
|
+
if isinstance(right, Matrix_mod2_dense):
|
|
1629
|
+
other = <Matrix_mod2_dense> right
|
|
1630
|
+
elif isinstance(right, Matrix):
|
|
1631
|
+
from sage.matrix.constructor import matrix
|
|
1632
|
+
other = matrix(self.base_ring(),
|
|
1633
|
+
right.nrows(),
|
|
1634
|
+
right.ncols(),
|
|
1635
|
+
right.list(),
|
|
1636
|
+
sparse=False)
|
|
1637
|
+
else:
|
|
1638
|
+
raise TypeError("right must either be a matrix or a vector. Not {}".format(type(right)))
|
|
1639
|
+
|
|
1640
|
+
if self._nrows != other._nrows:
|
|
1641
|
+
raise TypeError("Both numbers of rows must match.")
|
|
1642
|
+
|
|
1643
|
+
if self._ncols == 0:
|
|
1644
|
+
return other.__copy__()
|
|
1645
|
+
if other._ncols == 0:
|
|
1646
|
+
return self.__copy__()
|
|
1647
|
+
|
|
1648
|
+
cdef Matrix_mod2_dense Z
|
|
1649
|
+
Z = self.new_matrix(ncols = self._ncols + other._ncols)
|
|
1650
|
+
if self._nrows == 0:
|
|
1651
|
+
return Z
|
|
1652
|
+
Z._entries = mzd_concat(Z._entries, self._entries, other._entries)
|
|
1653
|
+
if subdivide:
|
|
1654
|
+
Z._subdivide_on_augment(self, other)
|
|
1655
|
+
return Z
|
|
1656
|
+
|
|
1657
|
+
cdef _stack_impl(self, bottom):
|
|
1658
|
+
r"""
|
|
1659
|
+
Stack ``self`` on top of ``bottom``.
|
|
1660
|
+
|
|
1661
|
+
EXAMPLES::
|
|
1662
|
+
|
|
1663
|
+
sage: A = matrix(GF(2),2,2,[1,0,0,1])
|
|
1664
|
+
sage: B = matrix(GF(2),2,2,[0,1,1,0])
|
|
1665
|
+
sage: A.stack(B)
|
|
1666
|
+
[1 0]
|
|
1667
|
+
[0 1]
|
|
1668
|
+
[0 1]
|
|
1669
|
+
[1 0]
|
|
1670
|
+
sage: B.stack(A)
|
|
1671
|
+
[0 1]
|
|
1672
|
+
[1 0]
|
|
1673
|
+
[1 0]
|
|
1674
|
+
[0 1]
|
|
1675
|
+
|
|
1676
|
+
A vector may be stacked below a matrix. ::
|
|
1677
|
+
|
|
1678
|
+
sage: A = matrix(GF(2), 2, 5, range(10))
|
|
1679
|
+
sage: v = vector(GF(2), 5, range(5))
|
|
1680
|
+
sage: A.stack(v)
|
|
1681
|
+
[0 1 0 1 0]
|
|
1682
|
+
[1 0 1 0 1]
|
|
1683
|
+
[0 1 0 1 0]
|
|
1684
|
+
|
|
1685
|
+
The ``subdivide`` option will add a natural subdivision between
|
|
1686
|
+
``self`` and ``bottom``. For more details about how subdivisions
|
|
1687
|
+
are managed when stacking, see
|
|
1688
|
+
:meth:`sage.matrix.matrix1.Matrix.stack`. ::
|
|
1689
|
+
|
|
1690
|
+
sage: A = matrix(GF(2), 3, 5, range(15))
|
|
1691
|
+
sage: B = matrix(GF(2), 1, 5, range(5))
|
|
1692
|
+
sage: A.stack(B, subdivide=True)
|
|
1693
|
+
[0 1 0 1 0]
|
|
1694
|
+
[1 0 1 0 1]
|
|
1695
|
+
[0 1 0 1 0]
|
|
1696
|
+
[---------]
|
|
1697
|
+
[0 1 0 1 0]
|
|
1698
|
+
|
|
1699
|
+
TESTS::
|
|
1700
|
+
|
|
1701
|
+
sage: A = random_matrix(GF(2),0,3)
|
|
1702
|
+
sage: B = random_matrix(GF(2),3,3)
|
|
1703
|
+
sage: A.stack(B) == B
|
|
1704
|
+
True
|
|
1705
|
+
|
|
1706
|
+
sage: B.stack(A) == B
|
|
1707
|
+
True
|
|
1708
|
+
|
|
1709
|
+
sage: M = Matrix(GF(2), 0, 0, 0)
|
|
1710
|
+
sage: N = Matrix(GF(2), 19, 0, 0)
|
|
1711
|
+
sage: W = M.stack(N)
|
|
1712
|
+
sage: W.nrows()
|
|
1713
|
+
19
|
|
1714
|
+
sage: M = Matrix(GF(2), 1, 0, 0)
|
|
1715
|
+
sage: N = Matrix(GF(2), 1, 0, 0)
|
|
1716
|
+
sage: M.stack(N)
|
|
1717
|
+
[]
|
|
1718
|
+
"""
|
|
1719
|
+
cdef Matrix_mod2_dense other = <Matrix_mod2_dense>bottom
|
|
1720
|
+
cdef Matrix_mod2_dense Z
|
|
1721
|
+
Z = self.new_matrix(nrows=self._nrows + other._nrows, ncols=self._ncols)
|
|
1722
|
+
if self._ncols > 0:
|
|
1723
|
+
Z._entries = mzd_stack(Z._entries, self._entries, other._entries)
|
|
1724
|
+
return Z
|
|
1725
|
+
|
|
1726
|
+
def submatrix(self, Py_ssize_t row=0, Py_ssize_t col=0,
|
|
1727
|
+
Py_ssize_t nrows=-1, Py_ssize_t ncols=-1):
|
|
1728
|
+
"""
|
|
1729
|
+
Return submatrix from the index row, col (inclusive) with
|
|
1730
|
+
dimension nrows x ncols.
|
|
1731
|
+
|
|
1732
|
+
INPUT:
|
|
1733
|
+
|
|
1734
|
+
- ``row`` -- index of start row
|
|
1735
|
+
- ``col`` -- index of start column
|
|
1736
|
+
- ``nrows`` -- number of rows of submatrix
|
|
1737
|
+
- ``ncols`` -- number of columns of submatrix
|
|
1738
|
+
|
|
1739
|
+
EXAMPLES::
|
|
1740
|
+
|
|
1741
|
+
sage: A = random_matrix(GF(2),200,200)
|
|
1742
|
+
sage: A[0:2,0:2] == A.submatrix(0,0,2,2)
|
|
1743
|
+
True
|
|
1744
|
+
sage: A[0:100,0:100] == A.submatrix(0,0,100,100)
|
|
1745
|
+
True
|
|
1746
|
+
sage: A == A.submatrix(0,0,200,200)
|
|
1747
|
+
True
|
|
1748
|
+
|
|
1749
|
+
sage: A[1:3,1:3] == A.submatrix(1,1,2,2)
|
|
1750
|
+
True
|
|
1751
|
+
sage: A[1:100,1:100] == A.submatrix(1,1,99,99)
|
|
1752
|
+
True
|
|
1753
|
+
sage: A[1:200,1:200] == A.submatrix(1,1,199,199)
|
|
1754
|
+
True
|
|
1755
|
+
|
|
1756
|
+
TESTS for handling of default arguments (:issue:`18761`)::
|
|
1757
|
+
|
|
1758
|
+
sage: A.submatrix(17,15) == A.submatrix(17,15,183,185)
|
|
1759
|
+
True
|
|
1760
|
+
sage: A.submatrix(row=100,col=37,nrows=1,ncols=3) == A.submatrix(100,37,1,3)
|
|
1761
|
+
True
|
|
1762
|
+
"""
|
|
1763
|
+
cdef Matrix_mod2_dense A
|
|
1764
|
+
|
|
1765
|
+
cdef int highr, highc
|
|
1766
|
+
|
|
1767
|
+
if nrows < 0:
|
|
1768
|
+
nrows = self._nrows - row
|
|
1769
|
+
if nrows < 0:
|
|
1770
|
+
nrows = 0
|
|
1771
|
+
|
|
1772
|
+
if ncols < 0:
|
|
1773
|
+
ncols = self._ncols - col
|
|
1774
|
+
if ncols < 0:
|
|
1775
|
+
ncols = 0
|
|
1776
|
+
|
|
1777
|
+
highr = row + nrows
|
|
1778
|
+
highc = col + ncols
|
|
1779
|
+
|
|
1780
|
+
if row < 0:
|
|
1781
|
+
raise TypeError("Expected row >= 0, but got %d instead." % row)
|
|
1782
|
+
|
|
1783
|
+
if col < 0:
|
|
1784
|
+
raise TypeError("Expected col >= 0, but got %d instead." % col)
|
|
1785
|
+
|
|
1786
|
+
if highc > self._entries.ncols:
|
|
1787
|
+
raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead." % (highc, self._entries.ncols))
|
|
1788
|
+
|
|
1789
|
+
if highr > self._entries.nrows:
|
|
1790
|
+
raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead." % (highr, self._entries.nrows))
|
|
1791
|
+
|
|
1792
|
+
A = self.new_matrix(nrows = nrows, ncols = ncols)
|
|
1793
|
+
if ncols == 0 or nrows == 0:
|
|
1794
|
+
return A
|
|
1795
|
+
A._entries = mzd_submatrix(A._entries, self._entries, row, col, highr, highc)
|
|
1796
|
+
return A
|
|
1797
|
+
|
|
1798
|
+
def __reduce__(self):
|
|
1799
|
+
"""
|
|
1800
|
+
Serialize ``self``.
|
|
1801
|
+
|
|
1802
|
+
EXAMPLES::
|
|
1803
|
+
|
|
1804
|
+
sage: A = random_matrix(GF(2),10,10)
|
|
1805
|
+
sage: f,s = A.__reduce__()
|
|
1806
|
+
sage: f(*s) == A
|
|
1807
|
+
True
|
|
1808
|
+
|
|
1809
|
+
TESTS:
|
|
1810
|
+
|
|
1811
|
+
Check that :issue:`24589` is fixed::
|
|
1812
|
+
|
|
1813
|
+
sage: A = random_matrix(GF(2),10,10)
|
|
1814
|
+
sage: loads(dumps(A)).is_immutable()
|
|
1815
|
+
False
|
|
1816
|
+
sage: A.set_immutable()
|
|
1817
|
+
sage: loads(dumps(A)).is_immutable()
|
|
1818
|
+
True
|
|
1819
|
+
|
|
1820
|
+
Check that :issue:`39367` is achieved::
|
|
1821
|
+
|
|
1822
|
+
sage: l = len(dumps(random_matrix(GF(2), 2000, 2000))); l # random # previously ~ 785000
|
|
1823
|
+
610207
|
|
1824
|
+
sage: assert l < 650000
|
|
1825
|
+
"""
|
|
1826
|
+
cdef Py_ssize_t i,j, r,c
|
|
1827
|
+
cdef int size
|
|
1828
|
+
|
|
1829
|
+
r, c = self.nrows(), self.ncols()
|
|
1830
|
+
if r == 0 or c == 0:
|
|
1831
|
+
return unpickle_matrix_mod2_dense_v2, (r, c, None, 0, self._is_immutable)
|
|
1832
|
+
|
|
1833
|
+
sig_on()
|
|
1834
|
+
cdef gdImagePtr im = gdImageCreate(c, r)
|
|
1835
|
+
sig_off()
|
|
1836
|
+
cdef int black = gdImageColorAllocate(im, 0, 0, 0)
|
|
1837
|
+
cdef int white = gdImageColorAllocate(im, 255, 255, 255)
|
|
1838
|
+
gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)
|
|
1839
|
+
for i from 0 <= i < r:
|
|
1840
|
+
for j from 0 <= j < c:
|
|
1841
|
+
if mzd_read_bit(self._entries, i, j):
|
|
1842
|
+
gdImageSetPixel(im, j, i, black )
|
|
1843
|
+
|
|
1844
|
+
cdef char *buf
|
|
1845
|
+
try:
|
|
1846
|
+
buf = <char*>gdImagePngPtr(im, &size)
|
|
1847
|
+
finally:
|
|
1848
|
+
gdImageDestroy(im)
|
|
1849
|
+
|
|
1850
|
+
cdef bytes data
|
|
1851
|
+
try:
|
|
1852
|
+
data = buf[:size]
|
|
1853
|
+
finally: # recommended by https://cython.readthedocs.io/en/latest/src/tutorial/strings.html
|
|
1854
|
+
gdFree(buf)
|
|
1855
|
+
return unpickle_matrix_mod2_dense_v2, (r,c, data, size, self._is_immutable)
|
|
1856
|
+
|
|
1857
|
+
cpdef _export_as_string(self):
|
|
1858
|
+
"""
|
|
1859
|
+
Return space separated string of the entries in this matrix.
|
|
1860
|
+
|
|
1861
|
+
EXAMPLES::
|
|
1862
|
+
|
|
1863
|
+
sage: w = matrix(GF(2),2,3,[1,0,1,1,1,0])
|
|
1864
|
+
sage: w._export_as_string()
|
|
1865
|
+
'1 0 1 1 1 0'
|
|
1866
|
+
"""
|
|
1867
|
+
cdef Py_ssize_t i, j, k, n
|
|
1868
|
+
cdef char *s
|
|
1869
|
+
|
|
1870
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
1871
|
+
data = ''
|
|
1872
|
+
else:
|
|
1873
|
+
n = self._nrows*self._ncols*2 + 2
|
|
1874
|
+
s = <char*> check_malloc(n * sizeof(char))
|
|
1875
|
+
k = 0
|
|
1876
|
+
sig_on()
|
|
1877
|
+
for i in range(self._nrows):
|
|
1878
|
+
for j in range(self._ncols):
|
|
1879
|
+
s[k] = <char>(48 + (1 if mzd_read_bit(self._entries,i,j) else 0)) # "0" or "1"
|
|
1880
|
+
k += 1
|
|
1881
|
+
s[k] = <char>32 # space
|
|
1882
|
+
k += 1
|
|
1883
|
+
sig_off()
|
|
1884
|
+
s[k-1] = <char>0
|
|
1885
|
+
data = char_to_str(s)
|
|
1886
|
+
sig_free(s)
|
|
1887
|
+
|
|
1888
|
+
return data
|
|
1889
|
+
|
|
1890
|
+
def density(self, approx=False):
|
|
1891
|
+
"""
|
|
1892
|
+
Return the density of this matrix.
|
|
1893
|
+
|
|
1894
|
+
By density we understand the ratio of the number of nonzero
|
|
1895
|
+
positions and the self.nrows() * self.ncols(), i.e. the number
|
|
1896
|
+
of possible nonzero positions.
|
|
1897
|
+
|
|
1898
|
+
INPUT:
|
|
1899
|
+
|
|
1900
|
+
- ``approx`` -- return floating point approximation (default: ``False``)
|
|
1901
|
+
|
|
1902
|
+
EXAMPLES::
|
|
1903
|
+
|
|
1904
|
+
sage: A = random_matrix(GF(2), 1000, 1000)
|
|
1905
|
+
sage: d = A.density()
|
|
1906
|
+
sage: float(d) == A.density(approx=True)
|
|
1907
|
+
True
|
|
1908
|
+
sage: len(A.nonzero_positions())/1000^2 == d
|
|
1909
|
+
True
|
|
1910
|
+
|
|
1911
|
+
sage: total = 1.0
|
|
1912
|
+
sage: density_sum = A.density()
|
|
1913
|
+
sage: while abs(density_sum/total - 0.5) > 0.001:
|
|
1914
|
+
....: A = random_matrix(GF(2), 1000, 1000)
|
|
1915
|
+
....: total += 1
|
|
1916
|
+
....: density_sum += A.density()
|
|
1917
|
+
"""
|
|
1918
|
+
if approx:
|
|
1919
|
+
from sage.rings.real_mpfr import create_RealNumber
|
|
1920
|
+
return create_RealNumber(mzd_density(self._entries, 1))
|
|
1921
|
+
else:
|
|
1922
|
+
return matrix_dense.Matrix_dense.density(self)
|
|
1923
|
+
|
|
1924
|
+
def rank(self, algorithm='ple'):
|
|
1925
|
+
"""
|
|
1926
|
+
Return the rank of this matrix.
|
|
1927
|
+
|
|
1928
|
+
On average 'ple' should be faster than 'm4ri' and hence it is
|
|
1929
|
+
the default choice. However, for small - i.e. quite few
|
|
1930
|
+
thousand rows & columns - and sparse matrices 'm4ri' might be
|
|
1931
|
+
a better choice.
|
|
1932
|
+
|
|
1933
|
+
INPUT:
|
|
1934
|
+
|
|
1935
|
+
- ``algorithm`` -- either "ple" or "m4ri"
|
|
1936
|
+
|
|
1937
|
+
EXAMPLES::
|
|
1938
|
+
|
|
1939
|
+
sage: while random_matrix(GF(2), 1000, 1000).rank() != 999:
|
|
1940
|
+
....: pass
|
|
1941
|
+
|
|
1942
|
+
sage: A = matrix(GF(2),10, 0)
|
|
1943
|
+
sage: A.rank()
|
|
1944
|
+
0
|
|
1945
|
+
"""
|
|
1946
|
+
x = self.fetch('rank')
|
|
1947
|
+
if x is not None:
|
|
1948
|
+
return x
|
|
1949
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
1950
|
+
return 0
|
|
1951
|
+
cdef mzd_t *A = mzd_copy(NULL, self._entries)
|
|
1952
|
+
cdef mzp_t *P
|
|
1953
|
+
cdef mzp_t *Q
|
|
1954
|
+
|
|
1955
|
+
if algorithm == 'ple':
|
|
1956
|
+
P = mzp_init(self._entries.nrows)
|
|
1957
|
+
Q = mzp_init(self._entries.ncols)
|
|
1958
|
+
r = mzd_ple(A, P, Q, 0)
|
|
1959
|
+
mzp_free(P)
|
|
1960
|
+
mzp_free(Q)
|
|
1961
|
+
elif algorithm == 'm4ri':
|
|
1962
|
+
r = mzd_echelonize_m4ri(A, 0, 0)
|
|
1963
|
+
else:
|
|
1964
|
+
raise ValueError("Algorithm '%s' unknown." % algorithm)
|
|
1965
|
+
mzd_free(A)
|
|
1966
|
+
self.cache('rank', r)
|
|
1967
|
+
return r
|
|
1968
|
+
|
|
1969
|
+
def _solve_right_general(self, B, check=True):
|
|
1970
|
+
"""
|
|
1971
|
+
Solve the matrix equation AX = B for X using the M4RI library.
|
|
1972
|
+
|
|
1973
|
+
INPUT:
|
|
1974
|
+
|
|
1975
|
+
- ``B`` -- a matrix
|
|
1976
|
+
- ``check`` -- boolean (default: ``True``); whether to check if the
|
|
1977
|
+
matrix equation has a solution
|
|
1978
|
+
|
|
1979
|
+
EXAMPLES::
|
|
1980
|
+
|
|
1981
|
+
sage: A = matrix(GF(2), [[1, 0], [0, 1], [1, 1]])
|
|
1982
|
+
sage: A.solve_right(vector([1, 1, 0]))
|
|
1983
|
+
(1, 1)
|
|
1984
|
+
sage: A.solve_right(vector([1, 1, 1]))
|
|
1985
|
+
Traceback (most recent call last):
|
|
1986
|
+
...
|
|
1987
|
+
ValueError: matrix equation has no solutions
|
|
1988
|
+
|
|
1989
|
+
TESTS::
|
|
1990
|
+
|
|
1991
|
+
sage: n = 128
|
|
1992
|
+
sage: m = 128
|
|
1993
|
+
sage: A = random_matrix(GF(2), n, m)
|
|
1994
|
+
sage: B = A * random_vector(GF(2), m)
|
|
1995
|
+
sage: A * A.solve_right(B) == B
|
|
1996
|
+
True
|
|
1997
|
+
sage: m = 64
|
|
1998
|
+
sage: A = random_matrix(GF(2), n, m)
|
|
1999
|
+
sage: B = A * random_vector(GF(2), m)
|
|
2000
|
+
sage: A * A.solve_right(B) == B
|
|
2001
|
+
True
|
|
2002
|
+
sage: m = 256
|
|
2003
|
+
sage: A = random_matrix(GF(2), n, m)
|
|
2004
|
+
sage: B = A * random_vector(GF(2), m)
|
|
2005
|
+
sage: A * A.solve_right(B) == B
|
|
2006
|
+
True
|
|
2007
|
+
sage: matrix(GF(2), 2, 0).solve_right(matrix(GF(2), 2, 2)) == matrix(GF(2), 0, 2)
|
|
2008
|
+
True
|
|
2009
|
+
sage: matrix(GF(2), 2, 0).solve_right(matrix(GF(2), 2, 2) + 1)
|
|
2010
|
+
Traceback (most recent call last):
|
|
2011
|
+
...
|
|
2012
|
+
ValueError: matrix equation has no solutions
|
|
2013
|
+
sage: matrix(GF(2), 2, 0).solve_right(matrix(GF(2), 2, 2) + 1, check=False) == matrix(GF(2), 0, 2)
|
|
2014
|
+
True
|
|
2015
|
+
sage: matrix(GF(2), 2, 2).solve_right(matrix(GF(2), 2, 0)) == matrix(GF(2), 2, 0)
|
|
2016
|
+
True
|
|
2017
|
+
|
|
2018
|
+
Check that it can be interrupted::
|
|
2019
|
+
|
|
2020
|
+
sage: set_random_seed(12345)
|
|
2021
|
+
sage: n, m = 20000, 19968
|
|
2022
|
+
sage: A = random_matrix(GF(2), n, m)
|
|
2023
|
+
sage: x = random_vector(GF(2), m)
|
|
2024
|
+
sage: B = A*x
|
|
2025
|
+
sage: from sage.doctest.util import ensure_interruptible_after
|
|
2026
|
+
sage: with ensure_interruptible_after(0.5): sol = A.solve_right(B)
|
|
2027
|
+
"""
|
|
2028
|
+
cdef mzd_t *B_entries = (<Matrix_mod2_dense>B)._entries
|
|
2029
|
+
|
|
2030
|
+
cdef Matrix_mod2_dense X # the solution
|
|
2031
|
+
X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols)
|
|
2032
|
+
if self._entries.ncols == 0 or B_entries.ncols == 0:
|
|
2033
|
+
# special case: empty matrix
|
|
2034
|
+
if check and B != 0:
|
|
2035
|
+
raise ValueError("matrix equation has no solutions")
|
|
2036
|
+
return X
|
|
2037
|
+
cdef rci_t rows = self._entries.nrows
|
|
2038
|
+
if self._entries.nrows < self._entries.ncols:
|
|
2039
|
+
rows = self._entries.ncols # mzd_solve_left requires ncols <= nrows
|
|
2040
|
+
|
|
2041
|
+
cdef mzd_t *lhs = mzd_init(rows, self._entries.ncols)
|
|
2042
|
+
mzd_copy(lhs, self._entries)
|
|
2043
|
+
cdef mzd_t *rhs = mzd_init(rows, B_entries.ncols)
|
|
2044
|
+
mzd_copy(rhs, B_entries)
|
|
2045
|
+
|
|
2046
|
+
cdef int ret
|
|
2047
|
+
try:
|
|
2048
|
+
sig_on()
|
|
2049
|
+
# although it is called mzd_solve_left, it does the same thing as solve_right
|
|
2050
|
+
ret = mzd_solve_left(lhs, rhs, 0, check)
|
|
2051
|
+
sig_off()
|
|
2052
|
+
|
|
2053
|
+
if ret == 0:
|
|
2054
|
+
# solution is placed in rhs
|
|
2055
|
+
rhs.nrows = self._entries.ncols
|
|
2056
|
+
mzd_copy(X._entries, rhs)
|
|
2057
|
+
return X
|
|
2058
|
+
else:
|
|
2059
|
+
raise ValueError("matrix equation has no solutions")
|
|
2060
|
+
finally:
|
|
2061
|
+
mzd_free(lhs)
|
|
2062
|
+
mzd_free(rhs)
|
|
2063
|
+
|
|
2064
|
+
def _right_kernel_matrix(self, **kwds):
|
|
2065
|
+
r"""
|
|
2066
|
+
Return a pair that includes a matrix of basis vectors
|
|
2067
|
+
for the right kernel of ``self``.
|
|
2068
|
+
|
|
2069
|
+
INPUT:
|
|
2070
|
+
|
|
2071
|
+
- ``kwds`` -- these are provided for consistency with other versions
|
|
2072
|
+
of this method. Here they are ignored as there is no optional
|
|
2073
|
+
behavior available.
|
|
2074
|
+
|
|
2075
|
+
OUTPUT:
|
|
2076
|
+
|
|
2077
|
+
Returns a pair. First item is the string 'computed-pluq'
|
|
2078
|
+
that identifies the nature of the basis vectors.
|
|
2079
|
+
|
|
2080
|
+
Second item is a matrix whose rows are a basis for the right kernel,
|
|
2081
|
+
over the integers mod 2, as computed by the M4RI library
|
|
2082
|
+
using PLUQ matrix decomposition.
|
|
2083
|
+
|
|
2084
|
+
EXAMPLES::
|
|
2085
|
+
|
|
2086
|
+
sage: A = matrix(GF(2), [
|
|
2087
|
+
....: [0, 1, 0, 0, 1, 0, 1, 1],
|
|
2088
|
+
....: [1, 0, 1, 0, 0, 1, 1, 0],
|
|
2089
|
+
....: [0, 0, 1, 0, 0, 1, 0, 1],
|
|
2090
|
+
....: [1, 0, 1, 1, 0, 1, 1, 0],
|
|
2091
|
+
....: [0, 0, 1, 0, 0, 1, 0, 1],
|
|
2092
|
+
....: [1, 1, 0, 1, 1, 0, 0, 0]])
|
|
2093
|
+
sage: A
|
|
2094
|
+
[0 1 0 0 1 0 1 1]
|
|
2095
|
+
[1 0 1 0 0 1 1 0]
|
|
2096
|
+
[0 0 1 0 0 1 0 1]
|
|
2097
|
+
[1 0 1 1 0 1 1 0]
|
|
2098
|
+
[0 0 1 0 0 1 0 1]
|
|
2099
|
+
[1 1 0 1 1 0 0 0]
|
|
2100
|
+
sage: result = A._right_kernel_matrix()
|
|
2101
|
+
sage: result[0]
|
|
2102
|
+
'computed-pluq'
|
|
2103
|
+
sage: result[1]
|
|
2104
|
+
[0 1 0 0 1 0 0 0]
|
|
2105
|
+
[0 0 1 0 0 1 0 0]
|
|
2106
|
+
[1 1 0 0 0 0 1 0]
|
|
2107
|
+
[1 1 1 0 0 0 0 1]
|
|
2108
|
+
sage: X = result[1].transpose()
|
|
2109
|
+
sage: A*X == zero_matrix(GF(2), 6, 4)
|
|
2110
|
+
True
|
|
2111
|
+
|
|
2112
|
+
TESTS:
|
|
2113
|
+
|
|
2114
|
+
We test the three trivial cases. Matrices with no rows or columns will
|
|
2115
|
+
cause segfaults in the M4RI code, so we protect against that instance.
|
|
2116
|
+
Computing a kernel or a right kernel matrix should never pass these
|
|
2117
|
+
problem matrices here. ::
|
|
2118
|
+
|
|
2119
|
+
sage: A = matrix(GF(2), 0, 2)
|
|
2120
|
+
sage: A._right_kernel_matrix()
|
|
2121
|
+
Traceback (most recent call last):
|
|
2122
|
+
...
|
|
2123
|
+
ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed
|
|
2124
|
+
sage: A = matrix(GF(2), 2, 0)
|
|
2125
|
+
sage: A._right_kernel_matrix()
|
|
2126
|
+
Traceback (most recent call last):
|
|
2127
|
+
...
|
|
2128
|
+
ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed
|
|
2129
|
+
sage: A = zero_matrix(GF(2), 4, 3)
|
|
2130
|
+
sage: A._right_kernel_matrix()[1]
|
|
2131
|
+
[1 0 0]
|
|
2132
|
+
[0 1 0]
|
|
2133
|
+
[0 0 1]
|
|
2134
|
+
"""
|
|
2135
|
+
tm = verbose("computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1)
|
|
2136
|
+
if self.nrows()==0 or self.ncols()==0:
|
|
2137
|
+
raise ValueError("kernels of matrices mod 2 with zero rows or zero columns cannot be computed")
|
|
2138
|
+
cdef Matrix_mod2_dense M
|
|
2139
|
+
cdef mzd_t *A = mzd_copy(NULL, self._entries)
|
|
2140
|
+
# Despite the name, this next call returns X such that M*X = 0
|
|
2141
|
+
cdef mzd_t *k = mzd_kernel_left_pluq(A, 0)
|
|
2142
|
+
mzd_free(A)
|
|
2143
|
+
if k != NULL:
|
|
2144
|
+
M = self.new_matrix(nrows = k.ncols, ncols = k.nrows)
|
|
2145
|
+
mzd_transpose(M._entries, k)
|
|
2146
|
+
mzd_free(k)
|
|
2147
|
+
else:
|
|
2148
|
+
M = self.new_matrix(nrows = 0, ncols = self._ncols)
|
|
2149
|
+
verbose("done computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1, t=tm)
|
|
2150
|
+
return 'computed-pluq', M
|
|
2151
|
+
|
|
2152
|
+
def doubly_lexical_ordering(self, inplace=False):
|
|
2153
|
+
r"""
|
|
2154
|
+
Return a doubly lexical ordering of the matrix.
|
|
2155
|
+
|
|
2156
|
+
A doubly lexical ordering of a matrix is an ordering of the rows
|
|
2157
|
+
and of the columns of the matrix so that both the rows and the
|
|
2158
|
+
columns, as vectors, are lexically increasing. See [Lub1987]_.
|
|
2159
|
+
A lexical ordering of vectors is the standard dictionary ordering,
|
|
2160
|
+
except that vectors will be read from highest to lowest coordinate.
|
|
2161
|
+
Thus row vectors will be compared from right to left, and column
|
|
2162
|
+
vectors from bottom to top.
|
|
2163
|
+
|
|
2164
|
+
INPUT:
|
|
2165
|
+
|
|
2166
|
+
- ``inplace`` -- boolean (default: ``False``); using ``inplace=True``
|
|
2167
|
+
will permute the rows and columns of the current matrix
|
|
2168
|
+
according to a doubly lexical ordering; this will modify the matrix
|
|
2169
|
+
|
|
2170
|
+
OUTPUT:
|
|
2171
|
+
|
|
2172
|
+
A pair ``(row_ordering, col_ordering)`` of
|
|
2173
|
+
:class:`~sage.groups.perm_gps.constructor.PermutationGroupElement`
|
|
2174
|
+
that represents a doubly lexical ordering of the rows or columns.
|
|
2175
|
+
|
|
2176
|
+
.. SEEALSO::
|
|
2177
|
+
|
|
2178
|
+
:meth:`~sage.matrix.matrix2.Matrix.permutation_normal_form`;
|
|
2179
|
+
a similar matrix normal form
|
|
2180
|
+
|
|
2181
|
+
ALGORITHM:
|
|
2182
|
+
|
|
2183
|
+
The algorithm is adapted from section 3 of [HAM1985]_. The time
|
|
2184
|
+
complexity of this algorithm is `O(n \cdot m^2)` for a `n \times m`
|
|
2185
|
+
matrix.
|
|
2186
|
+
|
|
2187
|
+
EXAMPLES::
|
|
2188
|
+
|
|
2189
|
+
sage: # needs sage.groups
|
|
2190
|
+
sage: A = Matrix(GF(2), [[0, 1],
|
|
2191
|
+
....: [1, 0]])
|
|
2192
|
+
sage: r, c = A.doubly_lexical_ordering()
|
|
2193
|
+
sage: r
|
|
2194
|
+
(1,2)
|
|
2195
|
+
sage: c
|
|
2196
|
+
()
|
|
2197
|
+
sage: A.permute_rows_and_columns(r, c); A
|
|
2198
|
+
[1 0]
|
|
2199
|
+
[0 1]
|
|
2200
|
+
|
|
2201
|
+
::
|
|
2202
|
+
|
|
2203
|
+
sage: # needs sage.groups
|
|
2204
|
+
sage: A = Matrix(GF(2), [[0, 1],
|
|
2205
|
+
....: [1, 0]])
|
|
2206
|
+
sage: r, c = A.doubly_lexical_ordering(inplace=True); A
|
|
2207
|
+
[1 0]
|
|
2208
|
+
[0 1]
|
|
2209
|
+
|
|
2210
|
+
TESTS:
|
|
2211
|
+
|
|
2212
|
+
This algorithm works correctly for the matrix in
|
|
2213
|
+
Example 3.7 in [HAM1985]_::
|
|
2214
|
+
|
|
2215
|
+
sage: # needs sage.groups
|
|
2216
|
+
sage: A = Matrix(GF(2), [[1, 1, 0, 0, 0, 0, 0],
|
|
2217
|
+
....: [1, 1, 0, 0, 0, 0, 0],
|
|
2218
|
+
....: [1, 1, 0, 1, 0, 0, 0],
|
|
2219
|
+
....: [0, 0, 1, 1, 0, 0, 0],
|
|
2220
|
+
....: [0, 1, 1, 1, 1, 0, 0],
|
|
2221
|
+
....: [0, 0, 0, 0, 0, 1, 1],
|
|
2222
|
+
....: [0, 0, 0, 0, 0, 1, 1],
|
|
2223
|
+
....: [0, 0, 0, 0, 1, 1, 1],
|
|
2224
|
+
....: [0, 0, 0, 1, 1, 1, 0]])
|
|
2225
|
+
sage: r, c = A.doubly_lexical_ordering()
|
|
2226
|
+
sage: B = A.with_permuted_rows_and_columns(r, c)
|
|
2227
|
+
sage: for i in range(B.ncols()):
|
|
2228
|
+
....: for j in range(i):
|
|
2229
|
+
....: for k in reversed(range(B.nrows())):
|
|
2230
|
+
....: assert B[k][j] <= B[k][i]
|
|
2231
|
+
....: if B[k][j] < B[k][i]:
|
|
2232
|
+
....: break
|
|
2233
|
+
sage: for i in range(B.nrows()):
|
|
2234
|
+
....: for j in range(i):
|
|
2235
|
+
....: for k in reversed(range(B.ncols())):
|
|
2236
|
+
....: assert B[j][k] <= B[i][k]
|
|
2237
|
+
....: if B[j][k] < B[i][k]:
|
|
2238
|
+
....: break
|
|
2239
|
+
sage: r, c = A.doubly_lexical_ordering(inplace=True)
|
|
2240
|
+
sage: A == B
|
|
2241
|
+
True
|
|
2242
|
+
|
|
2243
|
+
An immutable matrix calling with ``inplace=True`` will raise an error::
|
|
2244
|
+
|
|
2245
|
+
sage: A = Matrix(GF(2), [[0, 1], [1, 0]], immutable=True)
|
|
2246
|
+
sage: r, c = A.doubly_lexical_ordering(inplace=True)
|
|
2247
|
+
Traceback (most recent call last):
|
|
2248
|
+
...
|
|
2249
|
+
TypeError: this matrix is immutable;
|
|
2250
|
+
use inplace=False or apply to a mutable copy.
|
|
2251
|
+
|
|
2252
|
+
The algorithm works collectly for a matrix with nrows=0 or ncols=0::
|
|
2253
|
+
|
|
2254
|
+
sage: # needs sage.groups
|
|
2255
|
+
sage: A = Matrix(GF(2), 0, 2, [])
|
|
2256
|
+
sage: A.doubly_lexical_ordering()
|
|
2257
|
+
((), ())
|
|
2258
|
+
sage: B = Matrix(GF(2), 2, 0, [])
|
|
2259
|
+
sage: B.doubly_lexical_ordering()
|
|
2260
|
+
((), ())
|
|
2261
|
+
"""
|
|
2262
|
+
if inplace and self.is_immutable():
|
|
2263
|
+
raise TypeError("this matrix is immutable;"
|
|
2264
|
+
" use inplace=False or apply to a mutable copy.")
|
|
2265
|
+
|
|
2266
|
+
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
|
|
2267
|
+
from sage.groups.perm_gps.permgroup_element import make_permgroup_element_v2
|
|
2268
|
+
symmetric_group_nrows = SymmetricGroup(self._nrows)
|
|
2269
|
+
symmetric_group_ncols = SymmetricGroup(self._ncols)
|
|
2270
|
+
|
|
2271
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
2272
|
+
return symmetric_group_nrows.one(), symmetric_group_ncols.one()
|
|
2273
|
+
|
|
2274
|
+
cdef list partition_rows = [False for _ in range(self._nrows - 1)]
|
|
2275
|
+
cdef int partition_num = 1
|
|
2276
|
+
cdef list row_swapped = list(range(1, self._nrows + 1))
|
|
2277
|
+
cdef list col_swapped = list(range(1, self._ncols + 1))
|
|
2278
|
+
|
|
2279
|
+
cdef Matrix_mod2_dense A = self if inplace else self.__copy__()
|
|
2280
|
+
|
|
2281
|
+
cdef int i, col, row, partition_i, partition_start, partition_end
|
|
2282
|
+
cdef int largest_col, row_start, row_end
|
|
2283
|
+
for i in reversed(range(1, A._ncols + 1)):
|
|
2284
|
+
# count 1 for each partition and column
|
|
2285
|
+
count1 = [[0]*partition_num for _ in range(i)]
|
|
2286
|
+
for col in range(i):
|
|
2287
|
+
parition_i = 0
|
|
2288
|
+
for row in reversed(range(1, A._nrows)):
|
|
2289
|
+
count1[col][parition_i] += mzd_read_bit(A._entries, row, col)
|
|
2290
|
+
if partition_rows[row - 1]:
|
|
2291
|
+
parition_i += 1
|
|
2292
|
+
count1[col][parition_i] += mzd_read_bit(A._entries, 0, col) # special case of row == 0
|
|
2293
|
+
|
|
2294
|
+
# calculate largest_col = col s.t. count1[col] is lexicographically largest (0 <= col < i)
|
|
2295
|
+
_, largest_col = max((c, i) for i, c in enumerate(count1))
|
|
2296
|
+
|
|
2297
|
+
# We refine each partition of rows according to the value of A[:][largest_col].
|
|
2298
|
+
# and also move down rows that satisfy A[row][largest_col] = 1 in each partition.
|
|
2299
|
+
partition_start = 0
|
|
2300
|
+
for _ in range(partition_num):
|
|
2301
|
+
partition_end = partition_start
|
|
2302
|
+
while partition_end < A._nrows - 1 and not partition_rows[partition_end]:
|
|
2303
|
+
partition_end += 1
|
|
2304
|
+
row_start = partition_start
|
|
2305
|
+
row_end = partition_end
|
|
2306
|
+
while row_start < row_end:
|
|
2307
|
+
while row_start < row_end and not mzd_read_bit(A._entries, row_start, largest_col):
|
|
2308
|
+
row_start += 1
|
|
2309
|
+
while row_start < row_end and mzd_read_bit(A._entries, row_end, largest_col):
|
|
2310
|
+
row_end -= 1
|
|
2311
|
+
if row_start < row_end: # swap row
|
|
2312
|
+
A.swap_rows_c(row_start, row_end)
|
|
2313
|
+
row_swapped[row_start], row_swapped[row_end] = row_swapped[row_end], row_swapped[row_start]
|
|
2314
|
+
partition_start = partition_end + 1 # for next partition
|
|
2315
|
+
|
|
2316
|
+
for row in range(A._nrows - 1):
|
|
2317
|
+
if mzd_read_bit(A._entries, row, largest_col) != mzd_read_bit(A._entries, row + 1, largest_col):
|
|
2318
|
+
if not partition_rows[row]:
|
|
2319
|
+
partition_rows[row] = True
|
|
2320
|
+
partition_num += 1
|
|
2321
|
+
|
|
2322
|
+
# swap column
|
|
2323
|
+
A.swap_columns_c(largest_col, i - 1)
|
|
2324
|
+
col_swapped[largest_col], col_swapped[i - 1] = col_swapped[i - 1], col_swapped[largest_col]
|
|
2325
|
+
|
|
2326
|
+
row_ordering = make_permgroup_element_v2(symmetric_group_nrows, row_swapped, symmetric_group_nrows.domain())
|
|
2327
|
+
col_ordering = make_permgroup_element_v2(symmetric_group_ncols, col_swapped, symmetric_group_ncols.domain())
|
|
2328
|
+
|
|
2329
|
+
return row_ordering, col_ordering
|
|
2330
|
+
|
|
2331
|
+
def is_Gamma_free(self, certificate=False):
|
|
2332
|
+
r"""
|
|
2333
|
+
Return True if the matrix is `\Gamma`-free.
|
|
2334
|
+
|
|
2335
|
+
A matrix is `\Gamma`-free if it does not contain a 2x2 submatrix
|
|
2336
|
+
of the form:
|
|
2337
|
+
|
|
2338
|
+
.. MATH::
|
|
2339
|
+
|
|
2340
|
+
\begin{pmatrix}
|
|
2341
|
+
1 & 1 \\
|
|
2342
|
+
1 & 0
|
|
2343
|
+
\end{pmatrix}
|
|
2344
|
+
|
|
2345
|
+
INPUT:
|
|
2346
|
+
|
|
2347
|
+
- ``certificate`` -- boolean (default: ``False``); whether to return a
|
|
2348
|
+
certificate for no-answers (see OUTPUT section)
|
|
2349
|
+
|
|
2350
|
+
OUTPUT:
|
|
2351
|
+
|
|
2352
|
+
When ``certificate`` is set to ``False`` (default) this method only
|
|
2353
|
+
returns ``True`` or ``False`` answers. When ``certificate`` is set to
|
|
2354
|
+
``True``, the method either returns ``(True, None)`` or ``(False,
|
|
2355
|
+
(r1, c1, r2, c2))`` where ``r1``, ``r2``-th rows and ``c1``,
|
|
2356
|
+
``c2``-th columns of the matrix constitute the `\Gamma`-submatrix.
|
|
2357
|
+
|
|
2358
|
+
ALGORITHM:
|
|
2359
|
+
|
|
2360
|
+
For each 1 entry, the algorithm finds the next 1 in the same row and
|
|
2361
|
+
the next 1 in the same column, and check the 2x2 submatrix that contains
|
|
2362
|
+
these entries forms `\Gamma` submatrix. The time complexity of
|
|
2363
|
+
this algorithm is `O(n \cdot m)` for a `n \times m` matrix.
|
|
2364
|
+
|
|
2365
|
+
EXAMPLES::
|
|
2366
|
+
|
|
2367
|
+
sage: A = Matrix(GF(2), [[1, 1],
|
|
2368
|
+
....: [0, 0]])
|
|
2369
|
+
sage: A.is_Gamma_free()
|
|
2370
|
+
True
|
|
2371
|
+
sage: B = Matrix(GF(2), [[1, 1],
|
|
2372
|
+
....: [1, 0]])
|
|
2373
|
+
sage: B.is_Gamma_free(certificate=True)
|
|
2374
|
+
(False, (0, 0, 1, 1))
|
|
2375
|
+
|
|
2376
|
+
TESTS:
|
|
2377
|
+
|
|
2378
|
+
The algorithm works collectly for larger matrices::
|
|
2379
|
+
|
|
2380
|
+
sage: A = Matrix(GF(2), [[1, 0, 1],
|
|
2381
|
+
....: [0, 0, 0],
|
|
2382
|
+
....: [1, 0, 0]])
|
|
2383
|
+
sage: A.is_Gamma_free(certificate=True)
|
|
2384
|
+
(False, (0, 0, 2, 2))
|
|
2385
|
+
sage: B = Matrix(GF(2), [[1, 0, 1],
|
|
2386
|
+
....: [0, 0, 0],
|
|
2387
|
+
....: [1, 0, 1]])
|
|
2388
|
+
sage: B.is_Gamma_free(certificate=True)
|
|
2389
|
+
(True, None)
|
|
2390
|
+
"""
|
|
2391
|
+
cdef int i, j, i_bottom, j_right
|
|
2392
|
+
|
|
2393
|
+
for i in range(self._nrows):
|
|
2394
|
+
j = 0
|
|
2395
|
+
while j < self._ncols:
|
|
2396
|
+
if mzd_read_bit(self._entries, i, j): # if A[i][j] == 1
|
|
2397
|
+
# find the next 1 in the row
|
|
2398
|
+
j_right = j + 1
|
|
2399
|
+
while j_right < self._ncols and not mzd_read_bit(self._entries, i, j_right):
|
|
2400
|
+
j_right += 1
|
|
2401
|
+
if j_right < self._ncols:
|
|
2402
|
+
# find the next 1 in the column
|
|
2403
|
+
i_bottom = i + 1
|
|
2404
|
+
while i_bottom < self._nrows and not mzd_read_bit(self._entries, i_bottom, j):
|
|
2405
|
+
i_bottom += 1
|
|
2406
|
+
if i_bottom < self._nrows and not mzd_read_bit(self._entries, i_bottom, j_right):
|
|
2407
|
+
# A[i_bottom][j_right] == 0
|
|
2408
|
+
if certificate:
|
|
2409
|
+
return False, (i, j, i_bottom, j_right)
|
|
2410
|
+
else:
|
|
2411
|
+
return False
|
|
2412
|
+
j = j_right
|
|
2413
|
+
else:
|
|
2414
|
+
j += 1
|
|
2415
|
+
|
|
2416
|
+
if certificate:
|
|
2417
|
+
return True, None
|
|
2418
|
+
else:
|
|
2419
|
+
return True
|
|
2420
|
+
|
|
2421
|
+
# Used for hashing
|
|
2422
|
+
cdef int i, k
|
|
2423
|
+
cdef unsigned long parity_table[256]
|
|
2424
|
+
for i from 0 <= i < 256:
|
|
2425
|
+
parity_table[i] = 1 & ((i) ^ (i>>1) ^ (i>>2) ^ (i>>3) ^
|
|
2426
|
+
(i>>4) ^ (i>>5) ^ (i>>6) ^ (i>>7))
|
|
2427
|
+
|
|
2428
|
+
# gmp's ULONG_PARITY may use special
|
|
2429
|
+
# assembly instructions, could be faster
|
|
2430
|
+
cpdef inline unsigned long parity(m4ri_word a) noexcept:
|
|
2431
|
+
"""
|
|
2432
|
+
Return the parity of the number of bits in a.
|
|
2433
|
+
|
|
2434
|
+
EXAMPLES::
|
|
2435
|
+
|
|
2436
|
+
sage: from sage.matrix.matrix_mod2_dense import parity
|
|
2437
|
+
sage: parity(1)
|
|
2438
|
+
1
|
|
2439
|
+
sage: parity(3)
|
|
2440
|
+
0
|
|
2441
|
+
sage: parity(0x10000101011)
|
|
2442
|
+
1
|
|
2443
|
+
"""
|
|
2444
|
+
if sizeof(m4ri_word) == 8:
|
|
2445
|
+
a ^= a >> 32
|
|
2446
|
+
a ^= a >> 16
|
|
2447
|
+
a ^= a >> 8
|
|
2448
|
+
return parity_table[a & 0xFF]
|
|
2449
|
+
|
|
2450
|
+
cdef inline unsigned long parity_mask(m4ri_word a) noexcept:
|
|
2451
|
+
return -parity(a)
|
|
2452
|
+
|
|
2453
|
+
|
|
2454
|
+
def unpickle_matrix_mod2_dense_v2(r, c, data, size, immutable=False):
|
|
2455
|
+
r"""
|
|
2456
|
+
Deserialize a matrix encoded in the string ``s``.
|
|
2457
|
+
|
|
2458
|
+
INPUT:
|
|
2459
|
+
|
|
2460
|
+
- ``r`` -- number of rows of matrix
|
|
2461
|
+
- ``c`` -- number of columns of matrix
|
|
2462
|
+
- ``s`` -- string
|
|
2463
|
+
- ``size`` -- length of the string ``s``
|
|
2464
|
+
- ``immutable`` -- boolean (default: ``False``); whether the
|
|
2465
|
+
matrix is immutable or not
|
|
2466
|
+
|
|
2467
|
+
EXAMPLES::
|
|
2468
|
+
|
|
2469
|
+
sage: A = random_matrix(GF(2),100,101)
|
|
2470
|
+
sage: _, (r,c,s,s2,i) = A.__reduce__()
|
|
2471
|
+
sage: from sage.matrix.matrix_mod2_dense import unpickle_matrix_mod2_dense_v2
|
|
2472
|
+
sage: unpickle_matrix_mod2_dense_v2(r,c,s,s2,i) == A
|
|
2473
|
+
True
|
|
2474
|
+
sage: loads(dumps(A)) == A
|
|
2475
|
+
True
|
|
2476
|
+
|
|
2477
|
+
TESTS:
|
|
2478
|
+
|
|
2479
|
+
Check that old pickles before :issue:`24589` still work::
|
|
2480
|
+
|
|
2481
|
+
sage: s = (b"x\x9ck`J.NLO\xd5\xcbM,)\xca\xac\x80R\xf1\xb9\xf9)F\xf1)\xa9y"
|
|
2482
|
+
....: b"\xc5\xa9\\\xa5y\x05\x99\xc9\xd99\xa9\xf1\x18R\xf1e\x86\\\x85"
|
|
2483
|
+
....: b"\x8c\x1a\xdeL\xdeL\xb1\x85L\x1a^\x9d\xff\xff\xff\xf7\x0e\xf0"
|
|
2484
|
+
....: b"\xf6\xf3v\xf7\xe6\xf5\xe6\xf2\x96\x02b\x060\xe4\xf5\xf6\xf4"
|
|
2485
|
+
....: b"\xf6\xf0v\xf1\x0e\x82\xf2\x99\xe04\xa373\x94\xed\xe1]\xe15"
|
|
2486
|
+
....: b"\x1fd@:T\x80\rh\x94\x8fw\x88\xb7+\x84\xef\x05\x94\xfb\x8fD,"
|
|
2487
|
+
....: b"\x05\x117A\x04H\x97\xd7]\x90V\x88FN\xef\x02\xa0i\x91\xde\xc5"
|
|
2488
|
+
....: b"`\x1e\x9f\xd7\x11\x98\x14\x94\xc9\xe85\x15Di{\xf3yKC\xb5\xf0"
|
|
2489
|
+
....: b"\x00\x1d\xe8\xe2\xed\x08\xb4\x8d\xc3k&H2\x19hF\x82w\x06X\x92"
|
|
2490
|
+
....: b"\x11(\xc5\xe0u\x10$\x9c\x0ft\x9d\xa1\xd7\x03\x84]\x0c@\x8d\xae@"
|
|
2491
|
+
....: b"\x1f\xbbx\xad\x03\t:y'x5\x01\x19\xa9\xde9%A\x85\xccz\x000I\x88D")
|
|
2492
|
+
sage: loads(s)
|
|
2493
|
+
[1 0]
|
|
2494
|
+
[0 1]
|
|
2495
|
+
|
|
2496
|
+
Check that old pickles before :issue:`39367` still work::
|
|
2497
|
+
|
|
2498
|
+
sage: loads(bytes.fromhex( # hexstring produced with dumps(matrix.zero(GF(2),10,10)).hex()
|
|
2499
|
+
....: '789c6b604a2e4e4c4fd5cb4d2c29caac8052f1b9f92946f129a979c5a95ca5'
|
|
2500
|
+
....: '790599c9d939a9f11852f165465c850c1ade5cde5cb1858c1a5e9dfffffff7'
|
|
2501
|
+
....: '0ef0f6f376f7e6050a4a01310318f27a7b7a7b78bb780741f95c709ad19b19'
|
|
2502
|
+
....: 'c2f6da0ed4ecf5076442acd73f100551c20634d0c73bc4db15aaec3f481982'
|
|
2503
|
+
....: '580a226e8288f920e22e4223a77701d0ce48ef62308fcfeb084c0aca64f49a'
|
|
2504
|
+
....: '0aa2b4bdf9bca5a15af880ce74f17604dac6e135132499ecf50344d57b3580'
|
|
2505
|
+
....: '75789b7b330195b103fd21e85deeb51064e362908c2f441d03147a025debe7'
|
|
2506
|
+
....: 'ede2b50e24e8e49de0d50464a47ae775961432051532eb0100093b9ba3'))
|
|
2507
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2508
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2509
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2510
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2511
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2512
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2513
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2514
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2515
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2516
|
+
[0 0 0 0 0 0 0 0 0 0]
|
|
2517
|
+
"""
|
|
2518
|
+
from sage.matrix.constructor import Matrix
|
|
2519
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
|
2520
|
+
|
|
2521
|
+
cdef Py_ssize_t i, j
|
|
2522
|
+
cdef Matrix_mod2_dense A
|
|
2523
|
+
|
|
2524
|
+
A = <Matrix_mod2_dense>Matrix(GF(2),r,c)
|
|
2525
|
+
if r == 0 or c == 0:
|
|
2526
|
+
return A
|
|
2527
|
+
|
|
2528
|
+
cdef signed char *buf
|
|
2529
|
+
cdef gdImagePtr im
|
|
2530
|
+
if isinstance(data, bytes):
|
|
2531
|
+
sig_on()
|
|
2532
|
+
im = gdImageCreateFromPngPtr(size, <char *><bytes>data)
|
|
2533
|
+
sig_off()
|
|
2534
|
+
else:
|
|
2535
|
+
buf = <signed char*>check_malloc(size)
|
|
2536
|
+
for i from 0 <= i < size:
|
|
2537
|
+
buf[i] = data[i]
|
|
2538
|
+
|
|
2539
|
+
sig_on()
|
|
2540
|
+
im = gdImageCreateFromPngPtr(size, buf)
|
|
2541
|
+
sig_off()
|
|
2542
|
+
sig_free(buf)
|
|
2543
|
+
|
|
2544
|
+
if gdImageSX(im) != c or gdImageSY(im) != r:
|
|
2545
|
+
raise TypeError("Pickled data dimension doesn't match.")
|
|
2546
|
+
|
|
2547
|
+
for i from 0 <= i < r:
|
|
2548
|
+
for j from 0 <= j < c:
|
|
2549
|
+
mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))
|
|
2550
|
+
gdImageDestroy(im)
|
|
2551
|
+
|
|
2552
|
+
if immutable:
|
|
2553
|
+
A.set_immutable()
|
|
2554
|
+
|
|
2555
|
+
return A
|
|
2556
|
+
|
|
2557
|
+
|
|
2558
|
+
from sage.misc.persist import register_unpickle_override
|
|
2559
|
+
register_unpickle_override('sage.matrix.matrix_mod2_dense',
|
|
2560
|
+
'unpickle_matrix_mod2_dense_v1',
|
|
2561
|
+
unpickle_matrix_mod2_dense_v2)
|
|
2562
|
+
|
|
2563
|
+
|
|
2564
|
+
def from_png(filename):
|
|
2565
|
+
"""
|
|
2566
|
+
Return a dense matrix over GF(2) from a 1-bit PNG image read from
|
|
2567
|
+
``filename``. No attempt is made to verify that the filename string
|
|
2568
|
+
actually points to a PNG image.
|
|
2569
|
+
|
|
2570
|
+
INPUT:
|
|
2571
|
+
|
|
2572
|
+
- ``filename`` -- string
|
|
2573
|
+
|
|
2574
|
+
EXAMPLES::
|
|
2575
|
+
|
|
2576
|
+
sage: from sage.matrix.matrix_mod2_dense import from_png, to_png
|
|
2577
|
+
sage: A = random_matrix(GF(2),10,10)
|
|
2578
|
+
sage: fn = tmp_filename()
|
|
2579
|
+
sage: to_png(A, fn)
|
|
2580
|
+
sage: B = from_png(fn)
|
|
2581
|
+
sage: A == B
|
|
2582
|
+
True
|
|
2583
|
+
"""
|
|
2584
|
+
from sage.matrix.constructor import Matrix
|
|
2585
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
|
2586
|
+
|
|
2587
|
+
cdef Py_ssize_t i,j,r,c
|
|
2588
|
+
cdef Matrix_mod2_dense A
|
|
2589
|
+
|
|
2590
|
+
fn = open(filename,"r") # check filename
|
|
2591
|
+
fn.close()
|
|
2592
|
+
|
|
2593
|
+
if type(filename) is not bytes:
|
|
2594
|
+
filename = filename.encode(FS_ENCODING, 'surrogateescape')
|
|
2595
|
+
|
|
2596
|
+
cdef FILE *f = fopen(filename, "rb")
|
|
2597
|
+
sig_on()
|
|
2598
|
+
cdef gdImagePtr im = gdImageCreateFromPng(f)
|
|
2599
|
+
sig_off()
|
|
2600
|
+
|
|
2601
|
+
c, r = gdImageSX(im), gdImageSY(im)
|
|
2602
|
+
|
|
2603
|
+
A = <Matrix_mod2_dense>Matrix(GF(2),r,c)
|
|
2604
|
+
|
|
2605
|
+
for i from 0 <= i < r:
|
|
2606
|
+
for j from 0 <= j < c:
|
|
2607
|
+
mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))
|
|
2608
|
+
fclose(f)
|
|
2609
|
+
gdImageDestroy(im)
|
|
2610
|
+
return A
|
|
2611
|
+
|
|
2612
|
+
|
|
2613
|
+
def to_png(Matrix_mod2_dense A, filename):
|
|
2614
|
+
"""
|
|
2615
|
+
Save the matrix ``A`` to filename as a 1-bit PNG image.
|
|
2616
|
+
|
|
2617
|
+
INPUT:
|
|
2618
|
+
|
|
2619
|
+
- ``A`` -- a matrix over GF(2)
|
|
2620
|
+
- ``filename`` -- string for a file in a writable position
|
|
2621
|
+
|
|
2622
|
+
EXAMPLES::
|
|
2623
|
+
|
|
2624
|
+
sage: from sage.matrix.matrix_mod2_dense import from_png, to_png
|
|
2625
|
+
sage: A = random_matrix(GF(2),10,10)
|
|
2626
|
+
sage: fn = tmp_filename()
|
|
2627
|
+
sage: to_png(A, fn)
|
|
2628
|
+
sage: B = from_png(fn)
|
|
2629
|
+
sage: A == B
|
|
2630
|
+
True
|
|
2631
|
+
"""
|
|
2632
|
+
cdef Py_ssize_t i,j, r,c
|
|
2633
|
+
r, c = A.nrows(), A.ncols()
|
|
2634
|
+
if r == 0 or c == 0:
|
|
2635
|
+
raise TypeError(f"cannot write image with dimensions {c} x {r}")
|
|
2636
|
+
|
|
2637
|
+
fn = open(filename, "w") # check filename
|
|
2638
|
+
fn.close()
|
|
2639
|
+
|
|
2640
|
+
if type(filename) is not bytes:
|
|
2641
|
+
filename = filename.encode(FS_ENCODING, 'surrogateescape')
|
|
2642
|
+
|
|
2643
|
+
cdef gdImagePtr im = gdImageCreate(c, r)
|
|
2644
|
+
cdef FILE * out = fopen(filename, "wb")
|
|
2645
|
+
cdef int black = gdImageColorAllocate(im, 0, 0, 0)
|
|
2646
|
+
cdef int white = gdImageColorAllocate(im, 255, 255, 255)
|
|
2647
|
+
gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)
|
|
2648
|
+
for i from 0 <= i < r:
|
|
2649
|
+
for j from 0 <= j < c:
|
|
2650
|
+
if mzd_read_bit(A._entries, i, j):
|
|
2651
|
+
gdImageSetPixel(im, j, i, black )
|
|
2652
|
+
|
|
2653
|
+
gdImagePng(im, out)
|
|
2654
|
+
gdImageDestroy(im)
|
|
2655
|
+
fclose(out)
|
|
2656
|
+
|
|
2657
|
+
|
|
2658
|
+
def pluq(Matrix_mod2_dense A, algorithm='standard', int param=0):
|
|
2659
|
+
"""
|
|
2660
|
+
Return PLUQ factorization of A.
|
|
2661
|
+
|
|
2662
|
+
INPUT:
|
|
2663
|
+
|
|
2664
|
+
- ``A`` -- matrix
|
|
2665
|
+
- ``algorithm`` -- string; one of
|
|
2666
|
+
|
|
2667
|
+
* ``'standard'`` asymptotically fast (default)
|
|
2668
|
+
* ``'mmpf'`` M4RI inspired
|
|
2669
|
+
* ``'naive'`` naive cubic
|
|
2670
|
+
|
|
2671
|
+
- ``param`` -- either k for 'mmpf' is chosen or matrix multiplication cutoff
|
|
2672
|
+
for ``'standard'`` (default: 0)
|
|
2673
|
+
|
|
2674
|
+
EXAMPLES::
|
|
2675
|
+
|
|
2676
|
+
sage: from sage.matrix.matrix_mod2_dense import pluq
|
|
2677
|
+
sage: A = matrix(GF(2), 4, 4, [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0])
|
|
2678
|
+
sage: A
|
|
2679
|
+
[0 1 0 1]
|
|
2680
|
+
[0 1 1 1]
|
|
2681
|
+
[0 0 0 1]
|
|
2682
|
+
[0 1 1 0]
|
|
2683
|
+
sage: LU, P, Q = pluq(A)
|
|
2684
|
+
sage: LU
|
|
2685
|
+
[1 0 1 0]
|
|
2686
|
+
[1 1 0 0]
|
|
2687
|
+
[0 0 1 0]
|
|
2688
|
+
[1 1 1 0]
|
|
2689
|
+
|
|
2690
|
+
sage: P
|
|
2691
|
+
[0, 1, 2, 3]
|
|
2692
|
+
|
|
2693
|
+
sage: Q
|
|
2694
|
+
[1, 2, 3, 3]
|
|
2695
|
+
"""
|
|
2696
|
+
cdef Matrix_mod2_dense B = A.__copy__()
|
|
2697
|
+
cdef mzp_t *p = mzp_init(A._entries.nrows)
|
|
2698
|
+
cdef mzp_t *q = mzp_init(A._entries.ncols)
|
|
2699
|
+
|
|
2700
|
+
if algorithm == "standard":
|
|
2701
|
+
sig_on()
|
|
2702
|
+
mzd_pluq(B._entries, p, q, param)
|
|
2703
|
+
sig_off()
|
|
2704
|
+
elif algorithm == "mmpf":
|
|
2705
|
+
sig_on()
|
|
2706
|
+
_mzd_pluq_russian(B._entries, p, q, param)
|
|
2707
|
+
sig_off()
|
|
2708
|
+
elif algorithm == "naive":
|
|
2709
|
+
sig_on()
|
|
2710
|
+
_mzd_pluq_naive(B._entries, p, q)
|
|
2711
|
+
sig_off()
|
|
2712
|
+
else:
|
|
2713
|
+
raise ValueError("Algorithm '%s' unknown." % algorithm)
|
|
2714
|
+
|
|
2715
|
+
P = [p.values[i] for i in range(A.nrows())]
|
|
2716
|
+
Q = [q.values[i] for i in range(A.ncols())]
|
|
2717
|
+
mzp_free(p)
|
|
2718
|
+
mzp_free(q)
|
|
2719
|
+
return B, P, Q
|
|
2720
|
+
|
|
2721
|
+
|
|
2722
|
+
def ple(Matrix_mod2_dense A, algorithm='standard', int param=0):
|
|
2723
|
+
"""
|
|
2724
|
+
Return PLE factorization of A.
|
|
2725
|
+
|
|
2726
|
+
INPUT:
|
|
2727
|
+
|
|
2728
|
+
- ``A`` -- matrix
|
|
2729
|
+
- ``algorithm`` -- string; one of
|
|
2730
|
+
|
|
2731
|
+
- ``'standard'`` asymptotically fast (default)
|
|
2732
|
+
- ``'russian'`` M4RI inspired
|
|
2733
|
+
- ``'naive'`` naive cubic
|
|
2734
|
+
|
|
2735
|
+
- ``param`` -- either k for 'mmpf' is chosen or matrix multiplication
|
|
2736
|
+
cutoff for 'standard' (default: 0)
|
|
2737
|
+
|
|
2738
|
+
EXAMPLES::
|
|
2739
|
+
|
|
2740
|
+
sage: from sage.matrix.matrix_mod2_dense import ple
|
|
2741
|
+
|
|
2742
|
+
sage: A = matrix(GF(2), 4, 4, [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0])
|
|
2743
|
+
sage: A
|
|
2744
|
+
[0 1 0 1]
|
|
2745
|
+
[0 1 1 1]
|
|
2746
|
+
[0 0 0 1]
|
|
2747
|
+
[0 1 1 0]
|
|
2748
|
+
|
|
2749
|
+
sage: LU, P, Q = ple(A)
|
|
2750
|
+
sage: LU
|
|
2751
|
+
[1 0 0 1]
|
|
2752
|
+
[1 1 0 0]
|
|
2753
|
+
[0 0 1 0]
|
|
2754
|
+
[1 1 1 0]
|
|
2755
|
+
|
|
2756
|
+
sage: P
|
|
2757
|
+
[0, 1, 2, 3]
|
|
2758
|
+
|
|
2759
|
+
sage: Q
|
|
2760
|
+
[1, 2, 3, 3]
|
|
2761
|
+
|
|
2762
|
+
sage: A = random_matrix(GF(2),1000,1000)
|
|
2763
|
+
sage: ple(A) == ple(A,'russian') == ple(A,'naive')
|
|
2764
|
+
True
|
|
2765
|
+
"""
|
|
2766
|
+
cdef Matrix_mod2_dense B = A.__copy__()
|
|
2767
|
+
cdef mzp_t *p = mzp_init(A._entries.nrows)
|
|
2768
|
+
cdef mzp_t *q = mzp_init(A._entries.ncols)
|
|
2769
|
+
|
|
2770
|
+
if algorithm == 'standard':
|
|
2771
|
+
sig_on()
|
|
2772
|
+
mzd_ple(B._entries, p, q, param)
|
|
2773
|
+
sig_off()
|
|
2774
|
+
elif algorithm == "russian":
|
|
2775
|
+
sig_on()
|
|
2776
|
+
_mzd_ple_russian(B._entries, p, q, param)
|
|
2777
|
+
sig_off()
|
|
2778
|
+
elif algorithm == "naive":
|
|
2779
|
+
sig_on()
|
|
2780
|
+
_mzd_ple_naive(B._entries, p, q)
|
|
2781
|
+
sig_off()
|
|
2782
|
+
else:
|
|
2783
|
+
raise ValueError("Algorithm '%s' unknown." % algorithm)
|
|
2784
|
+
|
|
2785
|
+
P = [p.values[i] for i in range(A.nrows())]
|
|
2786
|
+
Q = [q.values[i] for i in range(A.ncols())]
|
|
2787
|
+
mzp_free(p)
|
|
2788
|
+
mzp_free(q)
|
|
2789
|
+
return B, P, Q
|