passagemath-m4ri-m4rie 10.8.1rc0__cp312-cp312-win_amd64.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-m4ri-m4rie might be problematic. Click here for more details.

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