passagemath-linbox 10.6.43__cp313-cp313-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. passagemath_linbox/.dylibs/libfflas.1.dylib +0 -0
  2. passagemath_linbox/.dylibs/libffpack.1.dylib +0 -0
  3. passagemath_linbox/.dylibs/libflint.22.0.dylib +0 -0
  4. passagemath_linbox/.dylibs/libgd.3.dylib +0 -0
  5. passagemath_linbox/.dylibs/libgfortran.5.dylib +0 -0
  6. passagemath_linbox/.dylibs/libgivaro.9.dylib +0 -0
  7. passagemath_linbox/.dylibs/libgmp.10.dylib +0 -0
  8. passagemath_linbox/.dylibs/libgmpxx.4.dylib +0 -0
  9. passagemath_linbox/.dylibs/libiml.0.dylib +0 -0
  10. passagemath_linbox/.dylibs/liblinbox.0.dylib +0 -0
  11. passagemath_linbox/.dylibs/libm4ri.1.dylib +0 -0
  12. passagemath_linbox/.dylibs/libm4rie.1.dylib +0 -0
  13. passagemath_linbox/.dylibs/libmpfr.6.dylib +0 -0
  14. passagemath_linbox/.dylibs/libopenblasp-r0.3.29.dylib +0 -0
  15. passagemath_linbox/.dylibs/libpng16.16.dylib +0 -0
  16. passagemath_linbox/.dylibs/libquadmath.0.dylib +0 -0
  17. passagemath_linbox/.dylibs/libz.1.3.1.dylib +0 -0
  18. passagemath_linbox/__init__.py +3 -0
  19. passagemath_linbox-10.6.43.dist-info/METADATA +100 -0
  20. passagemath_linbox-10.6.43.dist-info/RECORD +75 -0
  21. passagemath_linbox-10.6.43.dist-info/WHEEL +6 -0
  22. passagemath_linbox-10.6.43.dist-info/top_level.txt +3 -0
  23. sage/all__sagemath_linbox.py +2 -0
  24. sage/geometry/all__sagemath_linbox.py +1 -0
  25. sage/geometry/integral_points.pxi +1426 -0
  26. sage/geometry/integral_points_integer_dense.cpython-313-darwin.so +0 -0
  27. sage/geometry/integral_points_integer_dense.pyx +7 -0
  28. sage/libs/all__sagemath_linbox.py +1 -0
  29. sage/libs/iml.pxd +10 -0
  30. sage/libs/linbox/__init__.py +1 -0
  31. sage/libs/linbox/conversion.pxd +185 -0
  32. sage/libs/linbox/fflas.pxd +189 -0
  33. sage/libs/linbox/givaro.pxd +109 -0
  34. sage/libs/linbox/linbox.pxd +219 -0
  35. sage/libs/linbox/linbox_flint_interface.cpython-313-darwin.so +0 -0
  36. sage/libs/linbox/linbox_flint_interface.pxd +18 -0
  37. sage/libs/linbox/linbox_flint_interface.pyx +192 -0
  38. sage/libs/m4ri.pxd +198 -0
  39. sage/libs/m4rie.pxd +204 -0
  40. sage/matrix/all__sagemath_linbox.py +1 -0
  41. sage/matrix/matrix_cyclo_linbox.cpython-313-darwin.so +0 -0
  42. sage/matrix/matrix_cyclo_linbox.pyx +361 -0
  43. sage/matrix/matrix_gf2e_dense.cpython-313-darwin.so +0 -0
  44. sage/matrix/matrix_gf2e_dense.pxd +15 -0
  45. sage/matrix/matrix_gf2e_dense.pyx +1573 -0
  46. sage/matrix/matrix_integer_iml.cpython-313-darwin.so +0 -0
  47. sage/matrix/matrix_integer_iml.pyx +316 -0
  48. sage/matrix/matrix_integer_linbox.cpython-313-darwin.so +0 -0
  49. sage/matrix/matrix_integer_linbox.pxd +5 -0
  50. sage/matrix/matrix_integer_linbox.pyx +358 -0
  51. sage/matrix/matrix_integer_sparse_linbox.cpython-313-darwin.so +0 -0
  52. sage/matrix/matrix_integer_sparse_linbox.pyx +465 -0
  53. sage/matrix/matrix_mod2_dense.cpython-313-darwin.so +0 -0
  54. sage/matrix/matrix_mod2_dense.pxd +14 -0
  55. sage/matrix/matrix_mod2_dense.pyx +2789 -0
  56. sage/matrix/matrix_modn_dense_double.cpython-313-darwin.so +0 -0
  57. sage/matrix/matrix_modn_dense_double.pyx +179 -0
  58. sage/matrix/matrix_modn_dense_float.cpython-313-darwin.so +0 -0
  59. sage/matrix/matrix_modn_dense_float.pyx +154 -0
  60. sage/matrix/matrix_modn_sparse.cpython-313-darwin.so +0 -0
  61. sage/matrix/matrix_modn_sparse.pyx +871 -0
  62. sage/matrix/matrix_rational_linbox.cpython-313-darwin.so +0 -0
  63. sage/matrix/matrix_rational_linbox.pyx +36 -0
  64. sage/matrix/misc.cpython-313-darwin.so +0 -0
  65. sage/matrix/misc.pyx +418 -0
  66. sage/modules/all__sagemath_linbox.py +1 -0
  67. sage/modules/numpy_util.cpython-313-darwin.so +0 -0
  68. sage/modules/numpy_util.pxd +10 -0
  69. sage/modules/numpy_util.pyx +136 -0
  70. sage/modules/vector_mod2_dense.cpython-313-darwin.so +0 -0
  71. sage/modules/vector_mod2_dense.pxd +11 -0
  72. sage/modules/vector_mod2_dense.pyx +547 -0
  73. sage/rings/all__sagemath_linbox.py +1 -0
  74. sage/rings/finite_rings/all__sagemath_linbox.py +1 -0
  75. 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